Change CSS Properties from Elm with CSS Custom Properties
Tags: elm, css, webdev
Published: 2023-08-03 12:17:02

---
title: Change CSS Properties from Elm with CSS Custom Properties
published: true
description: Learn how to change CSS properties from Elm with CSS custom properties.
tags: elm, css, webdev
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-08-03 12:10 +0000
---

**CSS custom properties** (sometimes called **CSS variables**) are like variables but not quite. The key distinguishing feature of a custom property is that it is tied to a selector and if its value changes, it affects all matching DOM elements just like any other CSS property. This cannot be achieved with preprocessor variables, JavaScript variables, or [Elm variables](https://github.com/elm/html/issues/129#issuecomment-313608729).

Learn more: [A Strategy Guide To CSS Custom Properties](# A Strategy Guide To CSS Custom Properties).

Suppose you want to draw some circles on a canvas. For e.g. like in the [Circle Drawer task](https://dwayne.github.io/elm-7guis/circle-drawer.html) from [7GUIs](https://7guis.github.io/7guis/). One way to do it would be to use inline styles. `joakin` takes this approach in [`viewCircle`](https://github.com/joakin/elm-7guis/blob/45a53c619df6ccda49ed37b5dc60ac2787286d53/src/Tasks/CircleDrawer/Main.elm#L275-L312):

```elm
--
-- N.B. The unnecessary parts have been removed for clarity.
--
viewCircle : Circle -> Html msg
viewCircle { x, y, radius } =
    div
        [ style "position" "absolute"
        , style "left" <| px x
        , style "top" <| px y
        , style "width" <| px (radius * 2)
        , style "height" <| px (radius * 2)
        , style "transform" <|
            "translateX(-"
                ++ px radius
                ++ ")"
                ++ "translateY(-"
                ++ px radius
                ++ ")"
        , style "border-radius" "50%"
        , style "border" "1px solid black"
        , style "background-color" "white"
        ]
        []
    )
```

It certainly gets the job done but it feels icky to me. I prefer to use an external CSS stylesheet and use Elm to control which styles get applied via CSS classes. I've found that in practice you get a cleaner separation between your business logic and design.

Let me show you what I mean.

In the [external CSS stylesheet](https://github.com/dwayne/elm-7guis/blob/17897d548ac17b1077e79ecc5ac78b881879f8d4/css/circle.css) I'd put:

```css
.circle {
	--circle-x: 0;
	--circle-y: 0;
	--circle-diameter: 30px;

	position: absolute;
	left: var(--circle-x);
	top: var(--circle-y);
	transform: translate(-50%, -50%);

	inline-size: var(--circle-diameter);
	aspect-ratio: 1;
	border-radius: 50%;

	border: 1px solid black;
	background-color: white;
}
```

The CSS variables `--circle-x`, `--circle-y`, and `--circle-diameter` allow me to control the position and size of the circle.

Normally my next step would be to create a few [modifier classes](https://dev.to/dwayne/my-notes-on-bem-4dni).

```css
.circle--small {
	--circle-x: 5px;
	--circle-y: 5px;
	--circle-diameter: 25px;
}

.circle--medium {
	--circle-x: 10px;
	--circle-y: 10px;
	--circle-diameter: 50px;
}

.circle--large {
	--circle-x: 15px;
	--circle-y: 15px;
	--circle-diameter: 75px;
}
```

But since there can be many positions and sizes of circles a finite predetermined collection of modifier classes won't do. Instead I'd have to set the CSS variables from Elm.

My [`viewCircle`](https://github.com/dwayne/elm-7guis/blob/17897d548ac17b1077e79ecc5ac78b881879f8d4/src/Task/CircleDrawer.elm#L477-L490) becomes:

```elm
viewCircle : Circle -> H.Html msg
viewCircle { position, diameter } =
    H.div
        [ HA.class "circle"
        , HA.customProperties
            [ ( "circle-x", String.fromInt position.x ++ "px" )
            , ( "circle-y", String.fromInt position.y ++ "px" )
            , ( "circle-diameter", Diameter.toString diameter ++ "px" )
            ]
        ]
        []
```

[`customProperties`](https://github.com/dwayne/elm-7guis/blob/17897d548ac17b1077e79ecc5ac78b881879f8d4/src/Task/CircleDrawer/Lib/Html/Attributes.elm#L7-L11) is a helper function that uses one style attribute to set custom properties. It makes use of the [inline property pattern](https://css-tricks.com/patterns-for-practical-css-custom-properties-use/#aa-pattern-6-inline-properties).

```elm
customProperties : List ( String, String ) -> H.Attribute msg
customProperties =
    List.map (\( name, value ) -> "--" ++ name ++ ": " ++ value)
        >> String.join "; "
        >> HA.attribute "style"
```

With this approach you get to cleanly expose the configurable parts of the `.circle` class.
## Conclusion

In this article I showed you how to change CSS properties from Elm with CSS custom properties. In particular, I used the example of drawing circles on a canvas to illustrate how it could be done. We needed to change the CSS properties `left`, `top`, and `inline-size` in order to change the positions and sizes of our circles. To do so we used the CSS custom properties `--circle-x`, `--circle-y`, and `--circle-diameter` to expose select "knobs of control" in an otherwise black box of immutable circle styles. In Elm, we then used the inline property pattern to "turn the knobs" and customize the circles. This helped us achieve a better separation of concerns between our business logic and design.

To me that's a huge win for CSS custom properties. What do you think?
## Further Reading

Here are some rough notes I collected on using [custom properties in Elm](https://gist.github.com/dwayne/1455037aefe52c1786188fd35f70eaee#file-custom-properties-md).

And here are some quality resources on effectively making use of custom properties:

- [A Complete Guide to Custom Properties](https://css-tricks.com/a-complete-guide-to-custom-properties/)
- [Patterns for Practical CSS Custom Properties Use](https://css-tricks.com/patterns-for-practical-css-custom-properties-use/)
- [Breaking CSS Custom Properties out of :root Might Be a Good Idea](https://css-tricks.com/breaking-css-custom-properties-out-of-root-might-be-a-good-idea/)
- [Performance of CSS Variables](https://lisilinhart.info/posts/css-variables-performance/)