marc walter

CSS styles in elm-reactor and pretty check boxes

2017-08-16 (Last updated on 2018-09-01)

Elm-reactor is a great tool to get started in elm.
It supports a time-traveling debugger, downloads the necessary packages and is generally nice to work with.

A problem though is that for styling you usually need to write inline-styles. This is sometimes fine, but if I want to concentrate on the elm code and add separate styles I usually pick another tool like create-elm-app.

But it is possible to add a style tag to the main view function and the css styles are applied to the document.
After the initial exploration, I can move the styles to a dedicated file.

If this iframe is not displayed, open [the file directly](./choose.min.html).

This uses checkboxes I adapted from a really nice example by @valerypatorius on codepen.

The trick

Render a style node and specify the content as a multiline string.

module Main exposing (main)

import Html exposing (Html, div, h1, text)
import Html.Attributes exposing (class)

main =
    div []
        [ Html.node "style" [] [ text css ]
        , h1 [] [ text "Example" ]
        ]

css =
    """/* injected css */
html {
    background-color: #f0c;
    color: #fff!important;
}
"""

code

The code was updated for Elm 0.19, the code for Elm 0.18 is still available here.

module Main exposing (main)

{-| Inspired by an animation I saw once and a talk by Evan Czaplicki
<https://www.youtube.com/watch?v=2ihTgEYiKpI&list=PLdgiYNRAzk2wtuTK3zaEvE1upwZY9uxBC&index=5>

Styling of the checkbox is adapted from <https://codepen.io/valerypatorius/pen/oXGMGL>

-}

import Browser
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)


type Model
    = None
    | One String
    | Two String String


main : Program () Model Msg
main =
    Browser.sandbox
        { init = One "Quality"
        , view = view
        , update = update
        }


view : Model -> Html Msg
view model =
    div [ class "container" ]
        [ addCssStyles
        , h1 [] [ text "Choose wisely" ]
        , ul []
            [ viewCheckbox "Quality" model
            , viewCheckbox "Fast" model
            , viewCheckbox "Cheap" model
            ]
        ]


viewCheckbox : String -> Model -> Html Msg
viewCheckbox which model =
    let
        id_ =
            "switch-" ++ which
    in
    li []
        [ input
            [ type_ "checkbox"
            , id id_
            , checked <| isChecked which model
            , onClick <| Toggle which
            ]
            []
        , label [ for id_ ] [ text which ]
        ]


isChecked : String -> Model -> Bool
isChecked which model =
    case model of
        None ->
            False

        One a ->
            a == which

        Two a b ->
            which == a || which == b


type Msg
    = Toggle String


update : Msg -> Model -> Model
update msg model =
    case msg of
        Toggle str ->
            case model of
                None ->
                    One str

                One a ->
                    if a /= str then
                        Two a str

                    else
                        None

                Two a b ->
                    if str == a then
                        One b

                    else if str == b then
                        One a

                    else
                        Two b str


addCssStyles : Html msg
addCssStyles =
    Html.node "style" [] [ text css ]


css : String
css =
    """/* injected css */
    html {
        height: 100%;
        margin: 0;
    }

    body {
        height: 100%;
        margin: 0;
        display: flex;
        align-items: center;
    }

    .container {
        font-family: sans-serif;
        font-size: 8vmin;
        height: 8em;
        margin: auto;
    }

    h1 { font-size: 120%;
        margin-bottom: .5em;
    }

    ul {
        list-style-type: none;
        padding-left: 1em;
        margin-top: 0;
    }

    li {
        margin: .5em 0;
    }

    input[type="checkbox"] { display: none; }

    input[type="checkbox"] + label {
        display: block;
        position: relative;
        padding-left: 1.6em;
        cursor: pointer;

        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
    }

    input[type="checkbox"] + label:before {
        content: '';
        display: block;
        width: 1em;
        height: 1em;
        border: .1em solid #000;
        position: absolute;
        left: 0;
        top: 0;
        opacity: .6;
        -webkit-transition: all .12s, border-color .08s;
        transition: all .12s, border-color .08s;
    }

    input[type="checkbox"]:checked + label:before {
        width: .5em;
        top: -.3em;
        left: .4em;
        border-radius: 0;
        border-width: .15em;
        opacity: 1;
        border-color: #199045;
        border-top-color: transparent;
        border-left-color: transparent;
        -webkit-transform: rotate(35deg);
        transform: rotate(35deg);
        border-radius: .1em;
    }
    """