Tailwind patterns
Skill Inteligentsensingsolutions/tdd-dev-workflow/skills/tailwind-patterns
This skill should be used when the user asks about "Tailwind CSS", "Tailwind v4", "CSS utility classes", "responsive design", "dark mode", "container queries", "design tokens", "@theme directive", "component styling", "OKLCH colors", or needs guidance on Tailwind CSS patterns and configuration.From its SKILL.md
npx -y skills add Inteligentsensingsolutions/tdd-dev-workflow --skill tailwind-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.4 KB, as published. Nobody here has run it
Tailwind Patterns: CSS-First Utility Design
Tailwind v4 Architecture
Tailwind CSS v4 moves from JavaScript-based configuration to CSS-first configuration. The entire design system is defined in CSS, making it composable and portable.
Key Changes from v3 to v4
| v3 (JavaScript) | v4 (CSS-first) |
|---|---|
tailwind.config.js | @theme directive in CSS |
@tailwind base/components/utilities | @import "tailwindcss" |
content array for purging | Automatic content detection |
| PostCSS-based engine | Oxide engine (Rust-based, faster) |
| Manual dark mode config | Built-in dark: with prefers-color-scheme |
| No container queries | Native @container support |
Minimal v4 Entry Point
@import "tailwindcss";
This single import replaces the three @tailwind directives from v3. No JavaScript configuration file is needed for most projects.
CSS-Based Configuration with @theme
Define design tokens directly in CSS using the @theme directive. This replaces tailwind.config.js entirely for most customization needs.
Defining Design Tokens
@import "tailwindcss";
@theme {
--color-primary: oklch(0.7 0.15 250);
--color-secondary: oklch(0.6 0.12 300);
--color-accent: oklch(0.8 0.18 150);
--color-success: oklch(0.72 0.19 145);
--color-warning: oklch(0.82 0.17 85);
--color-error: oklch(0.65 0.22 25);
--font-sans: "Inter", "system-ui", sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
--spacing-18: 4.5rem;
--spacing-72: 18rem;
--spacing-84: 21rem;
--radius-DEFAULT: 0.5rem;
--radius-lg: 1rem;
--radius-xl: 1.5rem;
--shadow-soft: 0 2px 8px oklch(0 0 0 / 0.08);
--shadow-card: 0 4px 16px oklch(0 0 0 / 0.12);
}
Token Naming Convention
Follow Tailwind's naming pattern so tokens map to utility classes automatically:
| Token Pattern | Generated Utility |
|---|---|
--color-primary | bg-primary, text-primary |
--font-sans | font-sans |
--spacing-18 | p-18, m-18, gap-18 |
--radius-lg | rounded-lg |
--shadow-card | shadow-card |
--breakpoint-xl | xl: prefix |
OKLCH Color System
Tailwind v4 uses OKLCH as the recommended color format. OKLCH provides perceptually uniform colors, meaning equal changes in numeric values produce equal visual changes.
OKLCH Syntax
oklch(lightness chroma hue)
| Component | Range | Description |
|---|---|---|
| Lightness | 0 -- 1 | 0 = black, 1 = white |
| Chroma | 0 -- 0.4 | 0 = gray, higher = more saturated |
| Hue | 0 -- 360 | Color wheel angle (0=red, 120=green, 240=blue) |
Building a Palette
Create consistent palettes by varying lightness while keeping chroma and hue constant:
@theme {
--color-blue-50: oklch(0.97 0.02 250);
--color-blue-100: oklch(0.93 0.04 250);
--color-blue-200: oklch(0.87 0.08 250);
--color-blue-300: oklch(0.78 0.12 250);
--color-blue-400: oklch(0.70 0.15 250);
--color-blue-500: oklch(0.60 0.18 250);
--color-blue-600: oklch(0.50 0.18 250);
--color-blue-700: oklch(0.40 0.16 250);
--color-blue-800: oklch(0.32 0.12 250);
--color-blue-900: oklch(0.25 0.08 250);
}
Opacity with OKLCH
Use the slash syntax for alpha transparency:
--color-overlay: oklch(0 0 0 / 0.5); /* 50% black */
--color-glass: oklch(1 0 0 / 0.1); /* 10% white */
In utility classes: bg-primary/50 applies 50% opacity to the primary color.
Container Queries
Tailwind v4 supports native container queries. Style children based on the parent container's size rather than the viewport.
Setting Up a Container
<div class="@container">
<div class="flex flex-col @md:flex-row @lg:grid @lg:grid-cols-3">
<!-- Layout changes based on container width, not viewport -->
</div>
</div>
Named Containers
Use named containers when nesting multiple query contexts:
<div class="@container/sidebar">
<div class="@container/card">
<p class="text-sm @md/card:text-base @lg/sidebar:text-lg">
Responds to different containers
</p>
</div>
</div>
Container Query Breakpoints
| Prefix | Min Width |
|---|---|
@xs | 320px |
@sm | 384px |
@md | 448px |
@lg | 512px |
@xl | 576px |
@2xl | 672px |
When to Use Container Queries vs Media Queries
- Container queries (
@container): Component-scoped responsiveness. Use for reusable components that appear in different layout contexts (sidebar, main content, modal). - Media queries (breakpoint prefixes): Page-level layout. Use for top-level page structure and navigation.
Responsive Design
Apply the mobile-first approach: base styles target mobile, then layer on breakpoint modifiers for larger screens.
Breakpoint Modifiers
<div class="
grid grid-cols-1
sm:grid-cols-2
lg:grid-cols-3
xl:grid-cols-4
">
<!-- 1 col mobile, 2 col small, 3 col large, 4 col extra large -->
</div>
Responsive Typography
<h1 class="text-2xl md:text-4xl lg:text-5xl font-bold">
Scales with viewport
</h1>
Responsive Spacing
<section class="px-4 md:px-8 lg:px-16 py-8 md:py-12 lg:py-20">
<!-- Breathing room increases on larger screens -->
</section>
Rules
- Start with the smallest screen. Add complexity for larger breakpoints.
- Use
min-widthlogic (default). Avoidmax-widthoverrides. - Test at each breakpoint boundary, not just at common device sizes.
- Combine responsive breakpoints with container queries for robust component design.
Dark Mode
Tailwind provides the dark: variant for dark mode styling.
Strategy: prefers-color-scheme (Default)
Automatically follows the OS/browser preference:
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
Adapts to OS preference
</div>
Strategy: Class-Based Toggle
For manual toggle control, configure the dark mode strategy and apply a dark class to a parent element (typically <html>):
<html class="dark">
<body class="bg-white dark:bg-gray-900">
<!-- Dark mode is active -->
</body>
</html>
Dark Mode Patterns
<!-- Borders that work in both modes -->
<div class="border border-gray-200 dark:border-gray-700">
<!-- Hover states for both modes -->
<button class="
bg-blue-500 hover:bg-blue-600
dark:bg-blue-600 dark:hover:bg-blue-500
">
<!-- Shadows that adapt -->
<div class="shadow-lg dark:shadow-none dark:ring-1 dark:ring-gray-700">
Rules
- Define both light and dark values for every visual property (background, text, border, shadow).
- Avoid pure black (
#000) in dark mode. Use dark grays likegray-900orgray-950. - Increase contrast for text in dark mode. Light gray on dark gray is easier to read than white on black.
- Test both modes during development, not as an afterthought.
Component Extraction
When to Extract
Extract a component when the same combination of utility classes appears three or more times. Before that threshold, duplication is acceptable.
Extraction Methods (In Order of Preference)
- Framework components (React, Vue, Svelte): Extract markup and classes into a reusable component. This is the primary method.
function Badge({ children, variant = 'default' }: BadgeProps) {
const variants = {
default: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200',
success: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
error: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
};
return (
<span class={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${variants[variant]}`}>
{children}
</span>
);
}
@applydirective: Use sparingly for base-layer elements that cannot be componentized (e.g., global prose styles, form resets).
@layer components {
.btn-primary {
@apply inline-flex items-center px-4 py-2 rounded-lg
bg-primary text-white font-medium
hover:bg-primary/90 focus:outline-none focus:ring-2
focus:ring-primary/50 transition-colors;
}
}
Class Ordering Convention
Order utility classes by category for readability:
- Layout (display, position, flex/grid)
- Sizing (width, height, padding, margin)
- Typography (font, text, leading)
- Visual (background, border, shadow, ring)
- Interactive (hover, focus, transition, cursor)
- Responsive/Dark (sm:, md:, dark:)
Anti-Patterns
Do Not Use @apply for Everything
@apply defeats the purpose of utility-first CSS. Use it only for cases where component extraction is impossible (global styles, third-party library overrides).
Do Not Create Single-Use Utility Classes
If a class is used in only one place, inline the utilities. Custom CSS classes add indirection without reuse benefit.
Do Not Fight Tailwind with Custom CSS
If Tailwind provides a utility for the property, use it. Adding custom CSS alongside Tailwind creates two styling systems and causes specificity conflicts.
Do Not Use Arbitrary Values When a Token Exists
<!-- Bad: arbitrary value when a token exists -->
<div class="p-[1rem]">
<!-- Good: use the spacing scale -->
<div class="p-4">
Reserve arbitrary values ([...] syntax) for one-off values that do not belong in the design system.
Performance
Oxide Engine (v4)
Tailwind v4 uses the Oxide engine, a Rust-based compiler that is significantly faster than the v3 PostCSS-based engine. Benefits:
- Automatic content detection: No
contentarray configuration needed. Tailwind scans the project automatically. - Faster builds: 5-10x faster full builds, 100x faster incremental builds.
- Smaller output: Improved dead-code elimination.
Production Optimization
- No manual purge configuration is needed in v4.
- Use
@layerto ensure custom styles are properly treeshaken. - Avoid dynamically constructing class names (e.g.,
`text-${color}-500`). Use complete class names so the compiler can detect them.
Dynamic Class Names
// Bad: dynamic construction, undetectable by compiler
const color = `text-${status}-500`;
// Good: map to complete class names
const statusColors = {
success: 'text-green-500',
error: 'text-red-500',
warning: 'text-yellow-500',
} as const;
const color = statusColors[status];
Quick Reference
| Concept | Approach |
|---|---|
| Configuration | @theme directive in CSS, not tailwind.config.js |
| Entry point | @import "tailwindcss" |
| Colors | OKLCH format for perceptual uniformity |
| Responsive | Mobile-first with sm:, md:, lg: prefixes |
| Component responsive | Container queries with @container and @md: |
| Dark mode | dark: variant, both modes always |
| Extraction | Framework components first, @apply as last resort |
| Dynamic classes | Complete strings only, never concatenated |
| Performance | Oxide engine handles detection and purging |
Dependencies
None -- this is a foundation skill.
Used By
| Skill | Reason |
|---|---|
nextjs-patterns | Default styling approach in Next.js projects; co-located with components |