uncover-co / elm-theme-provider / ThemeProvider

Creating a Theme

Before using a Theme you need to create one! Thankfully it is really easy to do so:

fromList : List ( String, String ) -> Theme

Creates a theme based on a list of CSS variables names and their values.

lightTheme : Theme
lightTheme =
    Theme.fromList
        [ ( "background", "white"
        , ( "base", "black" )
        , ( "accent", "blue" )
        ]


type Theme

Propagating Themes

After creating one or more themes, you can use them in a few different ways.

globalProvider : Theme -> Html msg

Used to provide a Theme globally. It will be applied to your body element and it will be available for use anywhere in your application.

main : Html msg
main =
    div []
        [ ThemeProvider.globalProvider lightTheme
        , p [ style "color" "var(--my-theme-accent)" ]
            [ text "I have the `accent` color" ]
        ]

Note: You are still able to overwrite this Theme locally.

provider : Theme -> List (Html.Attribute msg) -> List (Html msg) -> Html msg

Used to propagate themes to an specific scope.

main : Html msg
main =
    div []
        [ ThemeProvider.globalProvider defaultTheme

        -- section using the default theme
        , section [] [ .. ]

        -- section using the orange theme
        , ThemeProvider.provider orangeTheme [] [ .. ]
        ]

Themes and Dark Mode

You can also use two similar functions as the ones above for specifying themes that automatically switch to a dark mode based on a defined strategy. The first thing to do then is decide which dark mode strategy will be the right one for your use case:


type DarkModeStrategy
    = SystemStrategy
    | ClassStrategy String

Defines the dark mode strategy.

globalProviderWithDarkMode : { light : Theme, dark : Theme, strategy : DarkModeStrategy } -> Html msg

Used to provide a Theme globally with a dark mode alternative. Themes will automatically switch based on the strategy condition.

main : Html msg
main =
    div []
        [ ThemeProvider.globalProviderWithDarkMode
            { light = lightTheme
            , dark = darkTheme
            , strategy = ThemeProvider.SystemStrategy
            }
        , p [ style "color" "var(--my-theme-accent)" ]
            [ text "I have the `accent` color" ]
        ]

Note: You are still able to overwrite this Theme locally.

providerWithDarkMode : { light : Theme, dark : Theme, strategy : DarkModeStrategy } -> List (Html.Attribute msg) -> List (Html msg) -> Html msg

Used to propagate themes to an specific scope with a dark mode alternative. Themes will automatically switch based on the strategy condition.

main : Html msg
main =
    div []
        [ ThemeProvider.globalProviderWithDarkMode
            { light = lightTheme
            , dark = darkTheme
            , strategy = ThemeProvider.SystemStrategy
            }

        -- section using the default light or dark theme
        , section [] [ .. ]

        -- section using the orange light and dark themes
        , ThemeProvider.providerWithDarkMode
            { light = lightOrange
            , dark = darkOrange
            , strategy = ThemeProvider.SystemStrategy
            }
            [] [ .. ]
        ]

Optimized Themes

If you're heavily using theme providers across your application, you're probably reusing the same themes over and over.

For those scenarios, otimized themes can be used to decouple the styles from the provider element so styles are only inserted once, even if the provider is being used at various locations.

optimizedTheme : Theme -> OptimizedTheme msg

optimizedTheme =
    optimizedTheme theme

body []
    [ myTheme.styles
    , myTheme.provider []
        [ ... ]
    ]

optimizedThemeWithDarkMode : { light : Theme, dark : Theme, strategy : DarkModeStrategy } -> OptimizedTheme msg


type alias OptimizedTheme msg =
{ styles : Html msg
, provider : List (Html.Attribute msg) -> List (Html msg) -> Html msg 
}