Hi 👋 wanted to make a home for discussing some strategies for encapsulating styles in halogen with:
purescript-halogen
purescript-halogen-css
purescript-css
If this issue should live in halogen-css instead I can move this discussion there, but writing this here since:
- this has potential vdom & halogen core implications
- this repo is the most popular and would open this discussion to more voices
The story right now
- global
<style>sheet (stylesheet :: forall i p. CSS -> HTML p i)
- per-element
style props (style :: forall r i. CSS -> IProp (style :: String | r) i)
Global stylesheet
Example
html
<style>
.my-element {
border-radius: 1rem;
color: white;
font-size: 12pt;
}
</style>
purescript-css
stylesheet do
select (Select.star `Select.with` Select.className "my-element") do
borderRadius $ rem 1.0
color white
fontSize $ pt 12.0
PROS:
CONS:
- allows / encourages large complex style expressions
- global styles reduce discoverability
- global styles add significant complexity because of the centralization
- classname / id targeting adds a potential bug surface due to string-typedness
style props
Example
html
<div style="border-radius: 1rem; color: white; font-size: 12pt;"> </div>
purescript
HH.div
[ style do
borderRadius $ rem 1.0
color white
fontSize $ pt 12.0
]
[]
PROS:
- styling is loosely coupled, atomic, and targeted
- encourages reusable purescript style fragments
CONS:
- clutters rendered dom nodes, and the rendered dom does not share styles in any way
- does not allow applying rules to pseudoselectors (
:hover, :first-child, :active)
- NOTE: the type system does nothing to prevent this gotcha, in a perfect world there would be some supertype of CSS like "CSSWithSelectors" but that would be very complex and a bandaid on top of limitations of the
style prop itself
- ruins style inheritance (style prop is equivalent to
important! on every rule)
Potential alternatives
Shadow DOM
The API could potentially be the same as the style prop, but under the hood effectfully wrap the element in question with a shadow DOM and inject a stylesheet next to the element.
Example
HH.shadow
[]
[
stylesheet do
select (Select.star `Select.with` Select.className "foo") do
borderRadius $ rem 1.0
color white
fontSize $ pt 12.0
, HH.div [className $ ClassName "foo"] []
]
where HH.shadow would be an HTML pseudo-node that would desugar to:
const shadowParent = /* get parent's dom ref from halogen */;
const shadowChildren = /* get rendered child nodes */;
const shadow = shadowParent.attachShadow({mode: 'open'});
shadowChildren.forEach(child => shadow.appendChild(child));
PROS:
- all of the style prop pros apply
- styling is encapsulated (no accidental application)
CONS:
- The rendered DOM does not share styles in any way
- A little unintuitive (could potentially be abstracted a bit more)
- classname / id targeting adds a potential bug surface due to string-typedness
- implementation complexity? (Requires a prop that wraps elements in a shadow DOM which may imply vdom changes, definitely implies halogen-core changes)
Classname sugar on top of purescript-halogen-css (a la styled-components)
Example
html
<style>
.fjdk2144 {
color: white;
}
.fjdk2144:hover {
color: black;
}
</style>
<div className="fjdk2144"> </div>
purescript
HH.div
[ Halide.style do
color white
Halide.select (Select.pseudo ":hover") do
color black
]
[]
PROS:
- all of the style prop pros apply
- styling is encapsulated (no styles bleeding to other elements unintentionally)
- respects style inheritance
- intuitive
- drop-in replacement for inline style props
CONS:
- The rendered DOM does not share styles (purs css expression re-use does not mean css fragment re-use)
- unless this lived in purescript-halogen-css this would be a bit harder to discover for new users (would have to ask "how do I encapsulate styles?" or chance across the library on pursuit, unless halogen or halogen-css pointed them to this library)
What I'm asking for
@thomashoneyman @garyb
is there any prior discussion around this I'm not aware of? LMK if so
If not, let me know what you think, as maintainers of halogen and halogen-css
I'm personally leaning towards the last option as the most robust and intuitive, and wondering what a temp check on folding something like this into the halogen org would be? ex. part of the halogen-css API or a new lib
Hi 👋 wanted to make a home for discussing some strategies for encapsulating styles in halogen with:
purescript-halogenpurescript-halogen-csspurescript-cssIf this issue should live in halogen-css instead I can move this discussion there, but writing this here since:
The story right now
<style>sheet (stylesheet :: forall i p. CSS -> HTML p i)styleprops (style :: forall r i. CSS -> IProp (style :: String | r) i)Global stylesheet
Example
html
purescript-css
PROS:
CONS:
style props
Example
html
purescript
PROS:
CONS:
:hover,:first-child,:active)styleprop itselfimportant!on every rule)Potential alternatives
Shadow DOM
The API could potentially be the same as the
styleprop, but under the hood effectfully wrap the element in question with a shadow DOM and inject a stylesheet next to the element.Example
where
HH.shadowwould be an HTML pseudo-node that would desugar to:PROS:
CONS:
Classname sugar on top of purescript-halogen-css (a la styled-components)
Example
html
purescript
PROS:
CONS:
What I'm asking for
@thomashoneyman @garyb
is there any prior discussion around this I'm not aware of? LMK if so
If not, let me know what you think, as maintainers of halogen and halogen-css
I'm personally leaning towards the last option as the most robust and intuitive, and wondering what a temp check on folding something like this into the halogen org would be? ex. part of the halogen-css API or a new lib