Hi,
miso-css is an evolutionary step from css-class-bindings.
CSS class of an atomic selector can be applied to any DOM element, but that is not true for classes used in composite selectors. Rules with partially matched selectors are silently ignored by browser and this opens door bugs during consequent changes. css-class-binding just cannot cope with such problem and miso-css uses dependent types to track what CSS classes can applied to HTML elements.
Composing tags
Before jumping straight to style application lets get familiar with syntax for tag composition because it is different in vanilla miso.
Appending a child
div_ </ p_
<div>
<p></p>
</div>
Adding a sibling
ul_ </ li_ </ li_
<ul>
<li></li>
<li></li>
</ul>
Appending a child to child
body_ </ (section_ </ p_)
<body>
<section>
<p></p>
</section>
</body>
Adding CDATA
a_ <@ "click"
<a>click</a>
Adding a raw miso DOM chunk
import Miso.Html qualified as MH
import Miso.Html.Property qualified as MH
go = div_ =< MH.p_ [] [ "h" ]
<div>
<p>h</p>
</div>
Adding tag attribute
a_ =<| atr @"href" "http://link.com"
<a href="http://link.com"></a>
Binding event handler
button_ =! onClick YourActionDc
Applying CSS class
{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
[css|.red { color: red; }|]
div_ =. red
<div class="red"></div>
Adding tag ID
Handmade tag id:
div_ =# ElementId "footer"
Generated tag id:
{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
[css|#footer { color: red; }|]
div_ =# Footer
<div id="footer"></div>
Mix all at once
{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
[css|.form .red { color: red; }|]
div_ =. form =# ElementId "footer"
</ (a_ =. red =<| atr @"href" "/click.php?x=1"
</ (span_ <@ "Click me"))
<div class="form" id="footer>
<a class="red" href="/click.php?x=1">
<span>Click me</span>
</a>
</div>
Breaking rules
Until now all above samples must be valid and should type
check. This section enumerates HTML snippets with ill-applied
classes, expected errors and comments.
There can be only one
An element ID can be used once in a HTML document.
div_ =# ElementId "Duncan MacLeod"
</ div_ =# ElementId "Duncan MacLeod"
Couldn't match type: '[DuplicatedId "Duncan MacLeod"]
with: '[]
Parent class is missing
[css|.a .b {}|]
div_ =. b
The error message is a list of triples where first element is a list of not
applied classes, ids (hashes), tag names or attribute names.
[([C "a"], [], [])]
Class a and b are missing:
[css|.a .b .c {}|]
div_ =. c
[ ([C "b"], [], [])
, ([C "a"], [], [])
]
B element
When selector with a child relation is partially applied the triple
contains B element. It is a synthetic element preventing the failed
rule from matching later somewhere upper in DOM by an accident.
[css|.a > .b {}|]
div_ </ div_ =. b
[([B, C "a"], [], [])]
One of classes is missing
Second element of triple is a list of applied classes. It helps to
understand what worked out and what didn’t in a composite selector.
[css|.a.b > .c {}|]
div_ =. a </ div_ =. c
[([B, C "b"], [C "a"], [])]
Sibling is missing
The third element of triple explains sibling errors.
[css|.a + .b {}|]
div_ </ div_ =. b
Class a is not applied:
[([B], [], [[ [B], [C "a"]]])]
Hello World
{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Miso.Css.Test.HelloWorld where
import Miso ( component, App, CSS(Style), Component(styles), View )
import Miso.Css
import Prelude
type Model = ()
type Action = ()
-- default name is "cssAsLiteralText"
renameCssTextConst "cssFromQq"
[css|
.c .b .a {
color: #fc2c2c;
}
|]
-- instead of quasi-quoted CSS
-- the whole CSS file can be included with:
-- includeCss "assets/style.css"
app :: App Model Action
app = (component () pure viewModel)
{ styles = [ Style cssFromQq ] }
{-
viewModel produce following HTML snippet:
<div class="c">
<div class="b">
<button class="a">
Submit
</button>
</div>
</div>
html_ and body_ don't produce tags,
because miso mount cannot be higher than body tag.
they serve just for type checking purpose
(e.g. html_ satisfies :root pseudo class)
-}
viewModel :: Model -> View Model Action
viewModel () = toView . html_ . body_ $
div_ =. c
</ (div_ =. b
</ (button_ =. a
<@ "Submit"))