# elm-ui: a layout language for Elm that replaces HTML and CSS

> elm-ui is an Elm package that draws its API from layout and typography rather than from CSS, so a broken layout is a type error, and every style lives in the view function instead of a stylesheet. It is a whole-page alternative, not a component library.

**mdgriffith/elm-ui** — What if you never had to write CSS again?

- Repository: https://github.com/mdgriffith/elm-ui
- Website: https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/
- Stars: 1,385 · Forks: 108
- Language: Elm
- License: BSD-3-Clause
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/mdgriffith-elm-ui

## A design toolkit, not a CSS library

elm-ui is an Elm package for layout and interface, and the tagline in the project description asks what if you never had to write CSS again.

The stated argument is that CSS and HTML are quite difficult to use when you are trying to do the layout and styling of a web page, and that this library is a complete alternative to both. You write your app using the library and mostly never think about HTML and CSS again.

The framing of the goal matters, because it explains what kind of thing this is. The high level goal is to be a design toolkit that draws inspiration from the domains of design, layout and typography, as opposed to drawing from the ideas as implemented in CSS and HTML. That is a deliberate refusal to inherit the CSS conceptual model, and it is why the API does not look like a CSS subset.

The consequences the project lists are four. Writing and designing your layout and view should be as simple and as fun as possible. Many layout errors, the kind you would run into using CSS, are just not possible to write in the first place. Everything should run fast. And layout and style are explicit and easy to modify, because CSS and HTML are hard to modify as layout tools since there is no central place representing your layout, so you are generally forced to bounce between multiple definitions in multiple files to adjust layout, even though that is probably the most common thing you do.

That last point is the one that explains the design. The problem being solved is not that CSS is ugly, it is that layout decisions are spread across a stylesheet, a template and a component tree, with no single value you can point at and change.

## The API, in the shape of a design rather than a stylesheet

The live example on Ellie is a good way to read the API, because it shows the whole shape in one file.

```elm
module Main exposing (..)

import Element exposing (Element, el, text, row, alignRight, fill, width, rgb255, spacing, centerY, padding)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font


main = 
    Element.layout []
        myRowOfStuff

myRowOfStuff : Element msg
myRowOfStuff =
    row [ width fill, centerY, spacing 30 ]
        [ myElement
        , myElement
        , el [ alignRight ] myElement
        ]


myElement : Element msg
myElement =
    el
        [ Background.color (rgb255 240 0 245)
        , Font.color (rgb255 255 255 255)
        , Border.rounded 3
        , padding 30
        ]
        (text "stylish!")
```

The shape is a functional one. There is an el function that takes a list of attributes and a child, a row function for a horizontal container, and text for a string. The attributes are the interesting part, because they are grouped into modules by what they affect. Background.color, Font.color and Border.rounded come from separate modules, and each attribute applies to one concern.

That is the difference from CSS in a single line. In CSS, background-color, color, border-radius and padding are all properties on one element, with the same declaration syntax and no type distinction. Here they are distinct values with their own types, and a mistake such as passing a colour where a width belongs is a compile error.

The container vocabulary is also explicit about direction and alignment. row is a horizontal container, width fill says take the available width, centerY and alignRight place children, and spacing 30 sets the gap. These are layout concepts rather than style properties, and the naming follows the design vocabulary rather than the CSS one.

The top-level call is Element.layout, which takes the page-level attributes and the tree below it. That is the whole application shape: one layout call, a tree of el and row and column, and no separate template file.

## How the tests are actually run

The repository carries three kinds of test and benchmark, and the package.json scripts show how each is invoked.

The unit tests run through elm-test, with the script named test. The Elm test runner is at 0.19.1-revision2 and the compiler at 0.19.1-3, and node-elm-compiler is a separate dependency, so the Elm compiler is driven from Node rather than assumed to be on the path.

Then there are the render tests, which are the interesting part. The view-test script runs node tests-rendering/automation/view.js, and test-render runs the same automation with a --chrome flag. The presence of selenium-webdriver at 4.0.0-alpha.7 tells you the mechanism: the render tests drive a real browser and check what the layout actually produced, rather than asserting on the element tree.

There is also a Sauce Labs variant, test-render-sauce and test-render-sauce-automated, which source a sauce.env file and run the automation with --sauce --headless. That is how the project tests rendering across browsers, and it explains why the tests-rendering directory exists separately from tests, since one is unit tests in Elm and the other is browser automation.

The documentation preview has its own script, test-doc-preview, which runs a shell script in tests-rendering/automation, so the rendered examples in the documentation are verified too.

The benchmarks run through node benchmarks/runtime/index.js, with a benchmarks directory at the top level, and there is a separate list-issues script that calls the GitHub hub command to dump issues into a CSV, which is a small maintenance convenience for the maintainer rather than part of the build.

The contributing guidance points at notes/RUNNING_TESTS.md for the details of how to run them, and the project asks contributors to add issues for the bugs they find.

## The CSS mapping, the history, and the surrounding project

A CSS-LOOKUP.md file at the repository root is the file to read first if you are coming from CSS, because it maps the CSS concepts onto the library's own vocabulary, and the whole premise of the library is that those concepts do not map one to one.

The lineage is documented in the History section. The work is based on a rewrite of the style-elements library, and a lot of that work was originally released under the Stylish Elephants project. The same author wrote all three, so the progression is from Stylish Elephants through style-elements to elm-ui, and each step reframed the problem rather than adding features to it.

The community around the library maintains a collection of examples called the elm-ui-cookbook, and the project describes it as a great resource if you are just starting out or get stuck on specific things. That is the pattern to expect here, and it is the pattern of any layout system: the API is small, and the difficulty is in composing it well for a particular screen.

The examples directory in the repository holds three programs, Basic.elm, Form.elm and Table.elm, with its own elm.json, so it is a self-contained Elm project rather than files dropped into the main package. A table example and a form example are the two layouts that are hardest to do well in any system, so they are a reasonable test of what the library can express.

The package is published on the Elm package site at mdgriffith/elm-ui, the licence is BSD-3-Clause with a LICENSE file at the root, and there is no published GitHub release. The last push was on 2026-09-21.

There is one operational note that is easy to miss and hard to diagnose. Make sure to have a DOCTYPE html on top of your application, otherwise things might get messed up in some browsers. That is the kind of detail that costs an afternoon if you hit it without knowing.

## What this approach costs

The first limitation is that it is a complete alternative, not an addition. The project says mostly never have to think about HTML and CSS again, and the price of that is that anything CSS can do that elm-ui has no concept of is simply unavailable. A third-party CSS framework cannot be dropped in, an existing HTML page cannot adopt it for one component, and a design that relies on a CSS feature the library has not modelled has no path.

The second limitation is that it is Elm only. The library is an Elm package with an elm.json, and the language's type system is what makes the compile-time guarantees work, so there is no JavaScript or TypeScript version of it. A project that has decided against Elm cannot use it at all.

The third limitation is the model of the whole page rather than a component library. Element.layout produces the page, and the tree below it is the interface. There is no partial adoption, and for a team with an existing application the migration is all or nothing, which is a very large project for a layout library.

The fourth is the surrounding practice. The project maintains a Slack channel on Elm Slack, a community cookbook, and a talk, Building a Better Design Toolkit, and the repository holds an experiments directory, a benchmarks directory and a notes directory with the test instructions. All of that is a healthy amount of material for a library, and all of it is a signal that learning the library is a real activity rather than something you pick up from the type signatures.

On maintenance, the repository is not archived and the last push was on 2026-09-21, and the package is published on the Elm package site rather than released with tags, so the version you get is whatever the package site currently serves.

## Conclusion

elm-ui suits an Elm team that wants a single place where layout and style live and that values compile-time guarantees over the flexibility of CSS, and it suits a designer and developer working together on a page whose layout they can reason about. It is a poor fit if you need arbitrary CSS, third-party CSS frameworks, or a component library you can drop into an existing HTML page, since this is a complete alternative to HTML and CSS rather than an addition to them. Before adopting, read CSS-LOOKUP.md, which is the project mapping CSS concepts onto its own vocabulary, and check the community cookbook for how other people solved the layout problems you are likely to hit.

## FAQ

### What is elm-ui and what does it replace?

elm-ui is an Elm package for layout and interface that is a complete alternative to HTML and CSS. The high level goal is to be a design toolkit that draws inspiration from the domains of design, layout and typography rather than from the ideas as implemented in CSS and HTML, so you write your app with the library and mostly never touch HTML or CSS.

### How is the elm-ui API different from CSS?

Attributes are grouped by concern into separate modules such as Element.Background, Element.Border and Element.Font, and each attribute has its own type, so passing a colour where a width belongs is a compile error. Layout is expressed with functions like row and column and attributes like fill, centerY, alignRight and spacing, and the whole page comes from one Element.layout call.

### How do I run the elm-ui tests?

The unit tests run with npm test, which calls elm-test. The render tests drive a real browser through selenium-webdriver with the view-test and test-render scripts, the latter adding a --chrome flag, and there are Sauce Labs variants for cross-browser runs. The benchmarks run through node benchmarks/runtime/index.js.

### What should I know before using elm-ui with a browser?

Make sure to have a DOCTYPE html on top of your application, otherwise things might get messed up in some browsers. The project also suggests reading CSS-LOOKUP.md for how CSS concepts map onto the library, and the elm-ui-cookbook for worked examples.

## Sources

- [Issues](https://github.com/mdgriffith/elm-ui/issues)
- [License: BSD-3-Clause](https://github.com/mdgriffith/elm-ui/blob/master/LICENSE)
- [mdgriffith/elm-ui on GitHub](https://github.com/mdgriffith/elm-ui)
- [Project website](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/)
- [README](https://github.com/mdgriffith/elm-ui/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mdgriffith-elm-ui
