miso

Core concepts

Attributes & properties

The Attribute type carries everything that can be attached to a DOM element:

data Attribute model action
  = Property MisoString Value                          -- DOM property (key/value)
  | ClassList [MisoString]                             -- CSS class list
  | On (model -> Sink action -> ...)                   -- fully-applied event handler
  | OnStatic (StaticPtr (EventHandler model action))   -- static handler, rebuilt on the main thread (dual-thread)
  | Styles (Map MisoString MisoString)                 -- inline style map

In practice you never construct these directly. Use the smart constructors from Miso.Html.Property, Miso.Html.Event, Miso.Property and Miso.CSS:

H.div_
  [ HP.id_ "container"                    -- textProp "id"
  , HP.className "card"                   -- ClassList
  , HP.classList_ [ ("active", isActive) ] -- ClassList, conditional
  , HP.disabled_                          -- boolProp "disabled" True
  , HE.onClick MyAction                   -- On event handler
  , CSS.style_ [ CSS.display "flex" ]     -- Styles map
  ]
  []

Custom properties

Use prop (or the typed variants textProp, boolProp, intProp, doubleProp, objectProp) from Miso.Property to set arbitrary DOM properties:

prop "data-index" (42 :: Int)      -- sets element.data-index = 42
textProp "placeholder" "Search…"   -- sets element.placeholder
boolProp "checked" True            -- sets element.checked = true

Note that DOM properties and HTML attributes are distinct. miso tries to set properties on the DOM node object (e.g. node.checked) first, then falls back to setting the HTML attribute (setAttribute("checked", …)). This matches what the browser exposes in JavaScript and avoids common pitfalls with boolean attributes.

Keys

key_ (and its alias keyProp) attaches a reconciliation key to any element. See Keys for details.

data Item = Item { itemId, itemLabel :: MisoString }

H.li_ [ key_ (itemId item) ] [ text (itemLabel item) ]

Try it

Conditional classes, structured styles and propertieslive

65% → .bar-ok

data BarModel = BarModel { _pct :: Int }
  deriving (Show, Eq)

data BarAction = SetPct MisoString

battery :: Component ctx () BarModel BarAction
battery = component (BarModel 65) update view
  where
    update (SetPct v) = modify (\m -> m { _pct = fromMisoString v })

    view _ () (BarModel p) =
      H.div_ []
        [ H.div_ [ HP.class_ "bar-track", HP.title_ (ms p <> "%") ]   -- textProp "title"
            [ H.div_
                [ HP.classList_                                       -- conditional classes
                    [ ("bar", True)
                    , ("bar-low",  p < 30)
                    , ("bar-ok",   p >= 30 && p < 80)
                    , ("bar-full", p >= 80)
                    ]
                , CSS.style_                                          -- structured inline style
                    [ CSS.width (CSS.pct (fromIntegral p))
                    , CSS.transition_ "width" (CSS.ms 250) "ease-out"
                    ]
                ] []
            ]
        , H.input_
            [ HP.type_ "range", HP.min_ "0", HP.max_ "100"
            , HP.value_ (ms p), HE.onInput SetPct
            ]
        , H.p_ [ HP.class_ "muted" ]
            [ H.code_ [] [ text (ms p), "% → ." , text klass ] ]
        ]
      where
        klass | p < 30 = "bar-low"
              | p < 80 = "bar-ok"
              | otherwise = "bar-full"