Coder Social home page Coder Social logo

Comments (20)

thomashoneyman avatar thomashoneyman commented on June 28, 2024 2

To give some more guidance -- we're unlikely to touch Halogen core to support CSS changes if they can reasonably be done without that. If the shadow DOM approach requires this then I think you will find we are more enthusiastic about another approach :)

from purescript-halogen.

thomashoneyman avatar thomashoneyman commented on June 28, 2024 1

I'm very much interested in a styled-components approach for Halogen. @nsaunders may also be interested in this topic, as he's recently released a new CSS library that's a considerable improvement over the contrib one.

There have been some prior looks at this, such as:
https://github.com/paulyoung/purescript-styled-system

but there is no active work that I know of on a true styled-components system for Halogen. Almost everyone I know either writes external stylesheets (standard CSS) or uses something like Tailwind along with a generator script to stamp out all the ClassNames for the classes they use.

from purescript-halogen.

garyb avatar garyb commented on June 28, 2024 1

I've not fully caught up with this discussion yet (I am interested in it though!) but there's a thread on the discourse that talks about some things people have done here also: https://discourse.purescript.org/t/css-strategies-for-purescript-apps/374 it probably doesn't add much to this, but just thought I'd mention anyway.

One thing I'm curious about, is what the perceived benefit of writing CSS in PureScript is? Is it just so "everything is in one place" / encapsulated with relevant components? The times I've needed to do CSS-in-PS for something it does feel like more of an obstacle than a benefit, since despite it being quite complicated to write, the type safety isn't doing much for us given there's no semantic benefits the way there is with normal code.

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024 1

The times I've needed to do CSS-in-PS for something it does feel like more of an obstacle than a benefit, since despite it being quite complicated to write, the type safety isn't doing much for us

@garyb as you know CSS is rather complicated; and getting it wrong has the potential to create show-stopping defects. Personally, that's why I am looking for some type safety in this domain, though admittedly (and to your point) it isn't easy to find, certainly at a reasonable cost. For example:

  • csstype, which is a tremendously popular TypeScript library, guards against e.g. setting the width property to true; but it doesn't stop me from e.g. setting the top property to "hello".
  • purescript-css, with which you are familiar, has a wide range of issues; for example, some which make it annoying/tedious to use, and some which even produce incorrect output.

Is it just so "everything is in one place" / encapsulated with relevant components?

Regardless of type safety, I think a lot of people (myself included) do find value in being able to package CSS together with the markup it affects. To your point, piggybacking off of PureScript's module system is an easy way to achieve that; and the benefit of "local reasoning" is too valuable to dismiss.

One thing I'm curious about, is what the perceived benefit of writing CSS in PureScript is?

Although this is a great question to ask, I think this issue might be more about when the CSS is rendered, how it is scoped, how it is added to the document, etc. So I have a slightly different question:

Why is it necessary to generate CSS at runtime?

Update 11/29/22: I've created a number of examples of easy mistakes that "CSS-in-PS" can prevent.

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024 1

Why is it necessary to generate CSS at runtime?
Great callout, I can definitely imagine a build- or bundle-time tool that transpiles a subset of purescript to CSS

The benefits in my mind to rendering the CSS on the fly would be purescript animations and dynamic classnames / targeting. good food for thought

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

Working (slightly hacky) example of shadow dom:

Halogen.HTML.Shadow.purs

module Halogen.HTML.Shadow (shadow) where

import Prelude

import Data.Maybe (Maybe(..))
import Halogen.HTML.Core (ref)
import Halogen.HTML.Properties (IProp(..))
import Web.DOM (Element)

foreign import unsafeWrapInShadow :: Element -> Unit

shadow :: forall r i. IProp r i
shadow =
  let
    refHandler el = do
      el' <- el
      let _ = unsafeWrapInShadow el'
      Nothing
  in
    IProp $ ref refHandler

Halogen.HTML.Shadow.js

export const unsafeWrapInShadow = el => {
  const shadow = el.attachShadow({mode: 'open'});

  const moveChildren = () => {
    Array.from(el.childNodes).forEach(c => shadow.appendChild(c));
  };

  (new MutationObserver(moveChildren)).observe(el, {childList: true});
  moveChildren();
};

App.Button.purs

module App.Button where

import Halogen.HTML as Html
import Halogen.HTML.CSS (style, stylesheet)
import Halogen.HTML.Shadow (shadow)
import App.Button.Style as Style
import CSS (CSS)

render
  :: { containerStyle :: CSS
     , buttonStyle :: CSS
     , children :: Array Html.PlainHTML
     }
  -> Html.PlainHTML
renderPlain { containerStyle, buttonStyle, children } =
  Html.div
    [ shadow, style containerStyle ]
    [ stylesheet Style.button
    , Html.button
        [ Html.classNames [ Style.className ]
        , style buttonStyle
        ]
        children
    ]

App.Button.Style.purs

module App.Button.Style where

import Prelude
import CSS

import CSS.Cursor (pointer)
import CSS.Selector as Select
import CSS.Time as Time

className :: String
className = "button"

activeClassName :: String
activeClassName = "button-active"

button :: CSS
button = do
  select (Select.element "button" `Select.with` Select.byClass className)
    do
      height $ pct 100.0
      width $ pct 100.0
      border solid (px 0.0) black
      sym padding $ px 0.0
      cursor pointer
      color white
      backgroundColor black
      transition "background-color" (Time.ms 75.0) kwapEasing (Time.ms 0.0)

  select
    ( Select.element "button"
        `Select.with` Select.byClass className
        `Select.with` pseudo "hover"
    )
    backgroundColor grey

  select
    ( Select.element "button"
        `Select.with` Select.byClass className
        `Select.with` pseudo "active"
    )
    backgroundColor black

  select
    ( Select.element "button"
        `Select.with` Select.byClass activeClassName
    )
    backgroundColor black

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

I'm not very familiar with halogen core or vdom but after digging a bit a "synthetic DOM node that, when rendered, performs some effects" feels like it could fit into today's vdom api as a widget? unless i'm misunderstanding their role. At worst shadow dom could be an effectful prop like the example above signifying "this element's children should be wrapped in a shadow."

One consideration I didn't think of in the writeup is that the shadow dom approach is much less stateful, much less effectful than randomly assigning and generating classnames; "synthetic sandboxing DOM node" vs "stateful and effectful central style manager"

The styled-component approach would imply some complexity:

  • styles need to be dispatched to some central style manager
  • style manager needs to randomly (or maybe deterministically?) generate classnames
  • style manager needs to apply styles to a stylesheet high in the dom tree

Which I'd be happy to champion, but I'd need a bit of guidance on the best way to do this with the least amount of raw JS and without littering user code with new slots / type bounds

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

Hi! There are a couple big benefits from my using purescript-css on my current project:

  • it's very easy to write style expressions for reuse, replacing glob selectors or micro css classes with purescript expressions
  • styles live very close to the DOM nodes they style
  • very easy to parameterize styles
  • potential to eliminate classnames entirely, removing a class of style bugs

Naturally the costs are non-zero:

  • giving up easy access to raw CSS
  • strong-type benefits are few (mainly ensuring that arguments are in the right order, vendoring, preventing typos)
  • having to contend with an API that, while good, has much less horsepower behind it than the CSS spec meaning it covers way fewer usecases

from purescript-halogen.

toastal avatar toastal commented on June 28, 2024

style props

Con: would be a nightmare to use without the style-src 'unsafe-inline' CSP value. I'd recommend against any inline styles for this reason as our apps should be secure and simple (nonces aren't simple).

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

style props

Con: would be a nightmare to use without the style-src 'unsafe-inline' CSP value. I'd recommend against any inline styles for this reason as our apps should be secure and simple (nonces aren't simple).

Not necessarily, I've used purescript-css in a few projects without unsafe-inline. Valuable input though, it's true there are definitely even more pits to fall in than I mentioned

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024

The benefits in my mind to rendering the CSS on the fly would be purescript animations and dynamic classnames / targeting. good food for thought

@cakekindel

  1. Can you elaborate a bit regarding "purescript animations"?
  2. Does "dynamic classnames / targeting" boil down to scoping?

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

Can you elaborate a bit regarding "purescript animations"?

By that I meant complex animations or those that are non-animateable CSS properties

e.g. if I wanted a swirling pure CSS gradient I have to reach for PS/JS

Does "dynamic classnames / targeting" boil down to scoping?

I think so? The more i think about this the less a usecase it seems to be.

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024

@cakekindel you might be interested in this article. I wouldn't encourage you to resort to CSS Modules, but I would bet that it's possible to extract at build time CSS that is written in PureScript and colocated with the Halogen component it affects. As an example, I've had some luck using webpack-virtual-modules for this purpose (though not specifically with PureScript).

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

I love the idea of deferring styles to build time, my experience definitely agrees with the article you shared.

The problem left to solve for me that css-in-purescript solves is synchronizing "stringly" selectors with the DOM, which is a much different (maybe smaller?) problem to solve than getting a CSS-in-purs DSL right.

Definitely dreaming here, but it would be neat to have build-time help like:

warning: classname not found in stylesheets
  --> src/Components/Foobar.purs:82:6
82 |     HH.div [ className $ ClassName "foobat" ] ...
   |                          ^^^^^^^^^^^^^^^^^^

found similar classnames:
  1) static/styles/common.css:32:1
      32 | .foobar {
      33 |   height: 100px;
 
  2) static/styles/common.css:48:1
      48 | .golbat {
      49 |   display: flex;

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

Separately from this, it would be really nice (speaking as a halogen user) if there was a sort of "paved road" or suggested styling strategy, even if it's just "use parcel and sass lol," I feel like having a sane starting point would help make adopting halogen even smoother.

Definitely interested in hearing maintainers' thoughts on this, though

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024

I love the idea of deferring styles to build time, my experience definitely agrees with the article you shared.

The problem left to solve for me that css-in-purescript solves is synchronizing "stringly" selectors with the DOM, which is a much different (maybe smaller?) problem to solve than getting a CSS-in-purs DSL right.

Definitely dreaming here, but it would be neat to have build-time help like:

warning: classname not found in stylesheets
  --> src/Components/Foobar.purs:82:6
82 |     HH.div [ className $ ClassName "foobat" ] ...
   |                          ^^^^^^^^^^^^^^^^^^

found similar classnames:
  1) static/styles/common.css:32:1
      32 | .foobar {
      33 |   height: 100px;
 
  2) static/styles/common.css:48:1
      48 | .golbat {
      49 |   display: flex;

@cakekindel this pattern isn't exactly what you're looking for, but I wonder if it might be close enough?

-- Class names
card = ClassName "card" :: ClassName -- annotation required to avoid a compiler warning
cardTitle = ClassName "card__title" :: ClassName
cardImage = ClassName "card__image" :: ClassName

unClassName :: ClassName -> String
unClassName (ClassName x) = x

-- Style sheet - use this value at build time to render the style sheet
css :: CSS
css = do
  universal &. unClassName card ? Rule.do
    borderWidth := px 1
    borderColor := rgb 225 225 225
  universal &. unClassName cardTitle ? Rule.do
    fontSize := px 24
  universal &. unClassName cardImage ? Rule.do
    float := left

-- Widget (or component)
cardWidget :: forall p i. Title -> Image -> HH.HTML p i
cardWidget title image =
  HH.div
    [ HP.class_ card ]
    [ {- ... -} ]

The unClassName utility is a little unfortunate, of course, but I guess that's the price we pay for not having a canonical data type to represent a class name...

Update 12/2/22: I just released a version of tecton-halogen which eliminates the need for the unClassName utility.

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024

Separately from this, it would be really nice (speaking as a halogen user) if there was a sort of "paved road" or suggested styling strategy, even if it's just "use parcel and sass lol," I feel like having a sane starting point would help make adopting halogen even smoother.

Definitely interested in hearing maintainers' thoughts on this, though

thomashoneyman/purescript-halogen-realworld#46 may be of interest. I know it's not exactly what you're looking for, but maybe that will come in the future...

On the other hand, if you do find a solution that works well for you, you could always put your own example/starter app out there as a suggestion for others. :-)

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024

Separately from this, it would be really nice (speaking as a halogen user) if there was a sort of "paved road" or suggested styling strategy, even if it's just "use parcel and sass lol," I feel like having a sane starting point would help make adopting halogen even smoother.

Definitely interested in hearing maintainers' thoughts on this, though

@cakekindel Although I'm not a Halogen maintainer, you might be interested in a starter project I've put together, which demonstrates the architecture I described above. (You could use the same approach with purescript-css instead of Tecton if you'd prefer.)

from purescript-halogen.

nsaunders avatar nsaunders commented on June 28, 2024

@cakekindel are you still interested in this issue?

If so, I think I could build a strongly-typed API along these lines:

HH.button
  [ X.style $
      color := rgb 200 0 0
      hover ? Rule.do
        color := rgb 255 0 0
  ]
  [ {- ... -} ]

Please let me know if you'd find something like that useful.

from purescript-halogen.

cakekindel avatar cakekindel commented on June 28, 2024

Ooh I like it a lot! I would definitely find this useful.

from purescript-halogen.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.