Skip to main content
Technology & EngineeringDesign Tool Services197 lines

Tailwind Plugins

Build custom Tailwind CSS plugins that add utilities, components, and variants.

Quick Summary29 lines
You are an expert in the Tailwind CSS plugin API (v3.4+ and v4). You create custom utilities, component classes, and variants that integrate seamlessly with Tailwind's JIT engine, respect the theme configuration, and compose naturally with existing utility classes.

## Key Points

- **Using `addUtilities` when values should be dynamic** -- prefer `matchUtilities` so users can pass arbitrary values with bracket syntax like `text-shadow-[0_2px_4px_red]`.
- **Registering dozens of static component classes** -- this recreates Bootstrap; keep plugins utility-first and compose in markup.
- **Ignoring `important` config** -- always let Tailwind handle specificity; avoid `!important` in plugin CSS.
- **Not declaring `content` paths for plugin-added classes** -- classes generated by `addComponents` still need to appear in scanned files.
- Adding design-system tokens (spacing scales, type scales, color aliases) as first-class Tailwind values.
- Creating project-specific utilities like fluid type, logical properties, or glass effects.
- Wrapping third-party CSS patterns (e.g., skeleton loaders, focus rings) as composable Tailwind classes.
- Building a shareable Tailwind preset for a multi-app monorepo.
- Implementing custom variants for states Tailwind doesn't cover (e.g., `hocus`, `aria-current`).

## Quick Example

```bash
npm install tailwindcss@latest
npm install -D postcss autoprefixer
```

```typescript
// Ignores user theme customization
plugin(function ({ addUtilities }) {
  addUtilities({ ".text-shadow-sm": { textShadow: "0 1px 2px rgb(0 0 0 / 0.1)" } });
});
```
skilldb get design-tool-services-skills/Tailwind PluginsFull skill: 197 lines
Paste into your CLAUDE.md or agent config

Tailwind CSS Custom Plugins

You are an expert in the Tailwind CSS plugin API (v3.4+ and v4). You create custom utilities, component classes, and variants that integrate seamlessly with Tailwind's JIT engine, respect the theme configuration, and compose naturally with existing utility classes.

Core Philosophy

Extend, Don't Override

Plugins should add capabilities to Tailwind's existing system. Use theme.extend to add values without clobbering defaults. Register utilities under clear namespaces so they coexist with core utilities.

Theme-Aware by Default

Every magic number in a plugin should reference theme() values. This ensures dark mode, responsive breakpoints, and custom theme overrides propagate automatically.

Composable Primitives

Design plugin utilities as single-purpose building blocks. A .card component class is less flexible than composable .surface, .rounded-card, and .shadow-card utilities.

Setup

npm install tailwindcss@latest
npm install -D postcss autoprefixer
// tailwind.config.ts
import type { Config } from "tailwindcss";
import plugin from "tailwindcss/plugin";

export default {
  content: ["./src/**/*.{ts,tsx,html}"],
  theme: {
    extend: {
      colors: {
        brand: { 500: "#6366f1", 600: "#4f46e5" },
      },
    },
  },
  plugins: [
    // inline plugin or require("./plugins/my-plugin")
  ],
} satisfies Config;

Key Patterns

Do: Use matchUtilities for value-driven utilities

plugin(function ({ matchUtilities, theme }) {
  matchUtilities(
    { "text-shadow": (value) => ({ textShadow: value }) },
    { values: theme("textShadow") }
  );
});

Not: Hard-code values inside the plugin

// Ignores user theme customization
plugin(function ({ addUtilities }) {
  addUtilities({ ".text-shadow-sm": { textShadow: "0 1px 2px rgb(0 0 0 / 0.1)" } });
});

Do: Register custom variants with addVariant

plugin(function ({ addVariant }) {
  addVariant("hocus", ["&:hover", "&:focus"]);
  addVariant("group-hocus", [":merge(.group):hover &", ":merge(.group):focus &"]);
});
// Usage: <button class="hocus:ring-2">

Common Patterns

Fluid typography utility

plugin(function ({ matchUtilities, theme }) {
  matchUtilities(
    {
      "text-fluid": (value: string) => {
        const [min, max] = value.split("_");
        return {
          fontSize: `clamp(${min}, 2vw + 1rem, ${max})`,
        };
      },
    },
    {
      values: {
        sm: "0.875rem_1.125rem",
        base: "1rem_1.25rem",
        lg: "1.25rem_1.75rem",
        xl: "1.5rem_2.25rem",
      },
    }
  );
});
// Usage: <h1 class="text-fluid-xl">

Component class with theme values

plugin(function ({ addComponents, theme }) {
  addComponents({
    ".btn": {
      padding: `${theme("spacing.2")} ${theme("spacing.4")}`,
      borderRadius: theme("borderRadius.lg"),
      fontWeight: theme("fontWeight.semibold"),
      fontSize: theme("fontSize.sm"),
      transition: "background-color 150ms ease",
    },
    ".btn-primary": {
      backgroundColor: theme("colors.brand.500"),
      color: theme("colors.white"),
      "&:hover": { backgroundColor: theme("colors.brand.600") },
    },
  });
});

Logical property utilities

plugin(function ({ matchUtilities, theme }) {
  matchUtilities(
    {
      "mbs": (value) => ({ marginBlockStart: value }),
      "mbe": (value) => ({ marginBlockEnd: value }),
      "mis": (value) => ({ marginInlineStart: value }),
      "mie": (value) => ({ marginInlineEnd: value }),
    },
    { values: theme("margin"), supportsNegativeValues: true }
  );
});
// Usage: <div class="mbs-4 mie-2">

Shareable plugin with options

// plugins/glass.ts
import plugin from "tailwindcss/plugin";

export const glass = plugin.withOptions<{ blur?: string }>(
  (options = {}) =>
    function ({ addUtilities }) {
      const blur = options.blur ?? "12px";
      addUtilities({
        ".glass": {
          background: "rgba(255, 255, 255, 0.1)",
          backdropFilter: `blur(${blur})`,
          border: "1px solid rgba(255, 255, 255, 0.15)",
        },
      });
    },
  () => ({}) // optional theme extensions
);

// tailwind.config.ts
// plugins: [glass({ blur: "16px" })]

Dark mode aware base styles

plugin(function ({ addBase, theme }) {
  addBase({
    ":root": {
      "--surface": theme("colors.white"),
      "--on-surface": theme("colors.gray.900"),
    },
    ".dark": {
      "--surface": theme("colors.gray.900"),
      "--on-surface": theme("colors.gray.100"),
    },
  });
});

Anti-Patterns

  • Using addUtilities when values should be dynamic -- prefer matchUtilities so users can pass arbitrary values with bracket syntax like text-shadow-[0_2px_4px_red].
  • Registering dozens of static component classes -- this recreates Bootstrap; keep plugins utility-first and compose in markup.
  • Ignoring important config -- always let Tailwind handle specificity; avoid !important in plugin CSS.
  • Not declaring content paths for plugin-added classes -- classes generated by addComponents still need to appear in scanned files.

When to Use

  • Adding design-system tokens (spacing scales, type scales, color aliases) as first-class Tailwind values.
  • Creating project-specific utilities like fluid type, logical properties, or glass effects.
  • Wrapping third-party CSS patterns (e.g., skeleton loaders, focus rings) as composable Tailwind classes.
  • Building a shareable Tailwind preset for a multi-app monorepo.
  • Implementing custom variants for states Tailwind doesn't cover (e.g., hocus, aria-current).

Install this skill directly: skilldb add design-tool-services-skills

Get CLI access →