Back

Understanding and unlocking the power of tailwind.config.js file

August 08, 2024 (about 1 month ago)

10 min read
views

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

  1. Extend the Default Theme: Add new values or override existing values for colors, spacing, fonts, and more.
  2. 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.

tailwind.config.js
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"],
      },
    },
  },
};
tailwind.config.js
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:

  1. @tailwindcss/typography
  2. @tailwindcss/forms
  3. @tailwindcss/aspect-ratio
  4. @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.

  1. Use addUtilities(), to create new static utility styles.
  2. Use matchUtilities(), to create new dynamic utility styles.
  3. Use addComponents(), to create new static component styles.
  4. Use matchComponents(), to create new dynamic component styles.
  5. Use addVariant(), to create new dynamic variant styles.
  6. Use matchVariant(), to create new dynamic variant styles.
  7. Use addBase(), to add base styles globally, it allows you to add or override these base styles.
  8. Use theme(), for looking up values in the theme configuration.
  9. Use config(), to access custom config values.
  10. Use e(), to escape special characters in class names.
  11. Use corePlugins(), to check the status of core plugins and add custom utilities accordingly.

addUtilities()

  1. 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.
tailwind.config.js
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%)",
        },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. 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 like sm, md... etc or direct values like 16px or 1rem. EG: clamp-[base-10vw-6rem].
tailwind.config.js
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]})`,
          };
        },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. 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.
tailwind.config.js
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"),
          },
        },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. 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.
tailwind.config.js
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}`),
        },
      );
    }),
  ],
};
tailwind.config.js
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()

  1. 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.
tailwind.config.js
const plugin = require("tailwindcss/plugin");
 
module.exports = {
  // ...
  plugins: [
    plugin(({ addVariant, e }) => {
      addVariant("children", ({ modifySelectors, separator }) => {
        modifySelectors(({ className }) => {
          return `.${e(`children${separator}${className}`)} > *`;
        });
      });
    }),
  ],
};
tailwind.config.js
const plugin = require("tailwindcss/plugin");
 
module.exports = {
  // ...
  plugins: [
    plugin(({ addVariant, e }) => {
      addVariant("children", ({ modifySelectors, separator }) => {
        modifySelectors(({ className }) => {
          return `.${e(`children${separator}${className}`)} > *`;
        });
      });
    }),
  ],
};

matchVariant()

  1. With matchVariant() we can create custom dynamic variants. Adding on the above example we can make the child selector dynamic with matchVariant(). We can use other pseudo classes with the child selector eg:- children-[p:hover]:text-red-500.
tailwind.config.js
const plugin = require("tailwindcss/plugin");
 
module.exports = {
  // ...
  plugins: [
    plugin(({ matchVariant }) => {
      matchVariant("children", (value) => {
        return `& > ${value}`;
      });
    }),
  ],
};
tailwind.config.js
const plugin = require("tailwindcss/plugin");
 
module.exports = {
  // ...
  plugins: [
    plugin(({ matchVariant }) => {
      matchVariant("children", (value) => {
        return `& > ${value}`;
      });
    }),
  ],
};

addBase()

  1. Use addBase() to add base styles globally, it allows you to add or override these base styles directly from your Tailwind CSS configuration.
tailwind.config.js
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" },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. 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.
tailwind.config.js
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"),
        },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. Use config() to access values from the Tailwind configuration file tailwind.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.
tailwind.config.js
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,
        },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. 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.

tailwind.config.js
const plugin = require("tailwindcss/plugin");
 
module.exports = {
  plugins: [
    plugin(function ({ addUtilities, e }) {
      addUtilities({
        [`.${e("w/3")}`]: {
          width: "33.333%",
        },
      });
    }),
  ],
};
tailwind.config.js
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()

  1. Use corePlugins() to create a custom configuration for core plugins. Customize and extend core plugin functionality based on specific conditions.
tailwind.config.js
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",
          },
        });
      }
    }),
  ],
};
tailwind.config.js
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

/plugin/customPlugin.js
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;
/plugin/customPlugin.js
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;
tailwind.config.js
const customPlugin = require("./plugins/customPlugin");
 
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: "#004cff",
        secondary: "#ffe600",
      },
    },
  },
  plugins: [customPlugin],
};
tailwind.config.js
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.