miso-css is an evolutionary step ahead 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 open a door for introducing 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.
miso-css runs a css parser to extract CSS selectors and generates Haskell constants for every CSS class with correspondent name that is found in the input. A type of such constant describes all possible ways the class can used in DOM.
Besides that the library ships E type representing an HTML element and a set of operators for constructing tags and combining them in DOM tree. E is miso VNode type protected with a few type parameters.
Before jumping straight to style application lets get familiar with syntax for tag composition because it is different in vanilla miso.
div_ </ p_<div>
<p></p>
</div>ul_ </ li_ </ li_<ul>
<li></li>
<li></li>
</ul>body_ </ (section_ </ p_)<body>
<section>
<p></p>
</section>
</body>a_ <@ "click"<a>click</a>import Miso.Html qualified as MH
import Miso.Html.Property qualified as MH
go = div_ =< MH.p_ [] [ "h" ]<div>
<p>h</p>
</div>a_ =<| atr @"href" "http://link.com"<a href="http://link.com"></a>button_ =! onClick YourActionDc{-# LANGUAGE QuasiQuotes #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
[css|.red { color: red; }|]
div_ =. red<div class="red"></div>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>{-# 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>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.
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: '[]
[css|.a .b {}|]
div_ =. bThe 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"], [], [])
]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"], [], [])]
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"], [])]The third element of triple explains sibling errors.
[css|.a + .b {}|]
div_ </ div_ =. bClass a is not applied:
[([B], [], [[ [B], [C "a"]]])]By default non-leaf classes in selector don't contribute to constraints. e.g.
.a > .b .cb doesn't require a as immediate parent it is handled by constraints form c.
It is possible to generate constraints for b to make checking even
stricter, though in such mode following DOM can't pass type check:
enableRulesForNonLeafClasses
[css|.a > .b .c {}|]
test_t = testGroup cssAsLiteralText
[ doNotTcNoBr [] [[[(JustNow, [B], [C "a"], [])]]] $
div_ =. a </ (div_ =. b </ (div_ =. b </ div_ =. c))
]data E
model
action
(en :: Symbol)
(es :: ElementStructure)
(re :: Maybe Root)
(ei :: Maybe Symbol)
(atrs :: [Symbol])
(knownIds :: KnownIDS)
(cls :: [Symbol])
(l :: [[[Seg]]])
(children :: [[SubSeg]])First two parameters model and action are forwarded to miso VNode type.
In ghci session:
:t div_
div_
:: E model
action
"div"
...
Most often its value is Composite which means that the element could have children. Es parameter of CDATA element is Atomic.
It is a root tag indicator. A root tag cannot be adopted.
[css|:root > .a {}|]div_ =# ElementId "Duncan":t a_ =# ElementId "x" =<| atr @"href" "/click.php?x=1"
...
["href", "id"]
...
:t div_ =# ElementId "x" </ div_ =# ElementId "y" </ div_ =# ElementId "z"
...
(KnownIds '[] ["x", "y", "z"])
...
Classes applied to children and descendants are not included.
:t div_ =. a =. b </ div_ =. c
...
["b", "a"]
...
The parameter describes requirements to be satisfied in ancestors of the tag.
[css|.a .b {}|]:t div_ =. b
...
'[ '[['(AutoClean, '[], '[], '[]),
'(NowOrLater, '[C "a"], '[], '[])]]]
...
List of lists of children subselectors in reverse order.
[css|.a {} .b {}|]:t div_ </ ul_ =. a =. b </ ol_ =# ElementId "x"
...
[[I "x", T "ol"], [T "ul", C "b", C "a"]]
...
{-# 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"))HLS should be available inside the default dev shell.
$ nix develop
$ emacs src/*/*/Qq.hs &
$ cabal build
$ cabal test --test-option=--hide-successesmiso-css was developed with miso v1.9