Category: Tailwind

I recently participated in the Handmade Network Visibility Jam and built a tool that I’d been wanting for my own use:

In a nutshell, Escape Artist shows ANSI escape sequences that are normally invisible. It’s kinda like View Source for the terminal; my hope is that it will be useful for anyone working on shells and terminal applications.

Tech Stack

Escape Artist is primarily written in Rust, using Axum to serve up a web UI using Tailwind+Preact. Events get streamed to the front-end over a websocket. I took great pains to keep the front-end simple; nearly all business logic lives in the Rust back-end, there is no front-end build step, and all dependencies are bundled with the application.

Escape Artist compiles down to a single-file 1.8MB executable, and nearly half of that is Tailwind and an embedded font.

Lessons Learned

Parsing escape sequences out of a stream of bytes is remarkably tricky. Thankfully the vte crate does most of the heavy lifting for us.

This minimal approach to web UI was generally pretty nice! I occasionally ran into a bit of trouble wrangling JS libraries that assume everyone is using NPM, React, and a bundler like webpack, but overall it wasn’t too bad.

I ended up needing to roll my own tooltip solution using Floating UI, and… wow. Their introductory tutorial is one of the best I’ve ever seen for a library in any language.

Immediate Feedback in Programming

Bret Victor's Inventing on Principle

Bret Victor’s talk Inventing on Principle (video, transcript) changed the way I think about computing in 2019. Inventing on Principle is partly about Bret’s guiding principle:

Creators need an immediate connection to what they create. And what I mean by that is when you’re making something, if you make a change, or you make a decision, you need to see the effect of that immediately.”

The Edit-Compile-Run Cycle

Although Bret doesn’t use the term, programmers are deeply familiar with his principle. We’ve all worked with toolchains that introduce significant delay before you can “see” the results of a change, and we know they’re painful. Everyone wants a short edit-compile-run cycle.

But until IoP, I’d assumed that slow cycles wouldn’t materially change the output – you’d eventually get to the same place. This was wrong. I also didn’t appreciate the very small time scales involved; a 5 second delay used to seem trivial to me, but it’s still meaningfully different from a response time measured in milliseconds.

Through some very impressive custom tools, Bret shows how immediate feedback enables exploration, which then gives birth to ideas which would otherwise never see the light of day. This was an epiphany for me. Since IoP I’ve constantly been looking for better ways to code, and re-evaluating my existing processes for shorter feedback cycles. The results:


My typical Rust development workflow goes something like this:

  1. Write a small function that does roughly what I want
  2. Write a small unit test inline to exercise the function (even if it’s a private function)
  3. Iterate using cargo test until the function is correct
  4. Later, “productionize” the tests if necessary

Rust’s native support for inline unit tests helps a lot here, and the excellent type system catches a lot of issues before I even run cargo test. On the other hand, Rust’s compiler is notoriously slow and that extends to IDE tooling that depends on the Rust Language Server. I’m looking forward to Cranelift for faster debug builds.

Future Imperfect 2.0

A near-complete website rewrite

I spent a few weeks in August rewriting this website, and as promised here are the deets. The source code is available here under the MIT license.


When I initially put this website together in January 2018, I used Julio Pescador’s Hugo port of the Future Imperfect theme. I’d heard good things about Hugo, I wanted to write blog posts in Markdown, and it was a good-looking theme that required minimal additional setup. It served me well for over a year, but a few things kept bothering me:

  1. The styling was very difficult to modify – it was nearly 3000 lines of CSS in a single file, with many duplicated colours and styles.
  2. The theme relied on a lot of JavaScript libraries: jQuery, highlight.js, Fancybox, Skel
    1. My simple static website was serving up hundreds of kilobytes of largely unnecessary scripts. As someone who grew up using a dialup modem, this offended me.

My goal was to rewrite the Hugo theme for extensibility and performance, and I figured it would take maybe a week. Of course, it took about 3 times that.

Dark Mode on the Web

Using the prefers-color-scheme media selector

I recently rewrote most of this website (more on that soon!) and it’s now much easier to work on. Last week, I shipped a feature that I really like: dark mode using the new prefers-color-scheme media query. If you have dark mode enabled in your OS and a reasonably new browser, now shows you a dark UI using the Solarized colour scheme:

Tailwind CSS

Web styles designed from scratch

I recently overhauled the UI for my letter builder web app, switching from Bootstrap to a neat framework named Tailwind CSS. It’s been great so far.

I dabble in web development, but it’s not “my thing”. Most of my time is spent on back-end systems and the occasional native UI. When I’m building a web UI, I usually spend a lot of time on MDN or W3Schools looking up syntax details.


Enter Tailwind. It’s a utility-first CSS framework, which in practice means they give you a whole bunch of “utility” classes which are effectively aliases for common styles. Here’s an example from when I was styling a button:

<button class="bg-blue-dark text-sm text-white rounded py-2 px-4 my-2"/>

With these classes, I’m saying that I want a dark blue background, small white text, 2 units of padding on the top and bottom (py-2), 4 units of padding on the left and right (px-4), 2 units of margin on the top and bottom (my-2), and rounded corners. Whew!

Right away, we can notice a few advantages relative to the normal way of doing things:

  1. This is fast to iterate on. I can add another utility class in-line without digging through my CSS file and reasoning about selectors.
  2. The class names are concise yet informative. py-2 is clearly operating on the Y axis (top and bottom), which is much easier to remember than a padding style with multiple unlabeled values.
  3. I have fewer choices to make. I didn’t need to worry about whether to use #0033CC or #0000FF for a dark blue, I just asked for dark blue. Same thing with padding+margins, I just chose from a small set of integers instead of wondering whether to do 0.5rem or 0.6rem.

Isn’t putting all your styles inline just asking for trouble?

Yes, this can get out of hand if you put too much stying inline – but Tailwind utility classes can be extracted into components as soon as you need some additional abstraction. The authors even recommend that:

Tailwind encourages a “utility-first” workflow, where new designs are initially implemented using only utility classes to avoid premature abstraction.

While we strongly believe you can get a lot further with just utilities than you might initially expect, we don’t believe that a dogmatic utility-only approach is the best way to write CSS.

Curation VS organic growth

I’m being a little hard on the “usual” way of writing styles in CSS. The web grew more-or-less organically, so it’s not fair to expect an overall design from the mess of styles available to modern browsers. Thankfully, that’s where projects like Tailwind come in.

It’s remarkable how much nicer it is to build web UIs when working with a thoughtfully curated and documented subset of styles. Sometimes a big step forward doesn’t need to come from a clever technical breakthrough – simply organizing existing information+symbols in a better way can reap massive benefits.


Cities & Code

Top Categories

View all categories