# How to do CSS in elm?

**URL:** <https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830>\
**Category:** Learn\
**Created:** [March 11, 2018, 5:57pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830 "2018-03-11T17:57:56Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ghivert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ghivert/32/630_2.png) [@ghivert](https://discourse.elm-lang.org/u/ghivert)\
**Post date:** [March 11, 2018, 5:57pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/1 "2018-03-11T17:57:56Z")

</div>

Hi everyone!

Just finished reading [the last post about native code in 0.19](https://discourse.elm-lang.org/t/native-code-in-0-19/826/2), and I was wondering something. Right now @tibastral and me were working on CSS in elm with one package ([elm-bodybuilder/elegant](https://github.com/elm-bodybuilder/elegant)), and we wrote some Native code (approximately 20 lines I think) to improve rendering speed.

We tried a lot of approaches, staying purely in elm, but performances weren’t good enough. We ended up with the idea of VirtualCss handling every CSS stuff related, and it was amazing (though not called like this in the package, it is the exact idea). In the future, with the release of 0.19, how would we be able to deal with this? CSS in JS is hot stuff right now, and removing the painful CSS from elm is really so good.

I also tried the elm-css way, but it’s not the way I want to follow. It’s not really about merging CSS directly into elm, but rather to write typed-check CSS and compile it to CSS stylesheet (and it’s awesome if you want to manage stylesheets — personally I don’t).

So it’s an open question, how would we be able to do CSS in elm in the future without native code?  
If anyone has an idea it would be cool. @evancz if you have some thoughts about it, I think it would be awesome.  
Thanks! 🙂

PS : Should it be posted into elm-dev instead of here? Don’t really understand the differences… ^^’

---

<div class="post-metadata">

**Author:** ![pdamoc](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/pdamoc/32/36_2.png) [@pdamoc](https://discourse.elm-lang.org/u/pdamoc)\
**Post date:** [March 11, 2018, 6:02pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/2 "2018-03-11T18:02:07Z")

</div>

> [@ghivert](#):
>
> We tried a lot of approaches, staying purely in elm, but performances weren’t good enough.

Do you have some sample code showing this sub-optimal performance?

From my own experiments, rendering CSS to string and mounting the resulting text in a `style` element in the head (via a port) is fast enough.

---

<div class="post-metadata">

**Author:** ![ghivert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ghivert/32/630_2.png) [@ghivert](https://discourse.elm-lang.org/u/ghivert)\
**Post date:** [March 11, 2018, 6:12pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/3 "2018-03-11T18:12:38Z")

</div>

Hi.

We were doing something like this:

```elm
myRedClass : List (String, String)
myRedClass =
  [("background-color", "red")]

view : Model -> Html Msg
view model =
  Html.div 
    [Html.div [ Html.Attributes.style myRedClass] [Html.text "Hello world!"]
    , Html.div [Html.Attributes.style myRedClass] [Html.text "Hello world too!"]
    ]

```

I don’t know how to extract the CSS and throw to JS via a port…  
Actually, we built a package to abstract CSS, which means we had something like this:

```elm
myStyles : Style
myStyles =
  Style.box [Box.border [ Border.thickness 1] ]

view : Model -> Html Msg
view model =
  Html.div [Html.Attributes.class <| computeStyles myStyles] []

```

We wanted to decrease computation time took by `computeStyles`. So we ended up with VirtualCss: you just give it the style object, and do some caching on styles, avoiding computing it each times, and pushing the styles as atomic classes in the User Agent directly (with [CSSOM, insertRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule)).

The idea is really to stop dealing with CSS, but rather writing styles directly on HTML nodes, which gets compiled to faster CSS.

---

<div class="post-metadata">

**Author:** ![pdamoc](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/pdamoc/32/36_2.png) [@pdamoc](https://discourse.elm-lang.org/u/pdamoc)\
**Post date:** [March 11, 2018, 6:25pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/4 "2018-03-11T18:25:53Z")

</div>

> [@ghivert](#):
>
> I don’t know how to extract the CSS and throw to JS via a port…

The easiest way would be to have module (page modules) function like `css : Model -> String` (equivalent to `view`) and compose the relevant styles in the top level `update`. You then send the `String` to JS where you have it installed in the head style.

In the view code you only use classes to switch styles.

As I said, I found this approach fast enough when I tried it. This is why I was asking for an actual example where this sub-optimal performance can be seen.

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [March 11, 2018, 7:28pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/5 "2018-03-11T19:28:34Z")

</div>

> [@ghivert](#):
>
> Tomorrow, with the release of 0.19

I apologize for the digression, but did I miss something? If so, where was the announcement?

---

<div class="post-metadata">

**Author:** ![ghivert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ghivert/32/630_2.png) [@ghivert](https://discourse.elm-lang.org/u/ghivert)\
**Post date:** [March 11, 2018, 7:49pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/6 "2018-03-11T19:49:41Z")

</div>

Sorry, was using Tomorrow in the sense of “in a near future”. I modified it to avoid problem. 🙂

---

<div class="post-metadata">

**Author:** ![rtfeldman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rtfeldman/32/50_2.png) [@rtfeldman](https://discourse.elm-lang.org/u/rtfeldman)\
**Post date:** [March 12, 2018, 12:01am UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/7 "2018-03-12T00:01:40Z")

</div>

> [@ghivert](#):
>
> [elm-css is] not really about merging CSS directly into elm, but rather to write typed-check CSS and compile it to CSS stylesheet (and it’s awesome if you want to manage stylesheets — personally I don’t).

This used to be true, but it’s the opposite today. Check out [the latest docs](http://package.elm-lang.org/packages/rtfeldman/elm-css/latest)!

It’s all done at runtime in plain Elm now - there are no separate stylesheets anymore (or `Native` for that matter).

---

<div class="post-metadata">

**Author:** ![ghivert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ghivert/32/630_2.png) [@ghivert](https://discourse.elm-lang.org/u/ghivert)\
**Post date:** [March 12, 2018, 9:16am UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/8 "2018-03-12T09:16:07Z")

</div>

Thanks for the answer, that’s good to know! I looked at your code and immediately got few questions. I think we ended up with the same algorithm, so I’m curious.

What’s happening when you more than 400 nodes on the page? Isn’t the rendering time too long? You are parsing the same styles over and over again at each rendering right?

We tried a solution like this:

- Write your HTML as styled nodes, and store styles in the node.
- Render the view function.
- Parse the resulting tree and extract the styles.
- Compute the styles and put them in a `style` node.
- Add the `style` node at the top of the DOM, add the view after this, and send everything to the runtime.

That was a perfect algorithm, perfectly working, and we even did some caching on styles to avoid recomputing styles twice (with a Dict). But we got a time rendering issue with large pages. When just writing some pages, it wasn’t a problem, because we had approximately  
500ms for a rendering, and it was good. But we wanted to do some animations. We wanted 60 fps, so we needed at most 16ms in theory for each rendering. So we changed our strategy:

- Write your HTML nodes as usual.
- Write the style in `VirtualCss.style`, which performs side effect in JavaScript.
- The style get computing if not computing previously (maintain a cache of computed styles), and pushed into the User Agent using `CSSOM.insertRule()`.
- The corresponding classes names are returned from the function, and they are put into the HTML via `Html.Attributes.class`.
- The corresponding HTML is returned from the view and passed to the runtime.

This way, you avoid the two tree exploration. The only one needed is the runtime exploration. For huge pages, we ended up with 30ms of rendering. That changed everything to be honest.

That’s why I think it’s _really_ important to talk about CSS and CSS in elm. Performances matter, and it’s not usable for me if a page took more than 200ms to render…

I would be glad to help in this or have a constructive discussion about how we should achieve this in the language.

I’m more than happy to see that @rtfeldman ended up with similar solutions, and to see that the idea of handling styles in elm is becoming more popular! 😁

---

<div class="post-metadata">

**Author:** ![pdamoc](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/pdamoc/32/36_2.png) [@pdamoc](https://discourse.elm-lang.org/u/pdamoc)\
**Post date:** [March 12, 2018, 9:27am UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/9 "2018-03-12T09:27:42Z")

</div>

> [@ghivert](#):
>
> I would be glad to help in this or have a constructive discussion about how we should achieve this in the language.

An example showing the problem would be a good step in my perspective.

Create an realistic example that shows the issue and with that example, multiple approaches can be explored.

Make the example heavier than it needs to be. Show how pre-rendered CSS is handling that example significantly better than runtime rendered stuff.

I am super curious to see a SSCCE showing this 500ms penalty and I am confident that with an example, members of the community could point out alternative strategies that would improve things considerably.

---

<div class="post-metadata">

**Author:** ![ghivert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ghivert/32/630_2.png) [@ghivert](https://discourse.elm-lang.org/u/ghivert)\
**Post date:** [March 12, 2018, 9:44am UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/10 "2018-03-12T09:44:55Z")

</div>

You’re right, I was on my smartphone, so a little bit difficult to make proper links sometimes.

Here’s the modern, correct example: [Blog with CSSOM](https://elm-bodybuilder.github.io/elegant/blog)  
Here’s the older version: [Blog without Native Code](https://elm-bodybuilder.github.io/elegant/perfs)

The second version is not way slower, but the slow feeling is there. On more charged web pages, the difference is way more pronounced.

---

<div class="post-metadata">

**Author:** ![edkv](https://avatars.discourse-cdn.com/v4/letter/e/439d5e/32.png) [@edkv](https://discourse.elm-lang.org/u/edkv)\
**Post date:** [March 12, 2018, 12:24pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/11 "2018-03-12T12:24:41Z")

</div>

> [@ghivert](#):
>
> Here’s the modern, correct example: Blog with CSSOM  
> Here’s the older version: Blog without Native Code

As I can see, the older version uses `position: relative` for the animation, and the example with CSSOM uses `transform: translate` which should generally be much more faster. I think this is the real reason for the performance difference between two examples.

---

<div class="post-metadata">

**Author:** ![tibastral](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/tibastral/32/540_2.png) [@tibastral](https://discourse.elm-lang.org/u/tibastral)\
**Post date:** [March 13, 2018, 3:29pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/12 "2018-03-13T15:29:41Z")

</div>

> [@edkv](#):
>
> As I can see, the older version uses position: relative for the animation, and the example with CSSOM uses transform: translate which should generally be much more faster. I think this is the real reason for the performance difference between two examples.

No it’s not the reason. The transform doesn’t change anything in terms of performances (look at the detailed breakdown inside the chrome dev tools please)

---

<div class="post-metadata">

**Author:** ![edkv](https://avatars.discourse-cdn.com/v4/letter/e/439d5e/32.png) [@edkv](https://discourse.elm-lang.org/u/edkv)\
**Post date:** [March 13, 2018, 6:24pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/13 "2018-03-13T18:24:31Z")

</div>

> [@tibastral](#):
>
> No it’s not the reason. The transform doesn’t change anything in terms of performances (look at the detailed breakdown inside the chrome dev tools please)

Seems like you’re right and it’s not playing that huge role in this particular case, but I think it would be a good a idea to modify the old example to use `transform` instead if it’s possible, just to be sure that it’s really not the reason.

Also, I’ve noticed that `elegant` uses class-per-declaration approach to render styles. It would be interesting to recreate that example with `elm-css` (which uses hashes instead) and see how it compares.

But `insertRule` is definitely the winner in any case, and I’m too very interested to see `VirtualCss` in Elm.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 13, 2018, 8:46pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/14 "2018-03-13T20:46:36Z")

</div>

Hi,

Without optimizations, I also have some performance issues with several hundred nodes and `elm-css` using `Html.Styled`.

Here is an example quite close in complexity to a real list view in my app:

[https://ellie-app.com/6rrjk87H4a1/1](https://ellie-app.com/6rrjk87H4a1/1) (desktop only, move the mouse over the list)

This is very sluggish even on a powerful computer.  
I put 500 items to exagerate the issue, and each one has 4 divs, so around 2000 nodes in this example.  
The list styles are actually a little more complex in the app, therefore the latency can be felt even with a few tenths of items.

Here is the version using a CSS stylesheet without any noticeable latency:

[https://ellie-app.com/74MhgFpvfa1/1](https://ellie-app.com/74MhgFpvfa1/1)

The best optimization I can think of is to use `Html.Styled.Lazy.lazy` on `viewItem`, but this would be quite ugly in this case without `lazy4` and more (which are not available in 0.18). It would be handy to have a lazy function that can be applied only on the `List Style` or on an Html `Attribute msg`.

Edit: I get around 200ms for `requestAnimationFrame` using `Html.Styled` vs around 5ms without.

---

<div class="post-metadata">

**Author:** ![edkv](https://avatars.discourse-cdn.com/v4/letter/e/439d5e/32.png) [@edkv](https://discourse.elm-lang.org/u/edkv)\
**Post date:** [March 14, 2018, 10:44am UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/15 "2018-03-14T10:44:56Z")

</div>

@dmy The good news are that `elm-css` is not yet optimized, so there is still  
some work that can be done to improve performance (I’m not sure whether it would be enough for your case though). If I understand correctly, styles are compiled twice – the first time [to calculate hashes](https://github.com/rtfeldman/elm-css/blob/master/src/VirtualDom/Styled.elm#L180), and the second time [to build a `style` node](https://github.com/rtfeldman/elm-css/blob/master/src/VirtualDom/Styled.elm#L292) (@rtfeldman please correct me if I’m wrong).

> [@dmy](#):
>
> The best optimization I can think of is to use Html.Styled.Lazy.lazy on viewItem

I’ve simplified your example a bit to try it – and it’s still slow [https://ellie-app.com/nzW2xBHRFa1/0](https://ellie-app.com/nzW2xBHRFa1/0).

But the interesting thing is that if you look into the dev tools, you can see that the browser now spends most of the time on rendering, while in the original example it spends it on scripting. I’m not sure what’s happening, but I think the reason might be a large number of `style` nodes in the DOM.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 14, 2018, 1:21pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/16 "2018-03-14T13:21:38Z")

</div>

Thank you @edkv

> [@edkv](#):
>
> I’ve simplified your example a bit to try it

Indeed, using `lazy` with reusable views is a pain without `lazy4` & co., this has a lot of impact on models and view configs and AFAICT it is sometimes barely possible at the reusable view function top level without storing ugly lists of parameters in the model.

> [@edkv](#):
>
> and it’s still slow

It is actually very fast on Firefox Quantum, maybe thanks to their new style renderer, but still slow on Chrome. So the situation will most likely also improve from the browsers side.

> [@edkv](#):
>
> The good news are that elm-css is not yet optimized, so there is still some work that can be done to improve performance (I’m not sure whether it would be enough for your case though).

I think I will use a few Foreign styles (only those rendered a lot) until the performance has improved enough for my use case.

Anyway, hopefully this example has illustrated the question asked by @ghivert and contributed to the discussion.

---

<div class="post-metadata">

**Author:** ![rtfeldman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rtfeldman/32/50_2.png) [@rtfeldman](https://discourse.elm-lang.org/u/rtfeldman)\
**Post date:** [March 14, 2018, 2:20pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/17 "2018-03-14T14:20:44Z")

</div>

There are three major performance optimizations I want to do for `elm-css`:

1. [Move the compatibility checks to a phantom type](https://github.com/rtfeldman/elm-css/issues/375), so they no longer have runtime overhead.
2. [Hash things more efficiently](https://github.com/rtfeldman/elm-css/blob/9fa5e9cbc69cefd089556255a1cb6aa3581b43f0/src/VirtualDom/Styled.elm#L180-L181). Plenty of room for improvement there.
3. Use `insertRule`. This is on Evan’s radar, and he’s talked through some API ideas with me. `style-elements` would benefit from `insertRule` too.

In the vein of [Make it Work, Make it Right, Make it Fast](http://wiki.c2.com/?MakeItWorkMakeItRightMakeItFast), the current `elm-css` API has gotten positive responses on its working and working right, so it makes sense to start shifting focus to performance!

---

<div class="post-metadata">

**Author:** ![ghivert](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ghivert/32/630_2.png) [@ghivert](https://discourse.elm-lang.org/u/ghivert)\
**Post date:** [March 14, 2018, 2:24pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/18 "2018-03-14T14:24:37Z")

</div>

Performance really interest me 🙂 . Is there any way to know a bit more about the API ideas you are talking about?

---

<div class="post-metadata">

**Author:** ![mdgriffith](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mdgriffith/32/61_2.png) [@mdgriffith](https://discourse.elm-lang.org/u/mdgriffith)\
**Post date:** [March 14, 2018, 3:27pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/19 "2018-03-14T15:27:03Z")

</div>

So, some additional thoughts on performance in addition/to elaborate on what Richard mentioned.

Style elements now uses an atomic css approach to generating style classes, which means one property per class, and the name of the class is esssentially the value of the property.

So, for `background-color: rgb(0,0,0,1)`, we render

```auto
.bg-0-0-0-1 { background-color: rgb(0,0,0,1); }

```

This also allows us to reduce the sheer number of css rules we have to render by running a deduplication step. This is a big gain because we can skip the work of translating everything into strings, and we can also help the browser skip work by not giving it more things to parse and figure out.

This also allows us to have a more targeted hash-like thing in a super fast way.

I also found that it’s really beneficial to separate styles into a static sheet and a dynamic sheet. Move absolutely everything you can into a static sheet instead of rendering on every frame.

---

<div class="post-metadata">

**Author:** ![rtfeldman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rtfeldman/32/50_2.png) [@rtfeldman](https://discourse.elm-lang.org/u/rtfeldman)\
**Post date:** [March 14, 2018, 5:47pm UTC](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830/20 "2018-03-14T17:47:05Z")

</div>

> [@ghivert](#):
>
> Is there any way to know a bit more about the API ideas you are talking about?

It was very preliminary - basically just looking at articles on how to get the best CSS perf at runtime, concluding that `insertRule` was the fastest, thinking about ways that could work with `elm-lang/html`, etc.

[Next page](https://discourse.elm-lang.org/t/how-to-do-css-in-elm/830.md?page=2)
