Utility-first CSS is the notion of composing many small utilitarian classes together. With this, the aim is to allow you to create robust, scalable and responsive user interfaces for the web. Tailwind is a CSS framework that provides a suite of utility classes out of the box. It also allows you to compose and add your own classes where required.
Tailwind doesn’t prescribe any specific look or feel. You’re free to build to your desired design without having to undo anything the framework offers up front.
A customisable config file drives the look and feel of your UI and provides default rules that you can tweak to suit your needs.
In this article, we’ll go into more depth on how you can use Tailwind to build a feature-rich user interface. We’ll also touch upon some of the more advanced features Tailwind has to offer.
Although Tailwind is available to use directly from a CDN it’s recommended to install it from npm . This allows you to take advantage of the full customisability Tailwind provides.
First, install Tailwind as a development dependency;
Once installed you need to initialise Tailwind’s configuration setup;
This command generates a config file called tailwind.js (or the custom filename provided). This file contains all the predefined rules for colours, breakpoints, fonts, margins and more. The idea is that you remove a lot of the boilerplate and leave only the config required to build your UI. The file contains comments with an overview of each rule.
For example, the default responsive breakpoints provided by Tailwind are;
These are the minimum widths used to generate CSS media queries.
Tailwind uses PostCSS to generate CSS output from the directives and config provided. This means integrating a build step into your current development process is necessary. Integration is possible via various build tools such as Webpack , Gulp and more. Being built on PostCSS means you’re able to leverage the ecosystem of plugins to adapt to your build process.
The simplest solution to get up and running is to create a CSS file and use the @tailwind directive to include all Tailwind’s utilities.
You can use the Tailwind npm module to build the CSS;
You can then reference this output file path in a <link /> tag in your HTML;
Tailwind provides utility classes for a wide array of needs. This allows you to build a responsive, bespoke UI without necessarily needing to write any new CSS.
For example .text-lg by default is 1.5 rem of the base font size. .bg-dark-red will apply a dark red background based on the settings in your config file.
The values set in rules are configurable from your config file. This means if you want .text-lg to be twice the size of your base font you can customise it to be.
You can apply state variants such as hover or focus in the same way as responsive styles. .hover:bg-blue will turn the background blue when hovering over the desired element.
To combine both responsive and state variants at the same time make sure you prefix the responsive variant first. .xl:hover:bg-blue will mean the background is only blue on hover at the xl breakpoint.
Below is an example of a card component with dark and light variations adapted from an example in Tailwind’s documentation .
This markup uses no custom CSS, just utility classes from Tailwind:
View example on CodeSandbox. One of the most powerful features we’re using here is the ability to create responsive grids with just a few classes. Flexbox powers this functionality under the hood.
By applying an outer .flex class we’re able to add classes to inner elements which dictate their widths. For example, w-full lg:w-1/2 xl:w-1/2 will create a full-width element at all breakpoints other than lg and xl , where the width will be half.
With this, you can create complex layouts by composing utility classes. You’re also free to create a grid layout that works for you rather than being prescribed one.
Generally, the utility class name syntax may take a while to get used to due to its shorthand nature. The comments within the generated config file and detailed website documentation are helpful.
As you may have noticed in the example above you can get to a point where you’re applying lots of utility classes to a single element. Use the same element in many places and you’re now having to update each one every time you need to make a tweak. This isn’t scalable.
Luckily Tailwind has a solution to reduce this in the form of component classes.
Component classes allow you to extract many utility classes as well as custom CSS into new classes. This will provide you with a single source of truth for a specific piece of functionality.
Using Tailwind’s @apply directive we can create component classes to abstract these rules.
Here’s the exact same design as the card example above built using component classes where deemed necessary:
And here’s the newly created classes:
This approach reduces the number of classes in the markup whilst delivering the exact same design. We’ve moved reused patterns to new classes allowing a single source of truth.
You’ll also see that we’re still composing utility classes alongside component classes. It’s not a choice between one or the other meaning you can have the best of both worlds.
For example, there’s no need to create a .card-dark class as that’d end up being @apply bg-grey-darkest; . You can use the .bg-grey-darkest class alongside the .card class in the markup.
This is one of the core concepts that gives Tailwind its power. While utilitarian at first glance Tailwind doesn’t force that pattern upon you.
Tailwind comes with a suite of predefined directives, we’ve come across three in this article already:
the @tailwind directive which allows you to import the core of Tailwind
the @apply directive which allows you to create new classes by composing other classes
and the @variants directive, which we’ll take a deeper look at now
In the card example above we’re using the @variant directive to merge our pill hover styles into a single class. This is a powerful way of creating stateful variants of your own utility or component classes. You can use the @variant directive to create focus, active or group-hover specific classes.
Here’s how we used the @variants rule in the example above to create hover variants for our custom component classes:
The @responsive directive will create a suite of responsive classes for any new classes you wrap within it;
This will generate classes prefixed with your responsive breakpoint names. For example, .sm:blur will only apply a blur to images at the small breakpoint.
Finally, the @screen directive allows you to target the already defined breakpoints. This avoids rewriting media queries and having to keep them in sync.
This example will provide stepped levels of blurriness depending on the breakpoint to any images with the blur class applied to it.
Directives are another powerful way to extend custom classes. They can help provide all the features you get from Tailwind’s utility classes to your own custom classes.
Here at NearForm , we use a variety of solutions to build user interfaces. We tailor solutions to the specific needs of clients and their projects.
CSS-in-JS libraries such as Styled Components or Emotion as well as frameworks such as Material-UI are amongst our arsenal. Given this, it’s worth investigating how Tailwind integrates with these tools.
Here’s an example of usage with Styled Components and React ;
This example works well and allows further abstraction via Styled Component’s visual primitives.
There are some caveats worth highlighting when trying to integrate Tailwind into a CSS-in-JS toolchain;
You’re still going to need to amend your toolchain to accommodate a build step for Tailwind
You’ve now got two places you can write styles. How do you ensure consistency?
The component style of a library like React feels somewhat at odds with the component style of Tailwind. You’re now able to componentise either via Tailwind with the @apply directive or in React with a component. A decision is now required on where any abstractions live.
If CSS-in-JS is your jam then there are ways to integrate the two workflows. However, you may end up not gaining the full benefits of either solution when used together.
By default Tailwind clocks in at around 36kb in size without any customisation, once minified and gzipped.
Once you begin to configure Tailwind to suit your UI look and feel you’ll be able to remove a lot of the predefined configuration Tailwind provides.
For example, if you trim the colour configuration down from the 73 colours provided by Tailwind to 25 colours you’ll reduce the bundle size down to 18kb. Similarly, if you reduce down the breakpoints from 5 to 3 you’ll shave 14kb off the bundle size.
Finally, by integrating a tool like PurgeCSS into your workflow you can remove any unused classes at build time. This means you’re only ever serving the classes your application uses.
Tailwind takes the benefits of CSS utility classes and extends upon them. By allowing composability and extensibility Tailwind provides an impressive developer experience. One where you can be productive straight away whilst not being tied to a predefined look and feel.
The lack of prescribed visual styles can be a huge boost to productivity. The speed with which you can compose a complex, responsive UI with its own look and feel is staggering.
If you looking for a team to deliver a rapid prototype using tools like Tailwind then don’t hesitate to get in touch. Cover image credit
Insight, imagination and expertly engineered solutions to accelerate and sustain progress.