skills/tailwind-design/SKILL.md
Design tokens and component patterns for Tailwind CSS projects.
npx skillsauth add hayeah/dotfiles tailwindInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Style guide for building with Tailwind CSS using centralized design tokens. The core principle: name design decisions once, reference them everywhere, separate meaning from appearance.
blue-600 = #2563eb, radius-md = 8pxcolor.primary = blue-600button.background.primary = color.primaryIn Tailwind, raw and semantic tokens live in tailwind.config, and semantic tokens reference CSS variables for theming.
Tokenize values that:
At minimum, centralize: colors, radius, shadows, typography.
Other common categories: spacing, font weights, z-index layers, motion durations/easing, breakpoints.
Define semantic colors referencing CSS variables in tailwind.config:
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: "hsl(var(--primary))",
"primary-foreground": "hsl(var(--primary-foreground))",
secondary: "hsl(var(--secondary))",
"secondary-foreground": "hsl(var(--secondary-foreground))",
muted: "hsl(var(--muted))",
"muted-foreground": "hsl(var(--muted-foreground))",
destructive: "hsl(var(--destructive))",
"destructive-foreground": "hsl(var(--destructive-foreground))",
surface: "hsl(var(--surface))",
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
},
},
},
}
Then define variables in globals.css:
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
/* ... */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 47.4% 11.2%;
/* ... */
}
Why CSS variables:
Name colors by role, not appearance.
Bad: bg-blue-600, text-gray-800
Good: bg-primary, bg-surface, text-foreground, text-muted-foreground
Standard semantic set:
background / foreground - page-levelsurface - card/panel backgroundsmuted / muted-foreground - subdued elementsprimary / primary-foreground - primary actionssecondary / secondary-foreground - secondary actionsdestructive / destructive-foreground - danger/deleteborder / input / ring - form and focus statesAvoid:
text-[#1f2937] - use a semantic color tokenrounded-[10px] - use a radius tokenshadow-[0_3px_12px_rgba(...)] - use a shadow tokenp-[13px], mt-[7px] - use the spacing scaleIf you need a value twice, make it a token.
Define constrained scales in config:
borderRadius: {
sm: "4px",
md: "8px",
lg: "12px",
xl: "16px",
},
boxShadow: {
sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)", // subtle
md: "0 4px 6px -1px rgb(0 0 0 / 0.1)", // card
lg: "0 10px 15px -3px rgb(0 0 0 / 0.1)", // modal
},
Components use rounded-md, shadow-sm, shadow-lg. Changing visual style means changing token values, not component code.
Standardize allowed text sizes. Optionally define semantic font utilities:
fontSize: {
caption: ["0.75rem", { lineHeight: "1rem" }],
body: ["0.875rem", { lineHeight: "1.25rem" }],
"heading-sm": ["1.125rem", { lineHeight: "1.75rem", fontWeight: "600" }],
"heading-lg": ["1.5rem", { lineHeight: "2rem", fontWeight: "700" }],
},
Usage: text-body, text-caption, text-heading-sm. Prevents arbitrary text-[15px].
Tailwind's spacing scale is already tokenized. The mistake is arbitrary spacing.
Encode spacing into component size variants:
const sizeStyles = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
}
Spacing becomes part of the component API, not ad-hoc classes.
Don't repeat class strings. Build components that consume tokens via variants.
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
{
variants: {
variant: {
primary: "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
ghost: "hover:bg-muted hover:text-muted-foreground",
outline: "border border-input bg-background hover:bg-muted",
},
size: {
sm: "h-8 px-3 text-sm",
md: "h-10 px-4 text-base",
lg: "h-12 px-6 text-lg",
},
},
defaultVariants: {
variant: "primary",
size: "md",
},
}
)
All variants reference semantic token utilities. If primary color changes, every button updates.
bg-blue-600tools
Web UI development — Vite+ toolchain setup and browser-based E2E testing workflow.
tools
Tooling and style guide for TypeScript projects.
development
Capture tmux pane content and export as text, HTML, SVG, PNG, or JPG. Use when you need a screenshot or text dump of a tmux pane for sharing, feeding to AI, or archiving terminal state.
testing
Copy-edit text. Fix grammar and/or tidy text into a concise listicle.