Building complex components from a constrained set of primitive utilities
Traditionally, whenever you need to style something on the web, you write CSS.
You have a new message!
<img class="chat-notification-logo" src="/img/logo.svg" alt="ChitChat Logo">
<p class="chat-notification-message">You have a new message!</p>
margin: 0 auto;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
With Elements, you style elements by applying pre-existing classes directly in your HTML.
You have a new message!
<div class="max-w-sm mx-auto flex p-6 bg-white rounded-lg shadow-xl">
<img class="h-12 w-12" src="/img/logo.svg" alt="ChitChat Logo">
<div class="ml-6 pt-1">
<h4 class="text-xl text-gray-900 leading-tight">ChitChat</h4>
<p class="text-base text-gray-600 leading-normal">You have a new message!</p>
In the example above, we’ve used:
- Elements’ flexbox and padding utilities (
p-6) to control the overall card layout
- The max-width and margin utilities (
mx-auto) to constrain the card width and center it horizontally
- The background color, border radius, and box-shadow utilities (
shadow-xl) to style the card’s appearance
- The width and height utilities (
h-12) to size the logo image
- The margin and padding utilities (
pt-1) to position the card text
- The font size, text color, and line-height utilities (
leading-tight, etc.) to style the card text
…allowing us to implement a completely custom component design, without writing a single line of custom CSS.
Now I know what you’re thinking, “this is an atrocity, what a horrible mess!” and you’re right, it’s kind of ugly. In fact it’s just about impossible to think this is a good idea the first time you see it — you have to actually try it.
But once you’ve actually built something this way, you’ll quickly notice some really important benefits:
- You aren’t wasting energy inventing class names. No more adding silly class names like
sidebar-inner-wrapperjust to be able to style something, and no more agonizing over the perfect abstract name for something that’s really just a flex container.
- Your CSS stops growing. Using a traditional approach, your CSS files get bigger every time you add a new feature. With utilities, everything is reusable so you rarely need to write new CSS.
- Making changes feels safer. CSS is global and you never know what you’re breaking when you make a change. Classes in your HTML are local, so you can change them without worrying about something else breaking.
When you realize how productive you can be working exclusively in HTML with predefined utility classes, working any other way will feel like torture.
Why not just use inline styles?
A common reaction to this approach is wondering, “isn’t this just inline styles?” and in some ways it is — you’re applying styles directly to elements instead of assigning them a class name and then styling that class.
But using utility classes has a few important advantages over inline styles:
- Designing with constraints. Using inline styles, every value is a magic number. With utilities, you’re choosing styles from a predefined design system, which makes it much easier to build visually consistent UIs.
- Responsive design. You can’t use media queries in inline styles, but you can use Elements’ responsive utilities to build fully responsive interfaces easily.
- Pseudo-classes. Inline styles can’t target states like hover or focus, but Elements’ pseudo-class variants make it easy to style those states with utility classes.
This component is fully responsive and includes a button with hover styles, and is built entirely with utility classes:
Customer Support Specialist
<div class="max-w-sm mx-auto bg-white shadow-lg rounded-lg overflow-hidden">
<div class="sm:flex sm:items-center px-6 py-4">
<img class="block mx-auto sm:mx-0 sm:flex-shrink-0 h-16 sm:h-24 rounded-full" src="https://randomuser.me/api/portraits/women/17.jpg" alt="Woman's Face">
<div class="mt-4 sm:mt-0 sm:ml-4 text-center sm:text-left">
<p class="text-xl leading-tight">Erin Lindford</p>
<p class="text-sm leading-tight text-gray-600">Customer Support Specialist</p>
<button class="text-violet-500 hover:text-white hover:bg-violet-500 border border-violet-500 text-xs font-semibold rounded-full px-4 py-1 leading-normal">Message</button>
The biggest maintainability concern when using a utility-first approach is managing commonly repeated utility combinations.
For this we recommend absracting these into components, but only when it’s absolutly required. Elements already ships with several component classes for example
.btn this is a component we use all the time so rather than writing all the classes for color and states there is a simple component class to handle this.
The best solution is to create your components as HTML or react with the required utility classes and then include that component as needed. This increases maintainability and prevents the creation of overly complex and specific classes being created.
Aside from that, maintaining a utility-first CSS project turns out to be a lot easier than maintaining a large CSS codebase, simply because HTML is so much easier to maintain than CSS. Large companies like GitHub, Heroku, Kickstarter, Twitch, Segment, and more are using this approach with great success.
If you’d like to hear about others’ experiences with this approach, check out the following resources:
- By The Numbers: A Year and a Half with Atomic CSS by John Polacek
- Building a Scalable CSS Architecture by Sarah Dayan of Algolia
- Diana Mounter on using utility classes at GitHub, a podcast interview
For even more, check out The Case for Atomic/Utility-First CSS, curated by John Polacek.
On this Page