Tailwind CSS Design System
Tailwind CSS v4 design system patterns: CSS-first configuration, design token hierarchy, CVA component variants, dark mode, and responsive grid systems.
Tags
README
tailwind
Tailwind CSS v4 design system patterns.
What It Does
Installs two rules files covering Tailwind v4 architecture and a known gotcha:
- CSS-first configuration - Use @theme in CSS instead of tailwind.config.ts
- Design token hierarchy - Primitive, semantic, and component token layers
- OKLCH color system - Perceptually uniform colors with full scales
- CVA component variants - Type-safe variant composition with class-variance-authority
- Dark mode - Class-based switching with @custom-variant, context providers, localStorage persistence
- Responsive patterns - Mobile-first, grid variants, size-* shorthand
- Native CSS animations - @keyframes in @theme, @starting-style for entry animations
- v3 to v4 migration - Reference table for common pattern changes
- cursor: pointer gotcha - Tailwind v4 preflight drops
cursor: pointeron buttons; base-style fix and where to put it
Manual Installation
# Global (all projects)
mkdir -p ~/.claude/rules
cp rules/tailwind.md ~/.claude/rules/tailwind.md
cp rules/frontend-css.md ~/.claude/rules/frontend-css.md
# Project-level
mkdir -p .claude/rules
cp rules/tailwind.md .claude/rules/tailwind.md
cp rules/frontend-css.md .claude/rules/frontend-css.md
Files
| File | Description |
|---|---|
rules/tailwind.md |
Tailwind v4 design system guide with tokens, CVA, dark mode, and migration notes |
rules/frontend-css.md |
Tailwind v4's missing cursor: pointer on buttons - base-style fix and where to put it in a project |
Will install
| Path | Action | Target | Type |
|---|---|---|---|
rules/tailwind.md | → | rules/tailwind.md | rule |
rules/frontend-css.md | → | rules/frontend-css.md | rule |
Dependencies
No dependencies.
Required by
No other module depends on this one.
Included in presets
Install this module
Agent prompt
Recommended for agent users -- hands the whole install off to your assistant.
Fetch https://cd23a9be.ccgm-site.pages.dev/modules/tailwind.md and install this module into my Claude Code setup.
Native plugin marketplace
One command via the native plugin marketplace -- additive, does not merge settings.json.
claude plugin install tailwind@ccgm
The marketplace path is additive, not a replacement: it installs commands, agents, and skills as native plugin components, but it does not perform the bash installer's deep settings.json merge, and it does not write the always-loaded global CLAUDE.md context. Rules are only injected via an opt-in SessionStart hook rather than being auto-loaded. Use the bash installer when those pieces matter to you.
Files
rule (2)
rules/tailwind.md
# Tailwind CSS Design System
Patterns for building design systems with Tailwind CSS v4. Emphasizes CSS-first configuration and token-based architecture.
## CSS-First Configuration (v4)
Tailwind v4 uses CSS for configuration instead of `tailwind.config.ts`:
```css
@theme {
--color-primary: oklch(0.7 0.15 250);
--color-surface: oklch(0.98 0.01 250);
--spacing-page: 2rem;
--font-display: "Cal Sans", sans-serif;
}
```
Do NOT create `tailwind.config.ts` or `tailwind.config.js` in v4 projects. All configuration belongs in CSS.
## Design Token Hierarchy
Organize tokens in three layers:
1. **Primitive tokens** - raw values (colors, sizes, font families)
2. **Semantic tokens** - purpose-driven references (`text-primary`, `bg-surface`, `border-subtle`)
3. **Component tokens** - specific UI usage (`button-bg`, `card-radius`)
Use semantic token names, not visual descriptions. `text-primary` not `dark-gray`. `bg-surface` not `light-beige`.
## Color System
Use OKLCH color space for better perceptual uniformity:
```css
@theme {
--color-primary-50: oklch(0.97 0.02 250);
--color-primary-100: oklch(0.93 0.04 250);
--color-primary-500: oklch(0.65 0.15 250);
--color-primary-900: oklch(0.30 0.10 250);
}
```
- Define full color scales (50-950) for primary, neutral, and accent palettes
- Never use hardcoded hex or rgb values in component code
- Reference tokens exclusively: `bg-primary-500` not `bg-[#3b82f6]`
## Component Variants with CVA
Use Class Variance Authority for type-safe component variants:
```typescript
import { cva, type VariantProps } from "class-variance-authority";
const button = cva("inline-flex items-center justify-center rounded-md font-medium", {
variants: {
variant: {
primary: "bg-primary-500 text-white hover:bg-primary-600",
secondary: "bg-surface border border-subtle hover:bg-muted",
ghost: "hover:bg-muted",
},
size: {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4",
lg: "h-12 px-6 text-lg",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
});
```
## Dark Mode
Use class-based dark mode with `@custom-variant`:
```css
@custom-variant dark (&:where(.dark, .dark *));
```
- Implement via a context-based theme provider
- Detect system preference with `prefers-color-scheme`
- Persist user choice to localStorage
- Test both modes for every component
## Responsive Patterns
- Mobile-first: write base styles for mobile, add breakpoint overrides for larger screens
- Use grid variants for responsive column layouts (1 column mobile, 2-3 tablet, 4-6 desktop)
- Use `size-*` shorthand when width and height are equal
- Use `gap-*` instead of `space-x-*` or `space-y-*`
## Animations
Define animations in `@theme` using native CSS `@keyframes`:
```css
@theme {
--animate-fade-in: fade-in 0.2s ease-out;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
```
Use `@starting-style` for entry animations on elements that appear dynamically.
## Migration Notes (v3 to v4)
| v3 Pattern | v4 Pattern |
|-----------|-----------|
| `tailwind.config.ts` | `@theme` in CSS |
| `theme.extend.colors` | `--color-*` custom properties |
| Plugin-based animations | `@keyframes` in `@theme` |
| `darkMode: 'class'` | `@custom-variant dark` |
| Separate `w-*` / `h-*` | `size-*` shorthand |
rules/frontend-css.md
# Frontend CSS Gotchas
## Tailwind v4: cursor: pointer Missing on Interactive Elements
**Problem**: Tailwind v4's preflight does NOT set `cursor: pointer` on `<button>` elements. Browsers default to `cursor: default` for buttons, so every clickable thing on the page looks non-interactive on desktop.
**Rule**: When starting any new project with Tailwind v4, add cursor: pointer base styles immediately - before writing any components.
### Pattern (put this in a shared CSS file imported by all apps)
```css
/* packages/ui/src/theme/base.css or equivalent */
@layer base {
button,
[role="button"],
[type="button"],
[type="reset"],
[type="submit"],
a[href],
label[for],
select,
summary {
cursor: pointer;
}
[disabled],
[aria-disabled="true"] {
cursor: not-allowed;
}
}
```
### Where to put it
- **Monorepo with shared UI package**: Add to `packages/ui/src/theme/base.css`, import in each app's CSS entry point after `@import 'tailwindcss'`
- **Single app**: Add directly to `src/index.css` or `src/globals.css` after `@import 'tailwindcss'`
- **Next.js**: Add to `app/globals.css` or `styles/globals.css`
### Import order matters
```css
@import 'tailwindcss';
@import './base.css'; /* cursor: pointer and other base resets */
@import './tokens.css'; /* design tokens */
```
The base import must come after `tailwindcss` so `@layer base` is available, but before component styles.
### Why this happens
Tailwind v4 changed their preflight philosophy - they removed the `cursor: pointer` override that existed in v3. The browser's native stylesheet sets `cursor: default` on `<button>`, which takes precedence unless explicitly overridden. This affects:
- All `<button>` elements (form submits, icon buttons, toggles)
- Custom interactive divs without a `cursor-pointer` class
- Select dropdowns, file inputs, etc.