STYLING ELM APPLICATIONS WITH TACHYONS AND ELM-CSS

TL;DR

I was recently exploring ways of styling elm applications with the elm-css package, while also leveraging the small utility CSS classes of the Tachyons CSS Toolkit.

I chose to spike the idea by creating a pomodoro timer we could use inhouse for pair programming, which would then double as a reference. You can view the source of the spike here.

Finally, You can see our first productionized elm app ( and potentially register as a foster carer ) here.

Introducing elm to the workplace

Introducing elm into the tech stack at Life Without Barriers was a no brainer.

My team works with a series of successful GatsbyJS static generated sites, and as we had an upcoming SPA that didnt necessarily have to stick 100% within our usual guidelines, we thought about looking elsewhere in an attempt to keep things interesting, while looking for an opportunity to upskill.

The only caveat was that the chosen framework had to be able to communicate with an existing GatsbyJS project. Seeing as elm interops with JS via ports, I knew this wasn’t an issue. Another bonus was that you can mount an elm app in your React ecosystem with a component such as react-elm-components. The elm framework has been so successful in many of my personal projects that I knew immediately it was suitable for the context of the upcoming SPA. Our team got together to discuss the pros and cons of forging ahead with elm.

The pros were that 2 out of 4 developers on our team are familiar with elm_, and that _elm could achieve the majority the desired functionality out of the box.

The cons were that there was going to be a steep learning curve for the 2 developers without elm experience - and more importantly - How are we going to approach styling the elm app?

We has a deadline of 4 weeks to complete the SPA, so it was time to spike out the styling aspect before moving forward.

Over the last 12 months we’ve defined a solid pattern of applying custom styles for elements using Styled Components, while leveraging Tachyons for layout and positioning. This has been quite a successful approach, and one I wanted to retain, with the advantages being well organised critical custom styles, with minimal duplication of rules.

I decided to look into elm-css. elm-css let’s you define type safe CSS in your elm applications. Drawing inspiration from Sass and Styled Components (among others), elm-css is a fully featured library enabling you to create/extend CSS styles, as well as create reusable styled elements.

I hadn’t used elm-css in a project before as it always seemed a bit of a behemoth, and I couldn’t really find any decent ‘real world’ examples to learn from, but the ‘reusable styled elements’ approach had peaked my interest for this project.

Here is a modest example of 3 styled elements (div, h1 and button) using elm-css.

We define a function for each of our ‘styled elements’. Each function then references the elm-css styled function, which accepts an element and a list of styles as parameters. We can then use these styled elements in the view function.

Ok great. That was easier than I thought.

Now, How can we leverage Tachyons to help minimise duplication in our styles?

Well, that’s even easier, and there are multiple ways.

Using Tachyons and elm-css : option 1

You could add the Tachyons stylesheet to the head of your project and just reference the Tachyon class names as per normal. This would mean you could then remove corresponding properties from your styled element functions, offloading them to _Tachyons_, while leaving only the critical styles.

<html>
  <head>
    <link rel="stylesheet" href="https://unpkg.com/tachyons@4.10.0/css/tachyons.min.css"/>
    ...
view model =
  blockButton
    [ onClick (SwitchMode Break)
    , class "bn br1 pointer outline-0 lh-solid relative"
    ]
    [ text "Break" ]

Using Tachyons and elm-css : option 2

You could also create a stylesheet in the body of your elm app by simply defining a node that references the Tachyons styles.

Tachyons-elm is an elm package that includes a helper function for outputting the content of a tachyons stylesheet.

...
import Tachyons exposing (tachyonsStylesheet)

view model =
  wrapper
  [ isBreakMode model.mode
  , class "overflow-hidden pa3 tc"
  ] [
    -- create a stylesheet
    node "style" [] [ text tachyonsStylesheet ]
    , ...
  ]

Using Tachyons and elm-css : option 3

Tachyons-elm also provides a typed function for each of the tachyons classes, which you can apply to your elements in a similar fashion as the previous examples, using it’s classes helper function.

import Tachyons exposing (classes, tachyonsStylesheet)
import Tachyons.Classes exposing (pa3, red, f1)

view model =  
  div [classes [pa3, red, f1]] []

One caveat of using Tachyons-elm and elm-css together is that the complier will complain about _Tachyons-elm‘_s classes function not being of type Html.Styled.Attribute. This is an easy workaround if you so desire to use this function.

import Tachyons exposing (tachyonsStylesheet)
import Tachyons.Classes exposing (pa3, red, f1)

classes : List String -> Attribute msg
classes stringList =
    class (String.join " " stringList)

view model =  
  wrapper [ classes [pa3, red, f1] ] []

Consclusion

So this looks like a great solution to me. I can create my styled elements with elm-css while offloading any repeated CSS rules to Tachyons. I’m happy with this approach.

As my spike progressed I wanted to see how much I could push elm-css. How would it handle things like animations, transitions, gradients, etc? There didn’t seem to be many obvious ‘real world’ examples out there that went beyond very basic styles, so I decided to take what I’ve learnt so far and apply it to a small project that would could use at LWB.

As pair programming is important in our work culture at LWB, I decided to code a pomodoro timer (https://tomatotmr.com/) that we could use inhouse. This project could then double as a reference of using elm-css with tachyons.

You can view the source of the pomodoro timer here.

As for our first productionized Elm app, we smashed our deadline of 4 weeks and we had completed project in 1 and a half weeks. I honestly put this all down to elm. The fact that there is minimal set up to get up and running with reactive views and state management (not to mention a helpful compiler), our team was able to spend more time learning and implementing elm.

You can view our Foster Carer Self Assessment app (and potentially register as a foster carer) here.

I really enjoyed working with elm-css. There are definatly some limitations to it, such as radial-gradients or unsupported properties like content: "". But these could be worked around by applying a little bit of creativity as well as the elm-css ‘catch all’ function property.

Alternatives

If you’re looking at alternative ways of styling your elm applications there are a couple options I feel are also worth mentioning. Be sure to checkout the elm packages elm-tailwind and elm-ui.