Author of Tailwind here! If you haven't worked with a library like this before, I promise your gut reaction will be "holy hell this is the worst thing I've ever seen" (it was my reaction too!)
You really do have to try it to shake that impression.
If you need a bit more convincing before you're willing to try it, I wrote an in-depth article a while ago that documents my journey from a "semantic classes"-loving HTML/CSS purist to a utility-loving heathen:
...and if you want "social proof", here's an interview I did with Diana Mounter who leads the design systems team at GitHub about how moving to a utility-based approach has made things infinitely more maintainable for them, and given their developers a lot more confidence:
> "holy hell this is the worst thing I've ever seen"
This is so true. All the classes in HTML source look so ugly and verbose when you first see it. At first I dismissed the framework and wondered how someone could think this was a good idea.
I kept seeing it pop up, and the comments were always positive, so I put in a little more effort into understanding how it works. Eventually I decided to try it, and within 5 minutes I was making stuff that actually looked nice. I used it to create a really minimal company website, that looks totally custom, and is really lightweight.
The problem I have is: what if I want to (for example) add a style or take it away from all my buttons? It's going to be a lot of manual fiddle, or is the idea that hand written sites are wrong to start with and I should be using generated code?
This is a totally real problem and one of the things that put me off of using some of the existing utility frameworks before creating Tailwind.
With Tailwind, we don't try to pretend that you will never need to write any CSS, and instead embrace that fact and give you as much tooling and guidance as possible on how to extend the framework the way it was intended to be extended.
In the situation you're talking about, you would extract a "component class" using Tailwind's `@apply` directive to enforce that the component followed your design system:
Then you could update all your buttons at once by making changes to that component class.
The key with Tailwind is that it encourages a "utility-FIRST" workflow, not a "utility-ONLY" workflow. Build your UI with small primitive utility classes, and extract components only when you start to experience painful duplication problems.
Then you could update all your buttons
at once by making changes to that component class
Oh my god, that's genius!
I'm looking forward using this right away.
First of all for the wonderful experience to add a class to my buttons. I think I will call the class "button". But I'm open for other suggestions at well.
Second for learning your DSL. Curious what other clever tricks you invented.
And most of all I am sure I will love implementing a compilation step into my workflow to turn your DSL into actual html and css. It always felt a bit too lean to just change a style and reload the page.
You can use a component. I personally design first using only tailwind utility classes. Once I reach a near-ready state I factor out the repeated code into custom components that will be easy to change in the future. That way I also don't have to wait for css to compile while I'm still making a lot of changes (I know, like 150ms right?).
The real value for me is just being able to sit down and start. The odious first impressions of tailwind are true. But try it out. I like it because I don't waste time flipping back and forth between my document and a style sheet. I don't waste time thinking of clever class names. I don't wait a second to compile css and refresh for every small change I make (even though developer tools have been helpful in this regard, you can only make so many changes at a time). I still break it down into components later but the actual design process is more fluid.
I use it in production at work. Wasn't my choice. I should have pushed harder for Bulma. Not only is the HTML just a hair length's away from `style` tags, but if I'm writing the same set of classes on more than 3 elements I compose a component.
Then I also have to write our own classes for modals, cards, toasts, lists, media objects, drop down menus, side menus, breadcrumbs, buttons, forms, their labels, images and their captions, and get the right mix of responsive utility classes as well so they all play nice together.
I'd rather just pick a framework that lets me customize the corner radii, padding, margins, and be done with it.
Buttons are sort of a pathological example. Most components in a web app are container-like things, with collections of a couple of buttons and other things that need to have a cohesive look, internal and external positioning, etc. And how often do you end up with one large, crufty CSS class for each of those, or a handful that are closely tied to each other and the markup anyway? Frameworks like this allow you to easily move that gunk from the CSS to the HTML.
This [1] is one of the best articles I've read describing the issues and thought processes around the complexities of structuring css. But the suggested conclusions are exactly where I start to have problems. Take this recommendation for example:
.align-left {
text-align: left;
}
This is a very reusable class, of course, because it is literally just css but with a different name. But that's what always ends up happening. The lowest common denominator for reusability in css is so low, that you often end up with ridiculously simple classes like this, or close, basically re-inventing css with a different syntax. The new challenge becomes learning this new language and how to compose it.
The core issue with CSS is that it has divergent architectural characteristics simultaneously. On the one hand there are usually a set of extremely re-usable primitives: btn, normal-text, overlay, card, etc. Then very quickly, we get higher-level components/styles that are extremely not re-usable: order-form, contact-page, etc. And other stuff in between. Applying the same structural models to all of it will cause sub-optimality at some level.
To address this, I've found that applying different models to different aspects of the css most beneficial. Basically having a mix of both utility css, and semantic css. Low-level, highly re-usable components are defined in a utility fashion so that font-sizes, colors and other things are consistent throughout the site. And complex, non-reusable high-level components that compose primitives, and other custom non-reusable css, to define the top-level components. When the need for sharing common css arises, those can be abstracted out into utility stuff, but part of the goal is to keep the utility part small so that people don't have to learn an entirely new language.
What if you need to change justification, image proportions, etc on mobile for instance?
Also, from a previous comment of mine:
“Problem is, defining quantity in the class name doesn’t work.
.m10 may be fine for desktop but not for mobile.
Then you’ll have media queries which define .m10 as margin: 5px or something, which is nuts.
You could go for unspecified .mSmall etc, but in real life, things tend to be more specific and messier, in my experience.”
Hint : you can also use 'calc' with vw and vh to get some more complex behaviours than purely proportional. You can add an offset, you can do some multiplications etc.
Don't go overboard, but a little offset can go a long way
Adam, your "Separation of Concerns" article was what sold me on utility-first CSS. Thank you.
For anyone building React apps with Tailwind or Tachyons, I just open-sourced a library that makes it easier to reuse CSS classes in standalone, customizable components:
I was curious what your thoughts are on a JS-based functional CSS library? I noticed that you mentioned emotion on the docs, and that was also what I thought would be the perfect tool for the job
Imagine a set of functions like `textColor()` or `margin()` that return composeable objects that you'd stick onto css props, and just like tailwinds would throw errors or lint warnings if you didn't pass in values it was configured to allow? (E.g., colors.red, okay, colors.maroon, error? Or maybe colors('red') okay)
I figured it'd more elegantly solve the problem of generating stylesheets that are too large and then needs to be pruned off
One trade-off I see is that the utilities would need to be imported to each file that needs it (as opposed to strings you can put anywhere), but the the good thing about that is you could more easily click into the definitions that way, and you could namespace utilities under a variable so you'd only need to import one thing
I would guess you'd put more thought on this topic than I have, very curious to hear what you think
> holy hell this is the worst thing I've ever seen
Not really :) It might have been my reaction some 10 years ago, but right now I think it's quite good. I've done BEM, the atomic crap, OOCSS and what have you but to be honest nothing beats composition when it comes to rapid development. I used Bootstrap in the past, it was heavily customised for the project, and I ended up making it look very similar to your approach. It makes sense when you have 20 Devs working on a website and you don't want to end up having separate stylesheet for every page. Because business hates consistent design and always wants this and and that to stand out just on this one page. It should be easy, right? I look at your framework as an embodiment of understanding business driven development in a big team.
Nice work Adam, I've grown to really like util based CSS, mainly from the new Bootstrap 4 utility classes. Since your article came out before BS4 release, wondering what your thoughts were on Bootstrap now?
I just wanted to tell you: THANK YOU! I love tailwind. It was a bit of a hustle to get started, but once you're there, there is no way back to bootstrap.
Tailwind is the best thing that has happened to CSS Frameworks in the last 10 years.
* Define the essence of your design in a JSON - typography, colors, spacings, shadows, borders et al.
* Anyone in the team including backend developers can create new interface components without waiting on a designer, thanks to well-defined scales that compose well.
* The component (HTML+CSS) is the unit of abstraction. eg: "ProfileCard". Inside ProfileCard you'll use Tailwind's utility classes to build your component. You reuse this component everywhere, and if you have to "change a button's padding across the product" (which to me is far too rare) you open your component files and change them there.
* It is so easy to build UIs - you don't have to name every single element in the DOM - these names are _only_ to act as "hooks" into the CSS, and serve no abstraction. With Utility classes you can just assemble them in the HTML without touching the CSS
* Tailwind has excellent documentation and great conventions - this means between projects all you need to know is their particular scale. Don't have to learn a new UI framework everytime.
* Consistency, consistency! Thanks to design scales.
* Works so well for custom UIs. Have your designer do absolutely original designs in Sketch, and first transcribe their scales into tailwind.config.js, and voila! start pushing out its HTML at breakneck speed.
I mean a lot of those things were possible with bootstrap-sass years ago.
> Define the essence of your design in a JSON - typography, colors, spacings, shadows, borders et al.
_variables.sass
> Anyone in the team including backend developers can create new interface components without waiting on a designer, thanks to well-defined scales that compose well.
This is true, but is solved by any sort of component system and not unique to tailwind.
> The component (HTML+CSS) is the unit of abstraction. eg: "ProfileCard". Inside ProfileCard you'll use Tailwind's utility classes to build your component. You reuse this component everywhere, and if you have to "change a button's padding across the product" (which to me is far too rare) you open your component files and change them there.
Why would you change a padding in multiple files when you can set `$base_padding: 10px`?
> It is so easy to build UIs - you don't have to name every single element in the DOM - these names are _only_ to act as "hooks" into the CSS, and serve no abstraction. With Utility classes you can just assemble them in the HTML without touching the CSS
Going by the first example in the linked article, something like "text-grey-dark" seems like an awful idea when what it really means is "text-quiet". Heck in their example "dark" is _lighter_ than the surrounding text.
_variables.sass works if you're using BEM. But now you have two component-like things: BEM classes, and the components themselves. BEM solves the issues inherent in nested CSS (cascade, inheritance, multiple selectors, specificity) by forcing us to never nest them, and instead having to name every single element in HTML. Though they look semantic, they are not truly reusable since they are coupled deeply with the markup. But with Tailwind, your component is your unit of abstraction and you don't have to name everything just for the sake of hooking up the HTML with CSS.
But you can even do BEM with Tailwind if you really want to with @apply.
> Why would you change a padding in multiple files when you can set `$base_padding: 10px`?
Neatly written CSS that uses variables for design scales are a good thing to have, but without any kind of tooling help, it is really difficult to keep it consistent. Tailwind on the other hand gives you the affordance for that - a "pit of success" because you never have to define magic values and instead use pre-defined functional CSS classes.
Regarding colors - you can use "text-quiet" or "text-title" etc. - the names are fully in your control and can be set in the configuration JSON file.
I personally have an issue with this "Utility/Functional" CSS pattern.
To me I still think that the good old "HTML describe how the content is organized, CSS describe how this content looks like" is the way to go. In the end it's how CSS and HTML were designed at first.
Isn't that basically the same thing as inline styles?
Not quite, for a few reasons:
Inline styles don't respect media queries, which basically rules out responsive design
Inline styles aren't limited to pre-defined options, meaning you can still end up with 90 different shades of blue)
Inline styles cause specificity issues, since they trump separate stylesheets.
Inline styles don't support print-specific styles.
Inline styles can't address pseudo-elements (such as ::before and ::after)
Inline styles can't apply to multiple elements. Utility classes can define .bg-blue once and have it apply to many things, which leads to shorter markup and quicker rendering speed.
Inline styles are a pain to type. Compare class="f-sm bg-blue" to style="font-size: 10px; background-color: #0000ff;".
Ok maybe I was too negative too quickly, sorry. After reading another article [1] linked in some other comment I can see the appeal, if used with some templating engine that allows reusing html (like Vue or React).
It is crazy how new technology brings up old patterns again. Similar to how it was frowned upon to have JS near your HTML ("must be separated!") and now with React they are intertwined to a maximum.
I oftentimes find css to be the most complex and hardest to change part of web applications and am thankful to any approach that explores alternatives!
There seems to be a misconception that you need to go all-in on utilities when using a framework like Tailwind.
If you have 500 buttons using the exact same set of classes, you should of had a button class in the first place. Almost all my projects have their own button and form input classes for this exact reason!
Where utility-based CSS frameworks really start to shine is the other 75% of your codebase, where you're inventing clever class names, only to end up using them once.
This is also why Tailwind calls itself a utility-FIRST framework. Build things with utilities, and extract components to classes when the need arises. Avoiding early abstractions provides a lot of value (in any programming language for that matter). Not having to invent a name for every single piece of UI also saves a ton of headaches down the road.
In my experience design revamps of this magnitude has always needed fresh markup, CSS, Javascript - aka an entirely new front-end. But in case it is just the color of all buttons, then you'd change them in the components. 700 components is quite a stretch - for example there are at most 50 components in Morning Star's design system - http://designsystem.morningstar.com/.
But let's say there are 700 components, what's the alternative if we were using BEM? You'd have to change all the 700 CSS classes, breaking things everywhere thanks to cascades, inheritance, multiple selectors, and specificity. With Functional CSS, all you have to do is go to the markup, and change "bg-white" to "bg-offgrey", and nothing breaks.
In my (limited) experience when I'm repeating the same set of classes over and over again I extract to a named component; You shouldn't have 700 identical buttons that all specify the classes that compose their look. 5-7 times = a use pattern and time to extract it.
In tailwind, you have a config file. You can just change the variable in the config file to the colour you need to change it to and it changes globally.
That’s incorrect. What your missing is that in the config file you define a new color; say ‘rose’ and then the framework auto provides bg-rose, text-rose, border-rose, cutting duplication and bringing consistency.
I think the big shift started to happen when people started to say, let’s treat CSS like code and what would we expect of good code, and then all of these CSS Sacred Cows were then seen in a new light. The biggest of them all for me was ‘naming things are hard’, traditional CSS implies naming your classes is easy, and for a once off example it is always easy, but that approach is a lifetime commmitmnet to generating names on names just to add styles and this adds a naming things complexity burden that until recently has just been a generally unacknowleged invisible cost.
In the post you mention extending the first class, author-bio, to the article class. When you introduced the idea of the media-card wouldn't it make sense to have both the author-bio and article classes extend from it, maintaining the semantic naming but also making it easy to override or change some style for one in particular?
Overrides are special cases. Usually you start with one, but before you know the CSS file has bloated to thousands of lines with hundreds of overrides depending on context. With enough of them you can't comprehend the CSS anymore, which means you can't change any existing classes, and so the CSS becomes write-only.
All CSS "frameworks" should be thought of as utility libraries, including Bootstrap. If you apply the framework classes directly to your HTML, you've tightly-coupled your HTML (which is already tightly-coupled to your information architecture) to an implementation detail of your presentation code.
Most frameworks support some kind of re-use of CSS declaration blocks - use that feature to build your components from the primitives they provide, but also add a layer of indirection by putting those components behind a semantic class name. Voila, loose-coupling with all the productivity benefits of the frameworks.
People please don't judge without giving it a try. Just signed up to write that Tailwind truly changed the way I work. Centralized configuration for styles, colors etc.
Tailwind is the only CSS framework I use since I discovered it and I'm gradually converting my/company projects to use it.
I tried the utility approach many years ago and while I can see the appeal in some situations I think it’s far from a panacea.
My journey led to me writing http://ECSS.io
The opposite way of tackling CSS at scale. Instead of abstraction I opted for isolation.
Recently I had to do some updates on a UI that used tailwind framework. It was the most unpleasant experience I have ever had. I quickly got eye strain looking at all those attributes.
Also http://basscss.com/ which convinced me of the genius of using this functional/utility approach to CSS.
The website themes just seemed so much simpler and minimalist, but also more consistent across the whole site. Significantly reducing the amount of time I need to create my own CSS classes.
It is convenient but you are not separating markup and presentation. I think the whole point of css is to swap stylesheets and get a different presentation, like demostrated by http://www.csszengarden.com/ BEM/OOCSS lets you do this.
How many times you do that in real project? Or have you ever done that in your career? And if a project needs substantial redesign it is usually acompanied by the functionality change, which means you need defferent html/components too, so you still can`t just swap css files.
+1 on this for "real projects" that actually do use different styles.
Our enterprise e-commerce platform uses an internally maintained design system with utility classes only, very similar to tailwindcss.
We have 15 different "brands", so simply reference a different "brand" to get it's unique styling, while the utility classes all remain the same.
I have done this a few times for large parts of a projects.
Much more common, however, is overriding styles at the component level, which gets funky with this approach.
You either have to add JavaScript logic to assemble the appropriate class names or you wind up with a situation where elements have classes that conflict with their styles, because they are overridden. Imagine something with .text-purple, where the text is clearly not purple.
I see this argument as similar to the "what if I want to switch my framework later" or "what if I want to change my database later". I think it's a nice idea, but I'm not sure when you would ever redesign everything _without_ touching the HTML.
I do see the 'utility'(!) of this kind of approach to css, but have a main issue on responsiveness. Yes, the solution commonly touted is to define global breakpoints: 'small' 'large', etc. But for me defining those globally is too narrow. Sometimes different groups of components need to respond differently, with context.
More worryingly, soon we'll move to 'element' queries, which should suit better than screen-based media queries. We'll be able to say 'when this component is squeezed down to < 500px wide, then wrap the second element underneath the first. That approach is more compatible with modern component architecture. But I don't see a context-specific or even component-specific solution immediately with this framework.
I like Tailwind. I think the appeal here is to write less CSS, get things done faster, have a consistent design and the maintainability. All good so far.
But let's remind everybody that this is only one approach of many you can use.
One approach that can solve problems Tailwind tries to is proper planning. An initial style guide can show you that the `author-bio` component looks similar to `article-preview` (taken from the author's blog post[1]), so you beforehand plan for that HTML and CSS and create a component you can reuse.
Of course, you could have decisions made after the fact and go back and change stuff; nothing is bulletproof.
An important difference in my mind is between designing a web application vs designing a web document.
In the document case, it is possible to mostly keep the document semantic and apply styling via CSS. It matches the content/style separation well.
For web applications, I really like tailwind-like approaches, for multiple reasons:
* changing the style globally is probably hard anyway without adapting the HTML as well (as in the app case, often lots of other things depend on margins etc for example and you have a lot less uniformity/more special cases than in the document case)
* because there is less uniformity, the reuse aspect of the traditional content/presentation split is greatly reduced anyway
* web apps often use component based frameworks already, so you still get reusable components
The big thing that Tailwind brings to functional CSS is the emphasis on and mechanisms for composing from utility classes.
This is something that went understated in other functional libraries, even though composition has been possible for years using preprocessors. So when people saw Tachyons, for instance, they would recoil at what they saw as an unmaintainable mess even though the underlying theory is sound.
I really like the work Adam is doing and where he is leading the conversation. I will read anything he writes, too. He's great at expressing his ideas and rallying you around things you didn't know you care about.
From adamwathan's article, Tailwind's advantage is it limits you to fewer choices. So .text-grey .text-grey-dark .text-grey-darker .text-grey-darkest... rather than coming up with and remembering the hex color values, which might vary between team members.
Wow, the letter spacing and intuitive responsive design is very impressive. While I can't quite yet abandon bootstrap, this looks like a great alternative.
We switched over from a Bourbon-based SASS framework at my work to Tailwind several months ago & it has been fantastic. I was very negative about it at first - “isn’t this just a crappier version of inline styles?” - but I’m a convert. It’s customizable, it enforces consistency, and it dramatically reduces the amount of CSS you have to write, which is a good thing on several levels. Give it a try.
I've been using Tailwind for about a year, and it has really put the joy back into using CSS for me. I know that's crazy to say, but I used to always have so much trouble figuring out what to name things and popping back and forth between HTML and CSS. It was super frustrating, and now I can just flyyyy while building out frontend.
Please give Tailwind a try before knocking it. The syntax seems strange, but after 15 minutes you'll be in love. It doesn't work for every project, but it's a great tool to have in your arsenal. I've been following Adam and Steve on Twitter for quite some time, and these guys are total pros.
I can only imagine the lack of semantic classes makes it almost impossible for end-users to reliably restyle the page using Stylus userstyles. I'd say this type of CSS is hurting power users.
Btw, nothing is preventing you from combining Tailwind with your other methods of writing CSS. For example you could write CSS Grids for general layout and use Tailwind for styling components.
Okay now I get it. That's neat. I'm using tachyons but this approach is compelling.
For what it's worth, I don't think "What is Tailwind?" makes that clear (the "designed to be customized" section hints at it) and I had to read https://tailwindcss.com/docs/configuration to understand the difference after reading your comment. Thanks!
One of the biggest strengths of Bootstrap is that everyone uses Bootstrap. I can come to a company, use Bootstrap, and expect that new employees will also use Bootstrap or that it's already been used there! It's so common in the industry.
It also means, for front-end developers like me, that I can create a company theme on Bootstrap, and get junior developers to use it can feel comfortable with being able to implement the brand design language and not have to pixel-fiddle. Not only does this strengthen the technical skillset of my team, but it also helps keep standards high when dealing with offshore resources.
We can get them to build UI for entirely separate projects, slap our branding on it, and dramatically cut down on resource allocation, time, and effort.
So, here's the thing: this is great, but Bootstrap also has utility classes these days. If I use Tailwind, or Foundation, or Bulma, I immediate lose out on developer leverage. This is so much more powerful than moving some classes around.
Approaching all of these design concerns in a slightly different way just isn't powerful enough to outweigh the leverage Bootstrap has over everything else out there.
It's just not significantly different enough. It doesn't standout to me. In fact, how it differentiates in its introduction is a _con_ to me. No, the industry doesn't need complete UI kits, design languages vary enough that this could provide additional undesired weight, however people use buttons, drop-downs, and many other common components _all the time_.
A major competitor to Bootstrap in the arena would need to do something revolutionary.
You make interesting points. For me, as a back-end developer, the prevalence of Bootstrap is a major con because I can't (due to lack of knowledge or confidence, I'm not sure) make a Bootstrap site look like anything other than a Bootstrap site with different colors. Tailwind is the first front-end framework that successfully got me away from that and helped me execute own unique design vision. What seems to come to you easily is a complete struggle for me.
Don't disagree with this at all. Bootstrap, like Tailwind, is a tool and after I purchased a cordless drill I didn't go around telling everybody I know to throw out their manual screwdrivers because the revolution has arrived. It seems to me that's what people perceive is happening here.
Tailwind isn't really competing against Bootstrap imo, it's an option to keep working the "Boostrap way" once you start fighting the framework and want to create your own instead, or want to preserve working with utility classes for simple custom landing pages.
If you need to customize the components a lot you might lose most of the benefits that Bootsrap provides, that's where Tailwind comes in.
You really do have to try it to shake that impression.
If you need a bit more convincing before you're willing to try it, I wrote an in-depth article a while ago that documents my journey from a "semantic classes"-loving HTML/CSS purist to a utility-loving heathen:
https://adamwathan.me/css-utility-classes-and-separation-of-...
If you want to watch it in action, here's a screencast where I rebuild the Netlify UI in an hour and a half without writing any custom CSS:
https://www.youtube.com/watch?v=_JhTaENzfZQ
...and if you want "social proof", here's an interview I did with Diana Mounter who leads the design systems team at GitHub about how moving to a utility-based approach has made things infinitely more maintainable for them, and given their developers a lot more confidence:
http://www.fullstackradio.com/75
If you have any questions I'll be checking the comments, thanks!