Skip to content

Theming Support #3

@gtbuchanan

Description

@gtbuchanan

The CSS Modules repo suggests handling theming like this. I tried to use a similar approach with this library but ran into a typing issue.

Given an Elmish component like this in a component library:

module MyLib.MyComponent

open Fable.Core.JsInterop
open Feliz

let private classes: CssModules.MyComponent = import "default" "./MyComponent.module.scss"

type State = { Text: string; Theme: option }

let render (state: State) dispatch =
  let theme = Option.defaultValue classes state.Theme
  Html.div [
    prop.classes [ theme.myClass ]
    prop.children [ prop.text state.Text ]
  ]

Trying to pass a different generated CSS module from a consuming app (expectedly) results in a different interface type:

module MyApp.ConsumingComponent

open Fable.Core.JsInterop
open Feliz

let private myComponentTheme: CssModules.MyCustomComponent = import "default" "./MyCustomComponent.module.scss"

let render (state: State) dispatch =
  MyLib.MyComponent.render ({ Text: "MyString"; Theme = Some myComponentTheme }) dispatch // Error; Type mismatch

Since import is not strongly typed, I could change the type of myComponentTheme to MyLib.CssModules.MyComponent to work around the error. However, this does not force the CSS module to actually define the interface members at compile time.

Do you have any thoughts on how to approach this problem? Perhaps there should be a way to define an interface the CSS module needs to implement, then the generated file would fail to compile if the implementing type did not contain the appropriate members. For example:

[<RequireQualifiedAccess>]
module CssModules

open Fable.Core

[<AbstractClass>]
type MyCustomComponent =
  /// <summary>
  /// Binding for <c>someOtherClass</c> class
  /// </summary>
  [<Emit("$0[\"someOtherClass\"]")>]
  abstract someOtherClass: string;

  interface MyLib.CssModules.MyComponent with
    member x.myClass = x.myClass // Error; MyCustomComponent missing myClass member

As for how to indicate the interface that needs to be implemented... I'm not sure. I suppose there would have to be some sort of directive in the CSS file. fable-css-modules would also have to have context about the referenced libraries to discover the interface members, which I'm not sure is a simple task. Perhaps I'm overthinking this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions