Sitelet https://github.com/purescript-halogen/purescript-halogen/issues/805
Skip to content

Discussion: CSS strategy for halogen applications #805

Description

@cakekindel

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:

  • styling is centralized

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions