purescript-contrib / purescript-css

A clean, type-safe library for describing, manipulating and rendering CSS
Apache License 2.0
106 stars 40 forks source link

Could each CSS names (elements, ids and classes) have specific types? #41

Open cies opened 8 years ago

cies commented 8 years ago

I guess it is not possible to implement this idea --if at all possible-- in purescript-css, it will need a library of it's own.

What I mean by specific types for CSS names can be seen in this Elm library, elm-css. It allows type checking of ids and classes between your style definitions and HTML templates. Which to me seems like an amazingly good idea. :)

Possibly a library that implements this already exist for PureScript, but that I'm simply unaware of it.

garyb commented 8 years ago

Do you mean introducing newtypes to distinguish between them or something? Sorry, I'm not quite sure what you're getting at. If so, sounds good to me!

cies commented 8 years ago

Please have a look at the elm-css lib. It pretty much explains itself.

I would not know if this can be achieved with only newtypes. I guess not as you want to distinguish between ids and classes.

On Aug 18, 2016 13:18, "Gary Burgess" notifications@github.com wrote:

Do you mean introducing newtypes to distinguish between them or something? Sorry, I'm not quite sure what you're getting at. If so, sounds good to me!

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/slamdata/purescript-css/issues/41#issuecomment-240694651, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAh9h_saP08ZcWA-iGeL3w2DqnLdCzMks5qhD9bgaJpZM4JlNgH .

cies commented 8 years ago

Ok, more time to go indepth now. :)

See here the defintion of some CSS using elm-css.

type CssClasses = NavBar
type CssIds = Page

css =
    (stylesheet << namespace "dreamwriter")
    [ body
        [ overflowX auto
        , minWidth (px 1280)
        ]
    , (#) Page
        [ backgroundColor (rgb 200 128 64)
        , color (hex "CCFFFF")
        , width (pct 100)
        , height (pct 100)
        , boxSizing borderBox
        , padding (px 8)
        , margin zero
        ]
    , (.) NavBar
        [ margin zero
        , padding zero
        , children
            [ li
                [ (display inlineBlock) |> important
                , color primaryAccentColor
                ]
            ]
        ]
    ]

Pretty similar to what it looks like with purescript-css, but with some actual types for NavBar and Page. Now these will be available when rendering HTML.

import Html.CssHelpers
import MyCss

{ id, class, classList } =
    Html.CssHelpers.withNamespace "dreamwriter"

view =
    Html.div []
        [ Html.div [ class [ MyCss.NavBar ] ] [ Html.text "this has the NavBar class" ]
        , Html.div [ id MyCss.Page ] [ Html.text "this has the Page id" ]
        ]

This brings typesafety to the rescue for typos in CSS-HTML id/class names, and allows auto-complete in editors to help you out more.

garyb commented 8 years ago

Ah! I see now :smile:. I'm sure we could accomodate something like this, yes. I don't really have time to work on it right now, but it seems like a good idea.

cies commented 8 years ago

Ok @garyb, I found some time for it and have a little to show you. It is currently all implemented as part of the test suite, just as a proof-of-concept, mainly for you (and other to shoot at). It likely have to be implemented all differently, or there's an easier way to implemented it, or it can be more well-typed.

Please have a look. I commented it with explanation.

cies commented 8 years ago

Oh and I really like to change this:

data CssId = TheWrapper | TheContent
derive instance genericCssId :: Generic CssId
instance cssPredicateCssId :: CssPredicate CssId where
  toPredicate = Id <<< spineCase <<< typeAsString

data CssClass = ButtonSEO | Active
derive instance genericCssClass :: Generic CssClass
instance cssPredicateCssClass :: CssPredicate CssClass where
toPredicate = Class <<< spineCase <<< typeAsString

into something more like (I know this does not work):

data CssId = TheWrapper | TheContent
derive instance cssIdPredicates :: CssPredicate CssId

data CssClass = ButtonSEO | Active
derive instance cssClassPredicated :: CssPredicate CssClass

but I did not yet figure out how.

garyb commented 8 years ago

Yeah, as you say, you can't derive like that, but with the way you've used generics there you could just define two common functions:

toIdPredicate = Class <<< spineCase <<< typeAsString
toClassPredicate = Class <<< spineCase <<< typeAsString

And then those would work generically for the toPredicate implementation of any class:

data CssId = TheWrapper | TheContent
derive instance genericCssId :: Generic CssId
instance cssPredicateCssId :: CssPredicate CssId where
  toPredicate = toIdPredicate 

data CssClass = ButtonSEO | Active
derive instance genericCssClass :: Generic CssClass
instance cssPredicateCssClass :: CssPredicate CssClass where
  toPredicate = toClassPredicate

At least that cuts down on the boilerplate for toPredicate implementations.

I wouldn't include Generic as a superclass of CssPredicate either, as it's not required for the definition, it just makes it easier to implement toPredicate - so if you make it optional, and only have the Generic constraint on toIdPredicate / toClassPredicate it will allow people to write their own toPredicate implementations if they really need to, without using generic deriving.