DEV Community

Cover image for TailwindCSS vs. UnoCSS
MapleLeaf
MapleLeaf

Posted on

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 😅

Top comments (13)

Collapse
 
spock123 profile image
Lars Rye Jeppesen

Great stuff

Collapse
 
jacobmgevans profile image
Jacob Evans

Love the article Maple, great work!

Collapse
 
dreyfus92 profile image
Paul Valladares • Edited

I've been looking the key differences between UnoCSS and Tailwind for some time and reading this helped me get it. Thanks for sharing this article Maple.

Collapse
 
vtenoris profile image
Enes Çakır

Thanks for article

Collapse
 
msnisha profile image
Nish

I like the tailwind for the nice colour choices and fan of the Refactoring UI book. But not very comfortable with using the utility classes as it doesn't bring advantage when we have to change style if we change the utility class name in all html. I prefer component style classes as we could change the styles in single place instead of going and changing the utility class in all html to apply a different style. It is just my opinion.

Collapse
 
machy8 profile image
Vladimír Macháček • Edited

I just add a link here to Stylify CSS for those that may be looking for an alternative to both of these with smaller learning curve and CSS-like selectors 😅🤟

Collapse
 
dev_geos profile image
Dev Geos

In the same genre as the 2, there is modcss. unlike tailwind and unoCss, it only uses arbitrary values ​​on its properties which is very convenient.
it also has a fully parametric grid system with n columns.
Le site
Le github

Collapse
 
lyqht profile image
Estee Tey

Learnt new stuff in this article, thank you for writing this!

On a side note though, my company project has had no issues trying to autocomplete custom classes that we add into the tailwind config within Vue's HTML template though. Perhaps it's powered by VSCode TailwindCSS extension?

Collapse
 
ninjanordbo profile image
ninjanordbo

Good article 👍

Collapse
 
ashishk1331 profile image
Ashish Khare😎

So the main difference between Tailwind and Uno is extensibility. Hmm?
Could you shed some more light on internal working engines of both?

Collapse
 
obere4u profile image
Nwosa Tochukwu

Thanks for sharing, learned something new.. first time knowing about UnoCss

Collapse
 
javed profile image
Javed

Also from my side, thank you for that effort

Collapse
 
akashpattnaik profile image
Akash Pattnaik

Thanks a lot for opening us to UnoCSS. Will surely give this a try now over tailwindcss.