August 4, 2019
•Last updated November 5, 2023
How to Extend Tailwind CSS
Tailwind CSS packs a punch as a utility-first CSS framework. Even with some amazing defaults at your disposal, there comes a time where you might need to extend the framework. In this post, learn how to extend Tailwind CSS even further.
Use Cases
Each use case below is what I would do. In no way are they meant for how you should approach it yourself. Consider these tips and tricks I have learned by working with the framework over time.
Internal/Personal approach
The first and probably obvious way to extend Tailwind is to use a CSS approach. Doing this might look like the following:
Note: In this video/example I reference the playground project on the Tailwind CSS Github project page. This uses basic dependencies to help you jump right in. In previous installments of this series, I walk you through how to get it up and running. Find those linked at the end of this post.
Adding new utilities via CSS
New utility classes should come only after the @tailwind
directives in your main stylesheet.
/* tailwind.css*/
@tailwind base;
@tailwind components;
@tailwind utilities;
.rotate-0 {
transform: rotate(0deg);
}
.rotate-90 {
transform: rotate(90deg);
}
.rotate-180 {
transform: rotate(180deg);
}
.rotate-270 {
transform: rotate(270deg);
}
Those classes now work alongside everything bundled with your Tailwind configuration. I should note that I am, using the default supplied by the Tailwind CSS docs.
What if we wanted the rotate-
classes to also be responsive? Enter the handy supplied @responsive
directive.
/* tailwind.css*/
...
@responsive {
.rotate-0 {
transform: rotate(0deg);
}
.rotate-90 {
transform: rotate(90deg);
}
.rotate-180 {
transform: rotate(180deg);
}
.rotate-270 {
transform: rotate(270deg);
}
}
What about hover/focus states? The same concept as @responsive
applies here. We can even wrap everything within @responsive
like the following:
/* tailwind.css*/
@responsive {
@variants hover, focus {
.rotate-0 {
transform: rotate(0deg);
}
.rotate-90 {
transform: rotate(90deg);
}
.rotate-180 {
transform: rotate(180deg);
}
.rotate-270 {
transform: rotate(270deg);
}
}
}
Pretty cool! This use case probably makes the most since for internal projects. Those might be projects created on your own or in a team setting. If you were to use Sass or Less you could extract these extra utilities into a partial and import the CSS using @import
to cut down on the amount of lines of code in your files.
Fancy PostCSS approach
With the @apply
statement adding existing Tailwind utility classes is completely possible. Maybe you want to define a few components for forms and make use of Tailwind. Ideally you want to extract all the classes from your HTML markup. That might look something like this.
.input {
@apply appearance-none block w-full bg-white text-gray-700 border border-black rounded py-3 px-4 leading-none;
}
.select {
@apply appearance-none py-3 px-4 pr-8 block w-full bg-white border border-black text-gray-700
rounded leading-tight;
-webkit-appearance: none;
}
The benefit here is to group already existing Tailwind utility classes for reuse inside a single scoped class name. Elements in your app/website that are often repeated are the best candidate for this type of grouping.
External / Open Source approach
Another approach towards extending Tailwind CSS involves more JavaScript configuration. While a little more complex, this approach allows you to extract the logic for others(or you) to use elsewhere. You could wrap it up as a node module for example, or even just a library of modules that define custom components for you or others. The sky is the limit.
Adding button components for example, (without writing any additional CSS in the stylesheet of your project) might look like the following:
// tailwind.config.js
module.exports = {
// a bunch of other tailwind settings
plugins: [
function({ addComponents }) {
const buttons = {
'.btn': {
padding: '.5rem 1rem',
borderRadius: '.25rem',
fontWeight: '600',
},
'.btn-primary': {
backgroundColor: '#4299e1',
color: '#fff',
'&:hover': {
backgroundColor: '#3182ce'
},
},
'.btn-secondary': {
backgroundColor: '#667eea',
color: '#fff',
'&:hover': {
backgroundColor: '#5a67d8'
},
},
}
addComponents(buttons)
}
]
}
Notice the addComponents
function is specific to the Tailwind config. We destructure it and are able to pass an object of CSS-in-JS. We define this function within the plugins
array inside the tailwind.config.js
file accordingly. You could also require this from another location in your project or even from a separate node module.
// tailwind.config.js
module.exports = {
plugins: [
require('./plugins/buttons')
]
}
The main disadvantage I see here is not having access to pre-existing utility classes for use in the direct CSS. You could reference a given users configuration with a little more configuration, but that's a bit more complex to do so.
On top of the addComponents
function there are several others you can hook into and extend Tailwind with. Here is a full list and a quick summary of each:
addUtilities()
, for registering new utility stylesaddComponents()
, for registering new component stylesaddBase()
, for registering new base stylesaddVariant()
, for registering custom variantse()
, for escaping strings meant to be used in class namesprefix()
, for manually applying the user's configured prefix to parts of a selectortheme()
, for looking up values in the user's theme configurationvariants()
, for looking up values in the user's variants configurationconfig()
, for looking up values in the user's Tailwind configuration
I often find myself extending the @tailwind base
using addBase()
as a plugin. I set heading levels to specific defaults accordingly since they are completely reset in size and style otherwise.
// tailwind.config.js
module.exports = {
plugins: [
function({ addBase, config }) {
addBase({
'h1': { fontSize: config('theme.fontSize.3xl') },
'h2': { fontSize: config('theme.fontSize.2xl') },
'h3': { fontSize: config('theme.fontSize.xl') },
'h4': { fontSize: config('theme.fontSize.lg') },
})
]
}
I recommend giving the plugin documentation a good read. There's so much more I haven't covered here you can do in your own projects to configure and extend Tailwind CSS quite easily.
Much of the code examples and documentation snippets above are taken from tailwindcss.com which is built and maintained by Adam Wathan. Go support him!
The Series So Far
Categories
Collection
Part of the Let's Build: With Tailwind CSS collection
Products and courses
-
Hello Hotwire
A course on Hotwire + Ruby on Rails.
-
Hello Rails
A course for newcomers to Ruby on Rails.
-
Rails UI
UI templates and components for Rails.