I've built Turbo CSS because I have some requirements that were not met by any of the existing CSS frameworks / languages out there. Tailwind came closest, hence I'm comparing them here. Note that I did not want to write it. I actively fought it. I explained my ideas in this blog post, published it in a few places including on the Tailwind subreddit and even direct messaged the Tailwind authors. I wanted to give them my ideas for free. I messaged them on two occasions, never got a reply. In the end, I gave in to only write a throwaway prototype - which turned out to be too good so I kept going. Now Turbo is tested, documented, used in production.
Turbo takes inspiration from utility first CSS, functional programming, Tailwind, my experience and my requirements building a website builder / web application platform.
I do respect to the Tailwind authors and hope Adam and Steve are not going to hate me for this. That said, as explained, I believe I have tried my best to try to collaborate with them, or simply give them my ideas. Except they never responded.
Tailwind has a default spacing scale that you can customize to your needs if you want. Sounds good in theory but I found it extremely limiting in practice.
Let's see a couple of examples on how to use certain sizes.
Size | Tailwind | Turbo |
---|---|---|
0px | 0 | 0 |
1px | px | 1 |
2px | 0.5 | 2 |
3px | not available by default | 3 |
4px | 1 | 4 |
5px | not available by default | 5 |
6px | 1.5 | 6 |
7px | not available by default | 7 |
8px | 2 | 8 |
9px | not available by default | 9 |
10px | 2.5 | 10 |
11px | not available by default | 11 |
12px | 3 | 12 |
13px | not available by default | 13 |
14px | 3.5 | 14 |
15px | not available by default | 15 |
16px | 4 | 16 |
17px | not available by default | 17 |
18px | not available by default | 18 |
19px | not available by default | 19 |
20px | 5 | 20 |
What if you need special units in a certain situation?
Size | Tailwind | Turbo |
---|---|---|
37% | not available by default | 37% |
37em | not available by default | 37em |
13rem | not available by default | 13rem |
17vh | not available by default | 17vh |
91vw | not available by default | 91vw |
You can certainly configure all these above. Not in advance because you could not compile Tailwind in finite time and space - but you could do it on demand. The question is, do you want to? Do you want to recompile Tailwind, Excel and Photoshop every time you use a number you have not used before?
As Turbo is a compiler, it will parse whatever number you entered and just use it. You can even use fractions like 16.6667%
if you want.
Additionally, to type less in the common scenario, most utilities have a default unit. Thus, for margins, you can use both m-1px
and the unitless m-1
.
Tailwind has the philosophy that constrains are good. Thus, to achieve a universal design, they introduce a very limited scale for various utilities. Typically, they have the names xs
, sm
, md
, lg
, xl
.
I'm sure it started with good reasons but it quickly grew out of hands. Now there is 2xl
, 3xl
, 4xl
, 5xl
, 6xl
, 7xl
, 8xl
and even 9xl
in the default config.
So, in their default spacing system, 1
means 4px
(technically it is defined as 0.25rem
), now here we face a new unit called xl
! Let's reverse engineer what it means in translated to px
unit. Let's extract some example values.
Utility | Tailwind unit | CSS value |
---|---|---|
screen size | xl | 1280px |
screen size | 2xl | 1536px |
border-radius | xl | 12px |
border-radius | 2xl | 16px |
font-size | xl | 20px |
font-size | 2xl | 24px |
max-width | xl | 576px |
max-width | 2xl | 672px |
It's clearly pointless to try to understand the meaning of xl
, it's just a dummy variable name. I think it would make sense for button sizes and other complex elements where you only need a few different sizes, but for base units like font-size and max-width, I really don't see the benefit.
In particular, why is 7xl
easier to remember than 112px
? The alias 7xl
only adds extra mental complexity. Now every time I see it, I have to translate it to 112px
and vica versa.
Additionally, if you are working on multiple projects that defines different sizes, you will really start to feel the pain. In that case, I certainly wouldn't want to be you.
Turbo CSS uses the perfect scaling system by not inventing a new one. Just use whatever size you want. If you want to have a finite style guide for a cohesive design, just create a document and write them down. I mean, come on, designers manage to do the same. You don't need to recompile Photoshop to use 37px
, then why do programmers have to? Just because we can?
If you really insist a finite scale is better, then why not define a finite scale and throw an error for anything that's not on the scale? There is really no need for meaningless variable names like xl
.
Let's see some Tailwind CSS vs Turbo CSS examples.
Utility | Tailwind CSS | Turbo CSS |
---|---|---|
screen size 1280px | xl: | w12: |
screen size 1536px | 2xl: | w15: |
border-radius 12px | rounded-xl | rounded-12 |
border-radius 16px | rounded-2xl | rounded-16 |
font-size 20px | text-xl | font-20 |
font-size 24px | text-2xl | font-24 |
max-width 576px | max-w-xl | max-w-576 |
max-width 672px | max-w-2xl | max-w-672 |
Sidenote one: the breakpoints have shortened values in Turbo CSS too. w12:
stands for viewport min-width of 1280px
. It's good enough, as you typically don't need a granularity finer than that, so we just chop off the last 2 digits from 1280
to arrive at the number 12
in w12:
. That's better at future-proofing the system, so we won't end up with screen sizes like xl
. Turbo CSS aims to minimize your mental load.
Sidenote two: in Tailwind, you can't really set font-size on its own. It automatically sets the line-height as well.
Turbo CSS has zero config options that would affect its API, by design. This means that you can copy-paste code snippets from one project to another and it will just work.
Don't worry, this doesn't mean that you can't configure your colors and fonts! You certainly can! For example, there is a default color named blue with a default value. You can customize what exact color blue shall mean on your project, but you can't remove the existence of blue. That way, templates written against the Turbo CSS API will be completely portable.
Copy-paste is our old friend! :)
To elaborate, if you copy-paste a code snippet from a template into your own project, it will automatically pick up your color and font configurations to seamlessly fit into the design of your site.
You not only need to install Tailwind CSS which requires NodeJS but to set up some complex CSS purging mechanism. That's a step that's parsing your entire codebase looking for the occurrence of any Tailwind utility class names. Those that are found are kept, the rest are not included in your production build.
That's actually quite tricky to get right. What if you are storing dynamic content in a database? What if you are dynamically gluing together utility class names?
You will have to hack your way through these issues. Except you will not know about them. If a class name is not defined in production, it will just look ugly on unreadable, you will not know about it.
As Turbo CSS is a compiled language, we can combine selectors in any way.
Set width 4px conditionally:
When parent is hovered: hover:/w-4
When grand-grand-grand-parent is hovered: hover:////w-4
When parent's previous sibling is hovered: hover:~/w-4
Think these never come up? Wrong. For example, to apply custom designs to a file input field that is also correctly accessible via keyboard and mouse, you will need to apply styles like focus:~/outline-2-blue
and hover:~/outline-2-blue
. I don't know of any CSS framework that handles these - except for the official Boomla uikit built on Turbo CSS.
Turbo CSS also supports ad-hoc uses of colors like:
color-hex-AB1234
color-rgb-12-34-56
color-rgb-12-34-56-78 (last segment is alpha)
color-hsl-90-50-100
color-hsl-90-50-100-66 (last segment is alpha)
These are just impossible in Tailwind due to the CSS file size issues.
Turbo CSS takes an API first approach: it is primarily a suite of test cases. The Turbo CSS compiler can be implemented in any programming language to minimize the integration complexity. It is already implemented in two programming languages, Go and Javascript.
Tailwind is NodeJS based. If you don't already have NodeJS integrated into your workflow, Tailwind will add significant complexity.
Because Turbo is a language, it can support shorthand functions (and it does). For example, you can write b-1-red
. The equivalent Tailwind CSS expression is border-1 border-red
.
Tailwind CSS takes the approach of introducing sane defaults. The problem is, some utility classes have unexpected side-effects. The official stance is that this is a trade-off.
I disagree, that's accidental complexity. The Turbo solution is to use shorthand utilities instead. No side-effects.
Turbo utility class names are closer to CSS, thus it's easier to learn and work with them. For example, in Turbo, the line-height can be set with the line
utility, while in Tailwind it can be set via the leading
utility.
I have a really hard time figuring out what things like leading-tight
and tracking-wider
mean.
Motto: don't make me think.
Turbo provides the easiest onboarding experience ever: just drop a JS file into your site and it does everything. It will compile all the CSS before the first paint event so that users will not see any flickers. It will also watch the DOM for changes (via MutationObserver) and add any CSS definitions required.
This means you can even use it on a static website, without any server-side support.
And, it's only 18KB minified gzipped. Tiny!
Lots of Tailwind uses CSS variables, which has almost good browser support.
That's not bad, but why use it when you don't have to? That again is accidental complexity. As
Turbo is a compiler, thus it can take a way cleaner approach. Pretty much like if you would hand-craft your CSS. No CSS variables needed.
Tailwind first generates all the possible classes based on your configs and then works hard on trimming it down. This is called the purge step: getting rid of the unused classes. Unfortunately, it's not trivial to figure out what is used and what is not. What if it comes from the DB? What if it is dynamically genrated.
Turbo only generates the required classes, period. If you have dynamic content, you can generate the relevant CSS dynamically, when serving the request. Everything else is just a hack and will bite you, sooner or later.
Because Turbo is a type-safe compiled language, it will tell you if you make a typo, so you can fix it.
Not Tailwind. If you make a typo in the class names you use, bad luck. Let's hope it won't happen to the pricing page.
In Tailwind, you can define custom classes via an @apply
keyword. They are not first-class citizens, as in, you can't use all the language features with them.
In Turbo, you define custom classes similar to CSS class definitions. Crucially though, every language feature is always available with all your user-defined classes. You can also use any language feature within your class definitions. It's true composability.
In Tailwind, you constantly battle your config file to access the features you need.
Turbo doesn't have a config file, as in, not for customizing the API and what language features shall be available. The only config options are for customizing your fonts and colors. This doesn't affect the API though.
Tailwind doesn't allow you to style pseudo-elements, it would make its CSS file size sky-rocket.
Turbo doesn't have this issue. As it is a language, it can provide a few selectors like before:
, after:
, selection:
, placeholder:
, etc. These compose well with all the other language features, making all utilities available for them.
If you like Turbo CSS, please consider spreading the word!
Cheers,