Elm Beginners Tutorial: How to make animated snackbars with zero CSS
Tags: tutorial, elm, css, webdev
Published: 2020-04-13 01:11:15

[Demo & Code](https://ellie-app.com/8zrnNH79HWqa1)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sz9zupp2ajhiv27zpq02.gif)

This is a beginner tutorial to build animated [snackbars](https://material.io/components/snackbars/). It should be understandable also if you never coded in Elm before. If you are already familiar with Elm you can skip the first section __Short Elm overview__.






# Short Elm overview

Find here some random concept about Elm specifically related to this tutorial for a more serious or comprehensive introduction to Elm you should refer to the [official guide](https://guide.elm-lang.org/).

In Elm everything is a function and functions are NOT written like

```Javascript
var sum = function(x, y) { return x + y; };
```

or

```Javascript
const sum = (x, y) => x + y;
```

but like

```Elm
sum x y = x + y
```

it is basically the same as your favorite language but without all the interstitial decorations.

Sometime above the function there is the "type signature". It describes the type of arguments that go in and out. It is optional but I usually write them everywhere. Like in

```Elm
sum: Int -> Int -> Int
sum x y = x + y
```

The last thing in the type signature, after the last arrow, is always the type of the returned value as functions can only return one value.

Some other time we will find triangles (▷ ◁) in the code . These are ["pipe operators"](https://package.elm-lang.org/packages/elm/core/latest/Basics#|%3E) and will display as triangles only if we use a font with ligatures, otherwise they will just render as |> <|.

The pipe operators reduce the needs of parentheses and in Elm we like uncluttered code. In practices they pass stuff from one side to the other.

So for example, let's suppose we have 4 functions calling each other:

```Elm
f ( g ( h ( i "Ciao" ) ) )
```

the same thing can be written as

```Elm
f <| g <| h <| i "Ciao"
```

or

```Elm
"Ciao" |> i |> h |> g |> f
```

that is usually formatted as

```Elm
"Ciao"
    |> i
    |> h
    |> g
    |> f
```

No need to count parenthesis anymore 🎉

For more info about syntax, refer to the [official syntax documentation](https://elm-lang.org/docs/syntax)

In Elm we can define our own types, that is pretty cool 😎

For example:

```Elm
type Fruit = Apple | Pear | Banana
```

is a type that can be either Apple, Pear or Banana.

When instead we see the word "alias" after "type", that is not a type definition but we are just giving a different name to the existing type. For example

```Elm
type alias HealthyFood = Fruit
```

Elm applications are usually written following what we call TEA that stand for The Elm Architecture. It is just a loop that waits for events (clicks, etc.) and when they happen, it sends them to us so that we can react and change the interface accordingly.

This animation explains The Elm Architecture cycle:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/k45h7gelibw1qsf7o8no.gif)

All that we need to do is to write the 2 functions on the right of the diagram:

* The `update` that take the event (we call "message" in Elm) together with the model that is a data structure containing the entire state of the application and return a new model (and possibly other stuff)
* The `view` that simply convert the model into HTML

Moreover:

* All data is immutable
* Functions must be pure (i.e. "No side effects" and "Same input == Same output")
* Function start with a small case letter (e.g. `sum`), types or type aliases with capital case letter (e.g. `Fruit`)
* Instead of objects Elm has records. They resemble Javascript objects but instead of `:` they need `=` as in `{ x = 3, y = 4 }`. Also being immutable we don't do `point.x = 42` but we can do `{ point | x = 42 }` that is like saying "Please make a copy of `point` and change only the `x` value". If you are coming from Javascript, you can find this and other useful examples in https://elm-lang.org/docs/from-javascript
* We will use [`mdgriffith/elm-ui`](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/), the library that will take care of smartly generate HTML and CSS for us, so we don't need to write them. elm-ui has some resemblance to CSS, for example the concept of `padding`. But it is quite different from CSS, for example it doesn't have `margin` but rather `spacing` that is simply the distance between children. Other two main concepts are `column` and `row` that are not existing in HTML/CSS.

I think this is all that we need to know about Elm. Elm is a pretty simple language as it has a small but expressive set of language constructs. This doesn't mean that we cannot write complex applications with it.

Some of the Javascript concepts that are not present (or not needed) in Elm are:

* null
* undefined
* this
* closures
* callbacks
* promise
* async/await
* hoisting
* prototypes
* class
* apply/call/bind
* truthiness
* try...catch
* type coercion
* throw
* var/let/const
* return
* for/do...while
* spread syntax
* rest syntax
* new
* super
* ++
* +=
* == vs. ===
* IIFE
* value vs. reference
* yield/generators
* runtime exceptions
* order of execution
* strange results are better than errors

Some of the concepts that are in Elm but not in Javascript are: 

* immutability
* purity
* custom types
* pattern matching
* function composition
* static types
* type inference
* a compiler
* errors at compile time are better than strange results

In Elm we don't write a list of operations to be performed in order but rather we define a list of pure functions that call each other.

In the case of a "list of operations" we need to keep track of everything computed to that point, that is the "state". Changing the state is the "side effect" of the operations.

In the case of "list of pure functions", there is no need to keep track of anything (i.e. stateless) because the output of pure functions depends completely on the input. The code can be reasoned just by looking at a function’s input and output.

An as [John Carmack](https://en.wikiquote.org/wiki/John_D._Carmack) once said "A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in”

To learn more:

https://guide.elm-lang.org/
https://package.elm-lang.org/packages/elm/core/latest/







# Tutorial

* The __Model__ data structure
* The __Messages__
* The __update__ function
* The __view__ function
* Wire all together

This is what we will get at the end

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sz9zupp2ajhiv27zpq02.gif)

[Demo & Code](https://ellie-app.com/8zrnNH79HWqa1)


## The __Model__ data structure

Let's decide the structure of the model that will contain the entire state of the application.

But wait, why are we talking about the state of the application while we are also saying that functional programming is stateless?

How can we change such a state while remaining pure?

The trick is simple. We pass _the-entire-state-of-the-applications_ to functions that need to modify it and they will return an updated version of the _the-entire-state-of-the-applications_.

This is why the update function has this type signature

```Elm
update : Msg -> Model -> Model
```

It is saying "I want to modify _the-entire-state-of-the-applications_ (model) but I also want to remain pure so please pass me the model, I will make a copy of it and I will return to you the new version without changing the old version".

The Elm Runtime will then take this new Model and, with the help of the view function, will change the state of the browser. So eventually is only the Elm Runtime that can have side effects. 100% of the code that we write is pure, without side effects.

But let's move back to our tutorial.

A description of a Snackbar can be

* Some __content__, for example text, images, etc.
* A __style__, for example a background color
* A __state__ that describe if a snackbar is __Entering__, is __Active__ or is __Leaving__ the view
* The __lifeSpan__, that is the duration in milliseconds of the snackbar, 0 meaning forever active

In code:

```Elm
type alias Snackbar =
    { content : Element.Element Msg
    , style : Style
    , state : State
    , lifeSpan : Float
    }
```

Let me explain a bit more why the type of `content` is `Element.Element Msg`.

The prefix `Element.` in the reference to the external package `elm-ui`. The second `Element` is actually a type (capitalized first letter) [defined inside the library](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/Element#Element).

In the following example snippets I will omit the prefix `Element.` because I usually import this library with `import Element exposing (..)` that will automatically expose all of the functions/types of the module so that `Element.Element` can be written as `Element`, `Element.columns` as `column`, etc.

All other functions also live in modules but they don't need any import or prefix because they are already [imported and exposed by default](https://package.elm-lang.org/packages/elm/core/latest/)

Why is `elm-ui` not imported as `import Ui`? This is because this is not an enforced rule. I opened an [issue to make this more explicit in the documentation](https://github.com/elm/package.elm-lang.org/issues/334).

Back to the tutorial.

For the __state__ that describe if a snackbar is __Entering__, is __Active__ or is __Leaving__ the view we use a custom type:

```Elm
type State
    = Entering Float
    | Active Float
    | Leaving Float
```

The number attached to each state will tell us how "old" is the snackbar in each state. It is the number of milliseconds since the snackbar entered that state.

This is an example of snackbar life time:

```
added to the Model => Entering 0.000
Entering 0.033
Entering 0.066
...
Entering 0.966
Entering 1.000 => change state to Active 0.000
Active   0.001
Active   0.002
...
Active   0.999
Active   1.000 => change state to Leaving 0.000
Leaving  0.033
Leaving  0.066
...
Leaving  0.966
Leaving  1.000 => removed from the Model
```

Numbers increase 60 times per seconds and the quantity they increase depends on the speed of each transition.

They are always in the range 0 ~ 1. 0 is the beginning of the transition, 1 is the end.

For example if we want the Entering transition to last 0.5 seconds (30 increases), each time we should increase by 1/(60 * 0.5) = 1/30 = 0.03333 so that after 30 increases, it reaches 1.

Don't worry if this is confusing. Just remember that the number always moves gradually from 0 to 1.

At any point in time there can be 0 or more snackbar on the page so the entire state of the application (the `Model`) will be

```Elm
type alias Model = List Snackbar
```

When the application load the first time there will be no snackbars so we can initialize the Model with an empty list:

```Elm
initModel : Model
initModel = []
```

Note that in the demo we actually pre-populate the model with some snackbar examples.





# The __Messages__

Let's make a list of action that we want to perform

* Add a snackbar
* Remove a snackbar
* Remove all snackbars

Now we can translate this into a list of Messages that will tell to our `update` function what to do

```Elm
type Msg
    = Add Snackbar
    | Close Int
    | CloseAll
    | OnAnimationFrame Time.Posix
```

We also added `OnAnimationFrame Time.Posix` that is a special message that fires approximately 60 times per second and is synced with the browser's repainting. This is the same of Javascript [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) and is the beast approach to build smooth animations.

Also note that `Add` requires a `Snackbar` while `Close` requires an `Int` that is the position in the list of the snackbar that we want to remove.

# The __update__ function

The update function, in its simplest form, has this type signature

```Elm
update : Msg -> Model -> Model
```

A more advanced form is

```Elm
update : Msg -> Model -> (Model, Cmd.msg)
```

This version can handle __Commands__ but we don't need them for this tutorial.

Commands are needed when we need the runtime to do some side effects but if the only side effects that we need are related to changing the page, these are already taken care of by the `view` function.

An example of Command is a request to send an HTTP request.

Going back to the simplest form, this is the skeleton:

```Elm
update : Msg -> Model -> Model
update msg model =
    case msg of
        Add snackbar ->
            ...

        Close index ->
            ...

        CloseAll ->
            ...

        OnAnimationFrame _ ->
            ...
```

Let's start filling the [...] in order of simplicity

## Add

Adding s snackbar is trivial:

```Elm
snackbar :: model
```
`::` is the operator that appends an item at the beginning of the list. The new snackbar should be in the state `Entering 0` but we don't enforce it here.

This is too simple so let's rewrite in a more complicated way:

```Elm
model
    |> (::) snackbar
```

The reason why we use the form above is for readability. 🤔

It will be clearer later once the update function is completed.

Note that `(::)` is the same as `::` but it accepts the argument as a regular function, all of them on the right side. It is called __infix notation__ and yes, the `::` operator, like all other operators, [is actually a function](https://package.elm-lang.org/packages/elm/core/latest/List#::).


## CloseAll

This is also pretty simple. We want to change the state of all snackbar to `Leaving 0` meaning "The beginning of the Leaving transition".

Let's make a mini-helper as it can be used in multiple places:

```Elm
close : Snackbar -> Snackbar
close snackbar =
    { snackbar | state = Leaving 0 }
```

then we close all the snackbars

```Elm
model
    |> List.map close
```

## Close

This is also pretty simple as it resembles `CloseAll` but should be applied to only one snackbar. Considering `index` as the position of the snackbar in the list of snackbars we can use List.indexedMap:

```Elm
model
    |> List.indexedMap (\index_ snackbar -> closeIf (index_ == index) snackbar)
```

Few notes here

* [`List.indexedMap`](https://package.elm-lang.org/packages/elm/core/latest/List#indexedMap) is like an usual map function but also gives the index of each item used to detect the snackbar that we want to close.
* When things start with "\" (that looks like a "λ" if we squint, a tribute to [Lambda Calculus](https://en.wikipedia.org/wiki/Lambda_calculus)), it means it is an anonymous function. We use this when we need a throwaway function to be used only once. So, the sum functions explained at the beginning `sum a b = a + b` can be written as `sum = \a b -> a + b`.
* `index_ == index` is the condition that we use to decide if we close a snackbar or not. The decision is taken by this little helper.

```Elm
closeIf : Bool -> Snackbar -> Snackbar
closeIf bool snackbar =
    if bool then
        close snackbar

    else
        snackbar
```

# OnAnimationFrame

Now we need to take care of the last section of the update function: `OnAnimationFrame`. This is a two step process:

1. We increase the "age" associate to the state and, if the number reaches 1, we change to the next state.
2. We remove all snackbars that reached the final state of `Leaving 1`

```Elm
model
    |> List.map (\snackbar -> { snackbar | state = nextState snackbar.lifeSpan snackbar.state })
    |> List.filter (\snackbar -> snackbar.state /= Leaving 1)
```

The first step is using couple of extra functions:

```Elm
nextState : Float -> State -> State
nextState lifeSpan state =
    case state of
        Entering value ->
            nextStateHelper (value + durationEntering) Entering (Active 0)

        Active value ->
            nextStateHelper (value + lifeSpan) Active (Leaving 0)

        Leaving value ->
            nextStateHelper (value + durationLeaving) Leaving (Leaving 1)


nextStateHelper : number -> (number -> a) -> a -> a
nextStateHelper newValue thisState_ nextState_ =
    if newValue > 1 then
        nextState_

    else
        thisState_ newValue
```

Done! This is all about the `update` function that is the core of our application. This is how the final results looks like:

```Elm
update : Msg -> Model -> Model
update msg model =
    case msg of
        Add snackbar ->
            model
                |> (::) snackbar

        Close index ->
            model
                |> List.indexedMap (\index_ snackbar -> closeIf (index_ == index) snackbar)

        CloseAll ->
            model
                |> List.map close

        OnAnimationFrame _ ->
            model
                |> List.map (\snackbar -> { snackbar | state = nextState snackbar.lifeSpan snackbar.state })
                |> List.filter (\snackbar -> snackbar.state /= Leaving 1)
```

See how all cases now follow a `model |>` pattern?






# The __view__ function

Let's start with some helpers.

This is a function that, depending on the state, it calculate the proper sizes of the snackbar:

```Elm
calculateValues : State -> { alpha : Float, height : Float, width : Float, font : Float, widthTimer : Float }
calculateValues state =
    case state of
        Entering value ->
            { alpha = value
            , height = maxHeight * value
            , width = maxWidth * value
            , font = maxFont * value
            , widthTimer = 0
            }

        Active value ->
            { alpha = 1
            , height = maxHeight
            , width = maxWidth
            , font = maxFont
            , widthTimer = maxWidth * value
            }

        Leaving value ->
            { alpha = 1 - value
            , height = maxHeight * (1 - value)
            , width = maxWidth * (1 - value)
            , font = maxFont * (1 - value)
            , widthTimer = maxWidth * (1 - value)
            }
```
Thanks to the fact that value goes from 0 to 1, all these calculations are trivial. `alpha` is the same as the opacity in CSS.

Now we can apply the calculated value to render the snackbar. Let's enter in the elm-ui realm

```Elm
viewSnackbar : Int -> Snackbar -> Element Msg
viewSnackbar index snackbar =
    let
        calculated =
            calculateValues snackbar.state
    in
    Input.button
        [ centerX
        , height <| px <| round calculated.height
        ]
        { onPress = Just <| Close index
        , label =
            el
                [ paddingEach { top = 10, right = 0, bottom = 0, left = 0 }
                , height fill
                ]
            <|
                column
                    ([ Border.rounded 10
                     , clip
                     , width <| px <| round calculated.width
                     , height fill
                     , alpha calculated.alpha
                     ]
                        ++ snackbar.style
                    )
                    [ row
                        [ width fill
                        , centerY
                        , paddingXY 10 0
                        , fontSize <| calculated.font
                        ]
                        [ snackbar.content
                        , el [ alignRight ] <| text "✕"
                        ]
                    , el
                        [ height <| px 5
                        , width <| px <| round calculated.widthTimer
                        , Background.color <| rgba 1 1 1 0.5
                        , alignBottom
                        ]
                      <|
                        none
                    ]
        }
```
This is all needed to render one snackbar. I believe it is quite intuitive. I can write a separate post about this if there are questions.

To render all the snackbars:

```Elm
viewSnackbars : Model -> List (Element Msg)
viewSnackbars model =
    List.indexedMap (\index snackbar -> viewSnackbar index snackbar) model
```

and to add this to a generic page:

```Elm
view : Model -> Html.Html Msg
view model =
    layout
        [ inFront <|
            column
                [ alignBottom
                , moveUp 20
                , centerX
                ]
            <|
                List.indexedMap
                    (\index snackbar -> viewSnackbar index snackbar)
                    model
        ]
    <|
        text "Your regular page here"
```




# Wire all together

Time to wire all together! We need to tell the Elm runtime where to find the `update` and `view` functions and how to initialize the `Model`.

Elm has several ways to connect these things together based on the level of expertise of the developer. For this demo we could almost use the simpler, the [Browser.sandbox](https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox)

```Elm
sandbox :
    { init : model
    , view : model -> Html msg
    , update : msg -> model -> model
    }
    -> Program () model msg
```

The only thing that is missing here is that we need to subscribe to the browser's onAnimationFrame and this is only allowed from the next level, the [Browser.element](https://package.elm-lang.org/packages/elm/browser/latest/Browser#element)

```Elm
element :
    { init : flags -> ( model, Cmd msg )
    , view : model -> Html msg
    , update : msg -> model -> ( model, Cmd msg )
    , subscriptions : model -> Sub msg
    }
    -> Program flags model msg
```

This level introduces 3 new concepts, the Commands, the Flags and the Subscriptions.

We don't need either Commands nor Flags. Commands are a way to tell Elm to do some side effects, as we discussed earlier, while Flags are a system to pass values from Javascript at the start of the application.

Anyway, let's plug everything, assuming small modifications are done so that all type signature matches:

```Elm
main : Program () Model Msg
main =
    Browser.element
        { init = init
        , view = view
        , update = update
        , subscriptions =
            \model ->
                if List.isEmpty model then
                    Sub.none

                else
                    Browser.Events.onAnimationFrame OnAnimationFrame
        }
```
Couple of notes here.

* The `()` in the type signature, in the place that contain the type of Flags, means that we are no expecting any Flag. `()` is the [__unit type__](https://en.wikipedia.org/wiki/Unit_type) that allows only one value so it cannot hold any information.
* The subscription to OnAnimationFrame is done only in the case that at least one snackbar exists, otherwise is not necessary and the application will keep looping when there is no need for it.

If you are interested in animation there are several packages in the Elm repository, including [elm-playground](https://package.elm-lang.org/packages/evancz/elm-playground/latest/) and [elm-animator](https://package.elm-lang.org/packages/mdgriffith/elm-animator/latest/).

This is all. Thank you for reading!

[Demo & Code](https://ellie-app.com/8zrnNH79HWqa1)


----

Picture in the cover: "[The interior of a snack bar in the Netherlands](https://commons.wikimedia.org/wiki/File:2015_1016_Snackbar_Kwalitaria_Leidsenhage.jpg)" by [Takeaway](https://commons.wikimedia.org/wiki/User:Takeaway) [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0)