December 6, 2023
•Last updated January 16, 2024
Create Tailwind CSS Plugins From Scratch
This tutorial guides you through creating a custom Tailwind CSS plugin to add a variety of button styles. It is an excellent way to rinse and reuse button components in the multiple prototype-like apps I build yearly. We'll cover designs like solid, outline, and ghost buttons, each with customizable colors and hover states.
Why Tailwind CSS plugins?
From the Tailwind CSS docs,
Plugins let you register new styles for Tailwind to inject into the user’s stylesheet using JavaScript instead of CSS.
This is handy when leveraging reusable tailwind components or styles across different apps, websites, or environments.
Prerequisites
- Familiarity with Tailwind CSS is super useful to have!
- Node.js and yarn or npm installed.
Install Vite.js
I’ll be using vite.js to make use of Tailwind CSS. I reach for it when I need something more static in production. Folks who have followed me might know I usually use Ruby on Rails for my full-stack framework of choice. This guide applies to everything in that environment, too, but this guide is less about Rails.
Alright, let’s install Vite. You’ll want to create a new app using the following command. Follow the steps and choose vanilla JavaScript to keep things simple.
yarn create vite
When naming the project, I chose:
tailwindcss-button-plugin
Choose vanilla JavaScript for any option that prompts you and run
cd tailwindcss-button-plugin
yarn
yarn dev
This should boot a development server and give you a localhost link to visit.
Install Tailwind CSS
Next, we need Tailwind CSS installed.
yarn add tailwindcss postcss autoprefixer -D
npx tailwindcss init -p
These two commands install Tailwind CSS dependencies in the project and create a tailwind.config.js
file and a postcss.config.js
file.
Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js
Inside the tailwind.config.js
file, we can add the following.
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {}
},
plugins: [],
}
Inside the style.css
file, I'll remove the current contents and add the following Tailwind directives.
/* style.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Navigating back to the localhost path Vite supplied us, we should see the browser reset. Tailwind CSS is working, but it’s not apparent until we code.
I’ll remove all default content from the app so we have a clean slate.
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tailwind CSS Button Plugin Tutorial</title>
</head>
<body>
<script type="module" src="/main.js"></script>
</body>
</html>
// main.js
import "./style.css"
You can just delete all the SVG assets, too. With this configuration complete, we are ready to write the plugin.
Step 1: Setting the Plugin File
In the app's root directory, create a new file for the plugin named tailwind-custom-buttons.js
. You can call this whatever you like.
// tailwind-custom-buttons.js
module.exports = function({ addComponents }) {
// Our button styles will be added here
};
We’ll need to export a function with built-in helper functions that give us access to various parts of the tailwind library.
Step 2: Writing the Button Styles
We'll create several button styles: solid, outline, and ghost. Here's how you can define them:
module.exports = function({ addComponents, theme }) {
const buttons = {
'.btn': {
padding: '.5rem 1rem',
borderRadius: theme('borderRadius.md'),
fontWeight: '600',
display: 'inline-block',
textAlign: 'center',
},
'.btn-solid': {
backgroundColor: theme('colors.blue.500'),
color: theme('colors.white'),
'&:hover': {
backgroundColor: theme('colors.blue.600'),
},
},
'.btn-outline': {
backgroundColor: 'transparent',
color: theme('colors.blue.500'),
border: '2px solid',
borderColor: theme('colors.blue.500'),
'&:hover': {
backgroundColor: theme('colors.blue.500'),
color: theme('colors.white'),
},
},
'.btn-ghost': {
backgroundColor: 'transparent',
color: theme('colors.blue.500'),
'&:hover': {
backgroundColor: theme('colors.blue.100'),
},
},
// Additional styles can be added here
};
addComponents(buttons);
};
Step 3: Testing the Plugin
Include the plugin in your tailwind.config.js
:
// tailwind.config.js
module.exports = {
// ...
plugins: [
require('./tailwind-custom-buttons.js'),
// Other plugins...
],
};
You can just run your build process to see the new button styles.
In the index.html
file I added the following
<!-- index.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tailwind CSS Button Plugin Tutorial</title>
</head>
<body class="bg-stone-50 text-stone-800">
<div class="max-w-3xl mx-auto my-24">
<h1 class="font-bold text-3xl pb-6 text-stone-800 text-center">Tailwind CSS Button Plugin Tutorial</h1>
<div class="flex items-center justify-center gap-4">
<button class="btn btn-solid">Solid</button>
<button class="btn btn-outline">Outline</button>
<button class="btn btn-ghost">Ghost</button>
<button class="btn btn-primary">Primary</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-custom">Custom</button>
</div>
</div>
<script type="module" src="/main.js"></script>
</body>
</html>
Step 4: Advanced Customization
Responsive and State Variants
You can easily add responsive and state variants to your buttons:
module.exports = function({ addComponents, variants }) {
const buttons = {
// ... your button styles
};
addComponents(buttons, {
variants: ['responsive', 'hover', 'focus'],
});
};
This unlocks the ability to change button styles that respond to interactions and screen viewport widths.
Dynamic Theming
Allow customization of button colors by using the theme's color configuration:
'.btn-custom': {
backgroundColor: theme('colors.custom', theme('colors.teal.500')),
color: theme('colors.white'),
'&:hover': {
backgroundColor: theme('colors.customHover', theme('colors.teal.600')),
},
},
// Similarly, add .btn-secondary, .btn-danger, etc.
In tailwind.config.js
, add custom colors if needed:
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
custom: "#f79b4b",
customHover: "#e38a3c",
},
},
},
plugins: [require("./tailwind-custom-buttons")],
}
Why not add these using CSS?
Well, you totally can do that. In fact, it’s pretty simple to do, but I think it boils down to reusability. If you’re on a team and build out a series of custom components like this one, you could effectively publish it as a separate repo somewhere and include it on new projects as we advance. With CSS, that’s a little harder to manage but not impossible.
I’ve primarily gone the CSS route since most of my custom CSS is relative to the application or website I’m creating and it didn’t make sense to extract into something super reusable. Your mileage may vary!
Conclusion
You've created a Tailwind CSS plugin that provides a rich set of customizable button styles. This plugin enhances your UI toolkit, enabling quick and consistent button styling across your projects.
You can experiment with different styles and configurations to tailor the plugin to your design needs. Your creativity is the limit!
Useful links
Collection
Part of the 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.