oward

Generate TwMerge Config

Script to generate tailwind-merge configuration from Tailwind CSS v4 files.

beta
⚙️ TAILWIND-MERGE CONFIG GENERATOR
CSS: app/globals.css
Output: lib/tw-merge-extended-config.ts
Parsing @theme tokens...
✔ CSS file parsed successfully
Analyzing color tokens...
✔ Found 35 color tokens
Analyzing spacing tokens...
✔ Found 12 spacing tokens
Analyzing radius tokens...
✔ Found 8 radius tokens
Building TypeScript config...
✔ Configuration generated
Writing output file...
✔ File written successfully

Installation

The script is part of the registry and can be added to your project via the oward-ui CLI:

npx oward-ui add generate-twmerge-config

Overview

The generate-twmerge-config script is an automation tool that automatically generates tailwind-merge configuration from your custom Tailwind CSS v4 tokens. It analyzes your CSS file containing @theme tokens, extracts custom values, and generates a ready-to-use TypeScript file with full type support.

  • Automatic extraction of tokens from @theme blocks
  • Full support: colors, spacing, radius, shadows, font-size, font-family, etc.
  • @import handling with recursive resolution and path traversal protection

Why this script?

When using custom tokens with Tailwind CSS v4, tailwind-merge doesn't recognize them by default. This can cause issues when merging classes:

// ❌ Without configuration
cn("bg-primary", "bg-accent"); // Returns: "bg-primary bg-accent" (both classes!)

// ✅ With generated configuration
cn("bg-primary", "bg-accent"); // Returns: "bg-accent" (correctly merged)

This script fully automates the generation of this configuration.

Integration with cn()

Once the configuration is generated, use it in your cn() utility function:

// lib/utils.ts (basic version)
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  // ⚠️ Custom tokens are not merged correctly.
  return twMerge(clsx(inputs));
}

Expected structure

The script expects to find your Tailwind CSS v4 tokens in a CSS file with @theme blocks:

/* style.css */
@theme {
  /* Colors */
  --color-primary: #3b82f6;
  --color-secondary: #8b5cf6;
  --color-accent: #f59e0b;

  /* Spacing */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;

  /* Radius */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
}

Usage

Basic command

Run the script from the root directory of your project:

node scripts/generate-twmerge-config

The script will:

  1. Read your CSS file (default: app/globals.css)
  2. Extract all tokens from @theme blocks
  3. Generate the TypeScript configuration file (default: lib/tw-merge-extended-config.ts)
  4. Create type exports for IDE autocompletion

CLI options

The script accepts several options to customize its behavior:

OptionAliasDescriptionDefault value
--path <path>-pBase path of the projectCurrent directory
--css <path>-cPath to the CSS fileapp/globals.css
--output <path>-oOutput file pathlib/tw-merge-extended-config.ts
--help-hDisplay help-

Usage examples

# Run from the current directory
node scripts/generate-twmerge-config

# With a specific path
node scripts/generate-twmerge-config --path /path/to/project

# With exit code check
node scripts/generate-twmerge-config || exit 1

Supported token categories

The script automatically detects several CSS token categories:

CategoryCSS PatternExample
Colors--color-*--color-primary, --color-accent
Typography--font-*, --text-*--font-sans, --text-lg
Font Weight--font-weight-*--font-weight-bold
Tracking--tracking-*--tracking-tight
Leading--leading-*--leading-relaxed
Spacing--spacing-*--spacing-md, --spacing-xl
Container--container-*--container-sm
Radius--radius-*--radius-sm, --radius-full
Shadow--shadow-*--shadow-md, --shadow-2xl
Inset Shadow--inset-shadow-*--inset-shadow-sm
Drop Shadow--drop-shadow-*--drop-shadow-lg
Blur--blur-*--blur-sm, --blur-xl
Perspective--perspective-*--perspective-dramatic
Aspect--aspect-*--aspect-video
Breakpoint--breakpoint-*--breakpoint-mobile
Ease--ease-*--ease-in-out
Animate--animate-*--animate-spin

Naming convention

Follow the --category-name convention for your tokens to be detected automatically. For example: --color-primary, --spacing-lg, --radius-md.

@import resolution

The script supports recursive resolution of CSS @import:

/* base.css */
@import "./colors.css";
@import "./spacing.css";

/* colors.css */
@theme {
  --color-primary: blue;
}

/* spacing.css */
@theme {
  --spacing-md: 1rem;
}

Watch out for relative import paths

Make sure import paths are relative to the parent file. For example, in base.css, importing ./colors.css is correct because it's relative to base.css.

Examples:

/* ✅ Correct */
@import "./colors.css";
@import "../tokens/spacing.css";

/* ❌ Incorrect */
@import "colors.css";

Features:

  • Recursive resolution of nested imports
  • Circular import detection and prevention
  • Path traversal protection (security)
  • Automatically ignores HTTP/HTTPS imports

Security - Path Traversal

The script validates that all imported files remain within project boundaries (automatically detected via pnpm-workspace.yaml, .git, or package.json). Compatible with monorepos: cross-package imports are allowed. Only imports going outside the project will be rejected.

Generated file

The script generates a TypeScript file with the complete configuration:

/**
 * @fileoverview Auto-generated tailwind-merge configuration
 * @registry-ignore
 *
 * @description This file is generated by generate-twmerge-config script.
 * Do not edit manually - regenerate using the script instead.
 */

import { extendTailwindMerge } from "tailwind-merge";

/**
 * Custom color tokens extracted from Tailwind CSS v4 @theme
 */
const customColors = [
  "accent",
  "primary",
  "secondary",
  // ... (sorted alphabetically)
] as const;

/**
 * Type helper for customColors
 */
export type CustomColorsToken = (typeof customColors)[number];

/**
 * Extended tailwind-merge configuration
 */
export const twMergeExtendedConfig = {
  extend: {
    classGroups: {
      "text-color": [{ text: customColors }],
      "bg-color": [{ bg: customColors }],
      "border-color": [{ border: customColors }],
      // ... (all color utilities)
    },
  },
} as const;

/**
 * Extended tailwind-merge instance with custom tokens
 */
export const extendedTwMerge = extendTailwindMerge(twMergeExtendedConfig);

Known limitations

The script uses a custom CSS parser optimized for @theme tokens.

Known limitation: Values containing CSS comment delimiters (/* and */) within strings may be misinterpreted.

Edge case example:

--url: "https://example.com/*/path/*"; /* comment */

On this page