I spent most of my winter break in my wood shop, working on some tech adjacent projects that I will post about later. However, I also spent a fair amount of time at my keyboard, working on a handful of gleam projects I wanted to introduce here.

I’m theoretically working on a small full stack application, but as so often happens with me, I was sidetracked by developing open source libraries that I have no hope of keeping maintained. So these projects are all in support of full stack web development on lustre and wisp.

Why Gleam?

I’ve written about Gleam before, though the tutorials are super out of date since the language and underlying libraries had not hit 1.0 yet. As it has stabilized, Gleam has become my all-time favourite language. I’m thrilled to see it increasing in popularity, and I hope it is a path forward from the default state of poorly coded NextJS cruft the vibe coding ecosystem has devolved into.

I love Gleam because it maps directly to dev velocity at scale. The ability to quickly refactor or edit a large codebase at scale is critical in today’s ecosystems, and most popular languages are extremely bad at this, especially those designed to run in the browser. For me, Gleam makes coding fun again, in a way I haven’t experienced in over a decade.

Some might say this isn’t important in the new world of AI coding but I disagree. Almost every commit in the projects I worked on below was hand coded because these were hobby projects and I prefer hand coding to AI coding, especially in this language. However, I was not above asking AI for a quick refactor, and I will say that Gleam is a much better language for AI coding because of the same dev velocity at scale issues. It doesn’t matter whether AI or humans are trying to work at scale; having software that compiles fast, is easy for agents and humans to read about, and provides clear error messages for rock solid compile-time guarantees is as important for AI coding as it is for humans.

Monks of Style

The Monks of Style project is my attempt to inject strong typing into writing CSS-in-Gleam. By default, Lustre treats styles as untyped key-value tuples of strings. The Sketch project augments this by creating typed keys with string values, but I wanted some structure on values.

This is pretty hard because CSS style values are often free-form or relatively so. I leave a raw string escape hatch and another for css variables to support that, but I wanted to have all the “enum-style” values auto-completable in my editor.

It looks something like this:

pub fn main() {
  let app =
    lustre.element(html.div(
      [
        attribute.styles([
          // Standard lustre tuple attributes when you need max control
          #("--override-color", "cyan"),
          // raw strings when you need custom values for typesafe keys
          background_color.raw("maroon"),
          // css design token variables on any prop
          color.var("override-color"),
          // typesafe key value pairs
          display.flex,
          flex_direction.column,
          margin.auto_,
          // typesafe lengths and sizes
          width.length(Percent(50.0)),
          padding.length(Rem(2.0)),
        ]),
      ],
      [
        html.text("hello world"),
      ],
    ))

  let assert Ok(_) = lustre.start(app, "#app", Nil)

  Nil
}

Open-Props-Gleam

The Gleam community is really bullish on Tailwind and… I’m not. I think Tailwind contained the seeds of a great idea, but the class-based utility classes are an abuse of everything that CSS stands for. They are impossible to maintain in the long run and the pidgen language Tailwind forces you to memorize is an abomination.

That is my opinion. I know it’s a minority opinion. My further opinion is that open-props is Tailwind done right. It leans into CSS standards instead of abusing them, and is a joy to work with.

So I wrote a project to use open-props styles from inside a Gleam project. It plays nicely with both Sketch and Monks of Style, as well as with raw Lustre attributes. Here’s an example with Monks of Style attributes:

  let app =
    lustre.element(
      html.div(
        [
          attribute.styles([
            background_color.raw(colors.pink_1),
            margin.raw(sizes.size_5),
            padding.raw(sizes.size_8),
          ]),
        ],
        [
          html.text("hello world"),
        ],
      ),
    )

Stytch Authentication

This one is incomplete and not available on Hex yet. I couldn’t find an identity and auth solution for Gleam so I decided to wrap one instead. This ended up being a rabbit hole fever dream that I still haven’t woken from. I’m not delighted with the code or the API so I haven’t published it yet.

The monorepo contains projects for calling stytch apis from the backend and for integrating a backed that wraps that client with Lustre. The backend part is fine, but the Lustre integration is a bit too tightly coupled and I don’t like it. Open to suggestions or pull requests!

This project involved a ton of rewriting, refactoring, and reworking, and Gleam made it so easy. Being able to change one thing, and then knowing the refactor is complete with no “did I miss anything” paranoia because the code compiles is truly a luxury you didn’t know you were missing.