TailwindCSS vs. UnoCSS

mapleleaf

MapleLeaf

Posted on May 1, 2023

TailwindCSS vs. UnoCSS

I’m a long time TailwindCSS user and a huge fan of utility CSS in general. I’ve rarely felt a strong need to switch, but UnoCSS has been on my mind for a bit. I got around to using it properly, so I thought it’d be fun to write up my thoughts on both in detail.

This article includes a lot of small nitpicky stuff that may not matter to others, but for me, the more I can reduce microfriction, the better.

Features

Tailwind has class names for pretty much every CSS feature you could think of, including some useful ones you may not know about, like isolation. Even for what’s missing, with arbitrary values, variants, and properties, most apps can be styled head-to-toe without a custom CSS file or plugins.

Uno supports all of Tailwind, plus some extras of the box that I really appreciate, like variant groups, fluid columns with CSS grid, and a lot more animations.

Uno also has an opt-in "attributify" transform, but I personally prefer my class names to stay in a single attribute separate from props. It's a neat idea though.

The Language

Tailwind has a reasonably well-defined language for class names:

There's no spec per se, but I've found it's a very "guessable" system. Based on how other class names work, you try stuff like grid-cols-[4rem,1fr,auto] and it Just Works™️.

By contrast, Uno's default preset is regex all the way down. There’s no real “language” per-se, and there’s no standardization, e.g. m4 and m-4 both do the same thing.

This is intentional on Uno's part; Uno is all about flexibility. But I still prefer Tailwind's guardrails and opinionated methodology.

This point is somewhat moot since one would really just use Tailwind's language with Uno. That, and Uno probably works better as a framework to implement your own language on top of it. Regardless, this is my evaluation if I'm looking at it and its default preset as a userland tool for styling apps.

Documentation

Both docs sites are beautiful, well-written, and highly usable. But I want to give a special shoutout to Uno's interactive docs, and the accent color shifting is brilliant. Might steal that 🤭

Custom Styles

Here’s an example of a custom plugin in Tailwind:

// adds s-* utilities to apply both width and height
plugin(function size(api) {
  api.matchUtilities(
    { s: (value) => ({ width: value, height: value }) },
    { values: api.theme("width") },
  )
}),
Enter fullscreen mode Exit fullscreen mode

Here’s (almost) the same in Uno:

// `s-*` classes to set width and height
[
  /^s-(\d+)$/,
  ([, size]) => ({
    width: `${Number(size) / 4}rem`,
    height: `${Number(size) / 4}rem`,
  }),
  { autocomplete: "s-<num>" },
],
Enter fullscreen mode Exit fullscreen mode

For both of these, the class s-4 will give the styles width: 1rem; height: 1rem.

Tailwind’s plugin API has gotten a lot nicer over the years. Simple utilities are easy to add, and even more complex ones like in this example aren’t that bad either.

Uno makes heavy use of regex for dynamic utilities, which feels error-prone. Uno encourages defining utilities that can take whatever value you give it, which reduces the need for the [] arbitrary value syntax. But I still prefer how Tailwind limits you to a specific set of values.

Aside: Tailwind’s arbitrary value syntax also lets you use any value. But the docs, plus the syntax required to use it, discourages them from being used. I like the clear, enforced sentinel of “this does not exist in the design system”.
By contrast, Uno more or less encourages users to use whatever value they want. Although you technically could build a more constrained design system within Uno, that's more leaps than what you get with Tailwind.

Editor Support

Tailwind’s editor support works pretty well, but has some gaps:

  • No awareness of custom classes in CSS files
  • Does not autocomplete class names when using @apply in plugins
  • You need to configure an “experimental” option with custom regex to get completion contexts elsewhere, and you need to dig through issues and discussions to find the recipe you want. Which still may not work in all cases, because... y'know, Regex 🫠

These issues make it tedious to reuse styles, via @apply or with class name strings in JS.

Tailwind authors recommend against using @apply altogether, but I still find it useful for small atomic elements, like buttons, inputs, and links.

Uno highlights class names and gives color hints everywhere, which is nice if you’re sharing class names in standalone strings, but it is funny to see it highlight “transition” in const transition = useTransition().

By extension, Uno's editor support also works in uno.config.ts, which is very nice for adding custom reused class names.

Caveat: you have to add // @unocss-include at the top of the config file to get it to complete class names in it. This works fine with Remix and PostCSS, but it may break in other setups. TBH the plugin should support this OOTB

However, Uno’s autocomplete is finicky in some ways:

  • Often you don’t get options for autocomplete until you type a full utility and -
  • Shortcuts don’t always get autocompleted, and it’s not really clear why
  • Autocomplete inside a variant group e.g. hover:(|) doesn’t work unless you put a space in it, like hover:(| )

That aside, I've found Uno's overall editor experience more frictionless than with tailwind.


Tailwind and Uno have their strengths and weaknesses. I highly appreciate Tailwind’s constraints and clearer authoring language, but if you value flexibility and extra features, you’ll probably like Uno. Uno also has an overall nicer editor experience as of writing, but maybe that’ll change! I’ll be watching both of them closely. 👀

Honorable mentions:

  • MasterCSS: Didn't really draw me in. Doesn't seem to have a big focus on constraints at all, and its language doesn't feel very ergonomic
  • Twind: The editor plugin still isn't updated to work with the latest version
  • TypeWind (and similar): I greatly appreciate the effort to have good DX without an extra editor plugin, but TS-based class names are very unergonomic 😅
💖 💪 🙅 🚩
mapleleaf
MapleLeaf

Posted on May 1, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

TailwindCSS vs. UnoCSS
tailwindcss TailwindCSS vs. UnoCSS

May 1, 2023