Introduction.
Tailwind CSS is a popular utility-first CSS framework that enables developers to build custom designs without leaving their HTML. One of the key features that make Tailwind CSS highly flexible and customizable is its configuration file, tailwind.config.js. This file allows developers to tailor the framework to their specific needs, extending or modifying the default settings provided by Tailwind.
What is the Tailwind Config File?
The tailwind.config.js file is a JavaScript file where you can define custom configurations for your Tailwind setup. By default, Tailwind CSS comes with a set of pre-defined utility classes. However, the configuration file allows you to configure your base style according to your project requirements.
Table of contents
- Extend the Default Theme: Add new values or override existing values for colors, spacing, fonts, and more.
- Add Plugins: Extend Tailwind's functionality with plugins.
Pre-requisite
Have tailwind installed. To know how to install tailwind in a basic html project check tailwind documents or check how to install in your preferred framework.
So now let's start with the configuration.
Extend the Default Theme
One of the most powerful features of the Tailwind config file is the ability to extend the default theme. This allows you to add custom colors, spacing, fonts, and other design tokens.
You can use functions for top-level theme keys and access colors, breakpoints by destructuring. It can be used to apply default values mentioned in your tailwind config file.
module.exports = {
theme: {
extend: {
backgroundImage: ({ colors, breakpoints }) => ({
"green-gradient-border": `linear-gradient(135deg,#00ff85 0%,#004a4a 23.5%,#251743 39%,#251743 48.5%,#251743 53.5%,#004a4a 78%,#00ff85 100%)`,
}),
boxShadow: () => ({
"green-shadow": "0px 0px 12px 0px #00ff8566",
}),
colors: {
background: "#ffffff",
foreground: "#070708",
primary: {
DEFAULT: "#18181b",
foreground: "#fafafa",
},
secondary: {
DEFAULT: "#f2f2f3",
foreground: "#18181b",
},
},
fontSize: {
xs: ".75rem",
sm: ".875rem",
base: "1rem",
lg: "1.125rem",
xl: "1.25rem",
"2xl": "1.5rem",
"3xl": "1.875rem",
"4xl": "2.25rem",
"5xl": "3rem",
},
spacing: {
72: "18rem",
84: "21rem",
96: "24rem",
},
fontFamily: {
sans: ["Inter", "sans-serif"],
serif: ["Merriweather", "serif"],
},
},
},
};
module.exports = {
theme: {
extend: {
backgroundImage: ({ colors, breakpoints }) => ({
"green-gradient-border": `linear-gradient(135deg,#00ff85 0%,#004a4a 23.5%,#251743 39%,#251743 48.5%,#251743 53.5%,#004a4a 78%,#00ff85 100%)`,
}),
boxShadow: () => ({
"green-shadow": "0px 0px 12px 0px #00ff8566",
}),
colors: {
background: "#ffffff",
foreground: "#070708",
primary: {
DEFAULT: "#18181b",
foreground: "#fafafa",
},
secondary: {
DEFAULT: "#f2f2f3",
foreground: "#18181b",
},
},
fontSize: {
xs: ".75rem",
sm: ".875rem",
base: "1rem",
lg: "1.125rem",
xl: "1.25rem",
"2xl": "1.5rem",
"3xl": "1.875rem",
"4xl": "2.25rem",
"5xl": "3rem",
},
spacing: {
72: "18rem",
84: "21rem",
96: "24rem",
},
fontFamily: {
sans: ["Inter", "sans-serif"],
serif: ["Merriweather", "serif"],
},
},
},
};
Add plugins
Tailwind allows you to extend its functionality with plugins. You can add community plugins or write your own.
Plugins can be added to your project by installing them via npm, then adding them to your tailwind.config.js
file:
Some of the official plugins developed by tailwind team are:
@tailwindcss/typography
@tailwindcss/forms
@tailwindcss/aspect-ratio
@tailwindcss/container-queries
To create custom plugin import plugin
from tailwindcss/plugin
then inside the plugin array you can call the plugin
function.
Listed below are all the functions provided by the plugin function.
- Use
addUtilities()
, to create new static utility styles. - Use
matchUtilities()
, to create new dynamic utility styles. - Use
addComponents()
, to create new static component styles. - Use
matchComponents()
, to create new dynamic component styles. - Use
addVariant()
, to create new dynamic variant styles. - Use
matchVariant()
, to create new dynamic variant styles. - Use
addBase()
, to add base styles globally, it allows you to add or override these base styles. - Use
theme()
, for looking up values in the theme configuration. - Use
config()
, to access custom config values. - Use
e()
, to escape special characters in class names. - Use
corePlugins()
, to check the status of core plugins and add custom utilities accordingly.
addUtilities()
- With
addUtilities()
we can create custom utility classes. These utility classes can be added directly to your HTML to apply specific styles. This method allows you to extend Tailwind's default set of utilities with your own custom ones.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/typography"),
plugin(function ({ addUtilities }) {
addUtilities({
".clip-polygon": {
clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",
},
".clip-circle": {
clipPath: "circle(50%)",
},
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
require("tailwindcss-animate"),
require("@tailwindcss/typography"),
plugin(function ({ addUtilities }) {
addUtilities({
".clip-polygon": {
clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",
},
".clip-circle": {
clipPath: "circle(50%)",
},
});
}),
],
};
matchUtilities()
- With
matchUtilities()
we can create dynamic utility styles. Since there is no clamp class in tailwind you can create it with a custom plugin. With this you can either give default tailwind sizes likesm
,md
... etc or direct values like16px
or1rem
. EG:clamp-[base-10vw-6rem]
.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(function ({ matchUtilities, theme }) {
matchUtilities({
clamp(value) {
const sizes = theme("fontSize");
const split = value
.split("-")
.map((v) => (sizes[v] ? sizes[v]["0"] : v));
return {
fontSize: `clamp(${split[0]}, ${split[1]}, ${split[2]})`,
};
},
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(function ({ matchUtilities, theme }) {
matchUtilities({
clamp(value) {
const sizes = theme("fontSize");
const split = value
.split("-")
.map((v) => (sizes[v] ? sizes[v]["0"] : v));
return {
fontSize: `clamp(${split[0]}, ${split[1]}, ${split[2]})`,
};
},
});
}),
],
};
addComponents()
- With
addComponents()
we can create static component styles. It's very helpful to create style which are re-used every where like Buttons, Badges, Cards, Alerts etc.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(function ({ addComponents, theme }) {
addComponents({
".btn": {
padding: "10px 15px",
width: "max-content",
borderRadius: "8px",
},
".btn-outline": {
border: "1px solid grey",
color: theme("colors.white"),
},
".btn-destructive": {
background: theme("colors.red.500"),
color: theme("colors.white"),
"&:hover": {
backgroundColor: theme("colors.red.700"),
},
},
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(function ({ addComponents, theme }) {
addComponents({
".btn": {
padding: "10px 15px",
width: "max-content",
borderRadius: "8px",
},
".btn-outline": {
border: "1px solid grey",
color: theme("colors.white"),
},
".btn-destructive": {
background: theme("colors.red.500"),
color: theme("colors.white"),
"&:hover": {
backgroundColor: theme("colors.red.700"),
},
},
});
}),
],
};
matchComponents()
- With
matchComponents()
we can create dynamic component styles. This allows you to define a set of rules that generate classes dynamically, similar to how Tailwind generates utility classes.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(function ({ matchComponents, theme, e }) {
const colors = theme("colors");
const flattenedColors = Object.fromEntries(
Object.entries(colors).flatMap(([key, value]) =>
typeof value === "string"
? [[key, value]]
: Object.entries(value).map(([shade, color]) => [
`${key}-${shade}`,
color,
]),
),
);
matchComponents(
{
scrollbar: (color) => ({
overflow: "scroll",
"scrollbar-width": "thin", // Customize as needed
"scrollbar-color": `${color} transparent`, // Thumb color and track color
}),
},
{
values: flattenedColors,
className: (name) => e(`scrollbar-${name}`),
},
);
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(function ({ matchComponents, theme, e }) {
const colors = theme("colors");
const flattenedColors = Object.fromEntries(
Object.entries(colors).flatMap(([key, value]) =>
typeof value === "string"
? [[key, value]]
: Object.entries(value).map(([shade, color]) => [
`${key}-${shade}`,
color,
]),
),
);
matchComponents(
{
scrollbar: (color) => ({
overflow: "scroll",
"scrollbar-width": "thin", // Customize as needed
"scrollbar-color": `${color} transparent`, // Thumb color and track color
}),
},
{
values: flattenedColors,
className: (name) => e(`scrollbar-${name}`),
},
);
}),
],
};
addVariant()
- With
addVariant()
we can create custom variants. Variants allow you to conditionally apply utility classes based on different states or contexts, such as hover, focus, active, etc.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addVariant, e }) => {
addVariant("children", ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`children${separator}${className}`)} > *`;
});
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addVariant, e }) => {
addVariant("children", ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`children${separator}${className}`)} > *`;
});
});
}),
],
};
matchVariant()
- With
matchVariant()
we can create custom dynamic variants. Adding on the above example we can make the child selector dynamic withmatchVariant()
. We can use other pseudo classes with the child selector eg:- children-[p:hover]:text-red-500.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ matchVariant }) => {
matchVariant("children", (value) => {
return `& > ${value}`;
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ matchVariant }) => {
matchVariant("children", (value) => {
return `& > ${value}`;
});
}),
],
};
addBase()
- Use
addBase()
to add base styles globally, it allows you to add or override these base styles directly from your Tailwind CSS configuration.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addBase }) => {
addBase({
h1: { fontSize: "2.25rem", fontWeight: "700" },
h2: { fontSize: "1.5rem", fontWeight: "700" },
p: { fontSize: "1rem", lineHeight: "1.5" },
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addBase }) => {
addBase({
h1: { fontSize: "2.25rem", fontWeight: "700" },
h2: { fontSize: "1.5rem", fontWeight: "700" },
p: { fontSize: "1rem", lineHeight: "1.5" },
});
}),
],
};
theme()
- Use
theme()
to access values defined in your theme configuration within your plugins or custom styles. This function is useful for ensuring consistency and leveraging the predefined design tokens (colors, spacing, fonts, etc.) that you've set up in your tailwind.config.js file.
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addBase, theme }) => {
addBase({
h1: {
fontSize: theme("fontSize.4xl"),
fontWeight: theme("fontWeight.extra-bold"),
},
h2: {
fontSize: theme("fontWeight.extra-bold"),
fontWeight: theme("fontWeight.extra-bold"),
},
p: {
fontSize: theme("fontSize.base"),
lineHeight: theme("lineHeight.5"),
},
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addBase, theme }) => {
addBase({
h1: {
fontSize: theme("fontSize.4xl"),
fontWeight: theme("fontWeight.extra-bold"),
},
h2: {
fontSize: theme("fontWeight.extra-bold"),
fontWeight: theme("fontWeight.extra-bold"),
},
p: {
fontSize: theme("fontSize.base"),
lineHeight: theme("lineHeight.5"),
},
});
}),
],
};
config()
- Use
config()
to access values from the Tailwind configuration filetailwind.config.js
within custom plugins. This function allows you to retrieve configuration options, including theme settings, variants, plugins, and other custom settings that you have defined.
const plugin = require("tailwindcss/plugin");
module.exports = {
theme: {
extend: {
colors: {
brand: {
light: "#3ab7bf",
DEFAULT: "#2697a1",
dark: "#1e7d84",
},
},
},
},
plugins: [
plugin(({ addUtilities, config }) => {
const brandColors = config("theme.colors.brand");
addUtilities({
".text-brand": {
color: brandColors.DEFAULT,
},
".bg-brand-light": {
backgroundColor: brandColors.light,
},
".bg-brand-dark": {
backgroundColor: brandColors.dark,
},
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
theme: {
extend: {
colors: {
brand: {
light: "#3ab7bf",
DEFAULT: "#2697a1",
dark: "#1e7d84",
},
},
},
},
plugins: [
plugin(({ addUtilities, config }) => {
const brandColors = config("theme.colors.brand");
addUtilities({
".text-brand": {
color: brandColors.DEFAULT,
},
".bg-brand-light": {
backgroundColor: brandColors.light,
},
".bg-brand-dark": {
backgroundColor: brandColors.dark,
},
});
}),
],
};
While both config()
and theme()
in Tailwind CSS are used to access values
defined in your tailwind.config.js
file, they serve slightly different
purposes and have different scopes of access.
theme() | config() |
---|---|
Purpose: The theme() function is specifically designed to access values within the theme section of your Tailwind configuration. | Purpose: The config() function is designed to access any value within the tailwind.config.js file, not just those within the theme section. |
Scope: Limited to the theme section of tailwind.config.js . | Scope: Can access the entire Tailwind configuration file, including custom configurations, variants, plugins, and other settings. |
Usage: Primarily used to access design tokens like colors, spacing, fonts, etc., that are defined within the theme object. | Usage: Used to retrieve configuration values that may not be strictly related to the theme, such as custom settings, variant configurations, or other top-level configuration options. |
Context: Commonly used within custom utilities and variants to ensure consistent design tokens. | Context: Useful when you need to access more than just the theme values, such as custom plugin settings or variant definitions. |
e()
- Use
e()
to escape special characters in class names. This function ensures that class names are valid CSS selectors, which is crucial when dynamically generating class names that might include characters with special meanings in CSS, such as :, /, or .
Eg: To create a custom utility class that includes a slash (/), such as w/3 for a width of 33.333%. You need to escape the slash to make it a valid CSS class name.
const plugin = require("tailwindcss/plugin");
module.exports = {
plugins: [
plugin(function ({ addUtilities, e }) {
addUtilities({
[`.${e("w/3")}`]: {
width: "33.333%",
},
});
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
plugins: [
plugin(function ({ addUtilities, e }) {
addUtilities({
[`.${e("w/3")}`]: {
width: "33.333%",
},
});
}),
],
};
The generated classname looks like
.w\/3 {
width: 33.333%;
}
.w\/3 {
width: 33.333%;
}
corePlugins()
- Use
corePlugins()
to create a custom configuration for core plugins. Customize and extend core plugin functionality based on specific conditions.
const plugin = require("tailwindcss/plugin");
module.exports = {
corePlugins: {
float: false, // Can be disabled
borderRadius: true, // Default is true
},
plugins: [
plugin(function ({ corePlugins, addUtilities }) {
if (corePlugins("borderRadius")) {
addUtilities({
".rounded-custom": {
borderRadius: "12px",
},
});
}
}),
],
};
const plugin = require("tailwindcss/plugin");
module.exports = {
corePlugins: {
float: false, // Can be disabled
borderRadius: true, // Default is true
},
plugins: [
plugin(function ({ corePlugins, addUtilities }) {
if (corePlugins("borderRadius")) {
addUtilities({
".rounded-custom": {
borderRadius: "12px",
},
});
}
}),
],
};
We can modularize the plugins to different files and then import it into main
tailwind.config.js
file to make it more readable.
Modularize
function customPlugin({ addComponents, theme }) {
addComponents({
".btn": {
padding: theme("spacing.4"),
borderRadius: theme("borderRadius.md"),
backgroundColor: theme("colors.primary"),
color: theme("colors.white"),
"&:hover": {
backgroundColor: theme("colors.secondary"),
},
},
});
}
module.exports = customPlugin;
function customPlugin({ addComponents, theme }) {
addComponents({
".btn": {
padding: theme("spacing.4"),
borderRadius: theme("borderRadius.md"),
backgroundColor: theme("colors.primary"),
color: theme("colors.white"),
"&:hover": {
backgroundColor: theme("colors.secondary"),
},
},
});
}
module.exports = customPlugin;
const customPlugin = require("./plugins/customPlugin");
module.exports = {
theme: {
extend: {
colors: {
primary: "#004cff",
secondary: "#ffe600",
},
},
},
plugins: [customPlugin],
};
const customPlugin = require("./plugins/customPlugin");
module.exports = {
theme: {
extend: {
colors: {
primary: "#004cff",
secondary: "#ffe600",
},
},
},
plugins: [customPlugin],
};
Conclusion
The tailwind.config.js file is a powerful tool for creating a comprehensive design system. By customizing the theme, creating custom utilities, adding variants, and leveraging plugins, you can build a scalable and maintainable design system that ensures consistency across your projects. Mastering this file will not only improve your workflow but also enhance the quality and coherence of your designs.