ursi / purescript-elmish

this will have its own name eventually
https://github.com/ursi/purescript-package-set
3 stars 0 forks source link

Compiled CSS does not respect Style order #1

Closed Quelklef closed 3 years ago

Quelklef commented 3 years ago

MWE:

module Main (main) where

import Prelude
import Html (Html)
import Html as H
import Css as S
import Platform (Program, app)

main :: forall msg. Program Unit Unit msg
main = app
  { init: \_ -> pure unit
  , update: \m _ -> pure m
  , subscriptions: const mempty
  , view
  }

view :: forall model msg. model -> { head :: Array (Html msg) , body :: Array (Html msg) }
view _ =
  { head: [ ]
  , body: [ button ]
  }
  where
    button =
      H.buttonS
      [ S.fontSize "3em"
      , S.hover [ S.background "red" ]
      , S.active [ S.background "cyan" ]
      ]
      [ ]
      [ H.text "button" ]
Expected behaviour: button hovered button active button background
no no grey
yes no red
no yes cyan
yes yes cyan
Actual behaviour: button hovered button active button background
no no grey
yes no red
no yes cyan
yes yes red <- wrong

The issue is that the CSS is generated as

._2832520887 { font-size: 3em; }
._2832520887:active { background: cyan; }
._2832520887:hover { background: red; }

with the :active rule before the :hover rule despite the code listing them in the opposite order.

Quelklef commented 3 years ago

Looks like perhaps the issue is that VirtualDom.Css.process :: List Style -> Maybe { class :: String, css :: String } first converts the styles to a map before processing them, thus losing any order?

ursi commented 3 years ago

I think you are correct in your assumption, which is unfortunate because that means solving this is going to be tricky. In the meantime you can use S.important to achieve the behavior you want in this instance.

Quelklef commented 3 years ago

:flushed: I had been appending "!important" to within the string itself

Is there a reason that process couldn't do the most naive algorithm, each Style becoming one declaration, giving output like the following?

._2832520887 { font-size: 3em; }
._2832520887 { font-weight: bold; }
._2832520887 { color: red; }
ursi commented 3 years ago

Okay, well I think I wasn't as familiar with the code as I thought, There's two levels of maps going on, one is essential to the functionality of the approach, the other, this one, not as much. I think we could probably do a hybrid approach that's slightly less naive where instead of building up a list of things with the same StringOp, we build a nested list of styles that preserves order. When inserting we'd check if the current style has the same StringOp as the last style, if it does we add it to that list, if it doesn't we put it in its own list. I think this will often times end up with the same output in the end, other than the order not being preserved. It also might be more performant.

ursi commented 3 years ago

tyvm