Generate TwMerge Config
Script to generate tailwind-merge configuration from Tailwind CSS v4 files.
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-configOverview
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
@themeblocks - 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-configThe script will:
- Read your CSS file (default:
app/globals.css) - Extract all tokens from
@themeblocks - Generate the TypeScript configuration file (default:
lib/tw-merge-extended-config.ts) - Create type exports for IDE autocompletion
CLI options
The script accepts several options to customize its behavior:
| Option | Alias | Description | Default value |
|---|---|---|---|
--path <path> | -p | Base path of the project | Current directory |
--css <path> | -c | Path to the CSS file | app/globals.css |
--output <path> | -o | Output file path | lib/tw-merge-extended-config.ts |
--help | -h | Display 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 1Supported token categories
The script automatically detects several CSS token categories:
| Category | CSS Pattern | Example |
|---|---|---|
| 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 */