tailwind-css
Tailwind CSS v4 patterns: CSS-first config, utility classes, component variants, v3 migration. Use when styling with Tailwind, configuring @theme tokens, using tailwind-variants/CVA, migrating v3 to v4, or fixing Tailwind styles and dark mode.
Install
npx skills add https://github.com/iliaal/whetstone/tree/master/distillery/generated-skills/tailwind-css
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install iliaal-whetstone@llmmart
git clone https://github.com/iliaal/whetstone.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole iliaal/whetstone collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Tailwind CSS v4
CSS-First Configuration
v4 eliminates tailwind.config.ts. All configuration lives in CSS.
| Directive | Purpose |
|---|---|
@import "tailwindcss" |
Entry point (replaces @tailwind base/components/utilities) |
@theme { } |
Define/extend design tokens — auto-generates utility classes |
@theme inline { } |
Map CSS variables to Tailwind utilities without generating new vars |
@theme static { } |
Define tokens that don't generate utilities |
@utility name { } |
Create custom utilities (replaces @layer components + @apply) |
@custom-variant name (selector) |
Define custom variants |
@import "tailwindcss";
@theme {
--color-brand: oklch(0.72 0.11 178);
--font-display: "Inter", sans-serif;
--animate-fade-in: fade-in 0.2s ease-out;
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
}
@custom-variant dark (&:where(.dark, .dark *));
Tokens defined with @theme become utilities automatically: --color-brand produces bg-brand, text-brand, border-brand.
v3 to v4 Breaking Changes
| v3 | v4 | Notes |
|---|---|---|
tailwind.config.ts |
@theme in CSS |
Delete config file |
@tailwind base/components/utilities |
@import "tailwindcss" |
Single import |
darkMode: "class" |
@custom-variant dark (...) |
CSS-only |
bg-gradient-to-r |
bg-linear-to-r |
Also: bg-radial, bg-conic |
bg-opacity-60 |
bg-red-500/60 |
All *-opacity-* removed |
rounded-md (6px) |
rounded (6px) |
Radius scale shifted down |
min-h-screen |
min-h-dvh |
dvh handles mobile browser chrome |
w-6 h-6 |
size-6 |
Size shorthand for equal w/h |
space-x-4 |
gap-4 |
Gap handles flex/grid wrapping correctly |
text-base leading-7 |
text-base/7 |
Inline line-height modifier |
require("tailwindcss-animate") |
tw-animate-css |
CSS-only animations |
forwardRef |
ref as prop |
React 19 change (not Tailwind, but co-occurs) |
Coding Rules
gapoverspace-x/space-y— gap handles wrapping; space-* breaks on wrapsize-*overw-* h-*— for equal dimensionsmin-h-dvhovermin-h-screen— dvh accounts for mobile browser chrome- Opacity modifier (
bg-black/50) —*-opacity-*utilities are removed in v4 - Design tokens over arbitrary values — check
@themebefore using[#hex] - Never construct classes dynamically —
text-${color}-500won't be detected; use complete class names @utilityover@applywith@layer—@applyon@layerclasses fails in v4
Class Merging
Use cn() combining clsx + tailwind-merge for conditional/dynamic classes. Use plain strings for static className attributes.
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }
// Static: plain string
<button className="rounded-lg px-4 py-2 font-medium bg-blue-600">
// Conditional: use cn()
<button className={cn("rounded-lg px-4 py-2", isActive ? "bg-blue-600" : "bg-gray-700")} />
Component Variants
Use tailwind-variants (tv()) for type-safe variant components. Alternative: class-variance-authority (cva()).
import { tv } from "tailwind-variants";
const button = tv({
base: "rounded-lg px-4 py-2 font-medium transition-colors",
variants: {
color: { primary: "bg-blue-600 text-white", secondary: "bg-gray-200 text-gray-800" },
size: { sm: "text-sm px-3 py-1", md: "text-base", lg: "text-lg px-6 py-3" },
},
defaultVariants: { color: "primary", size: "md" },
});
See tailwind-variants patterns for slots, composition, and responsive variants.
Common Errors
| Symptom | Fix |
|---|---|
bg-primary doesn't work |
Add @theme inline { --color-primary: var(--primary); } |
| Colors all black/white | Double hsl() wrapping — use var(--color) not hsl(var(--color)) |
@apply fails on custom class |
Use @utility instead of @layer components |
| Build fails after migration | Delete tailwind.config.ts |
| Animations broken | Replace tailwindcss-animate with tw-animate-css |
.dark { @theme { } } fails |
v4 does not support nested @theme — use :root/.dark CSS vars mapped via @theme inline |
Dark Mode (v4 Pattern)
:root { --background: hsl(0 0% 100%); --foreground: hsl(222 84% 4.9%); }
.dark { --background: hsl(222 84% 4.9%); --foreground: hsl(210 40% 98%); }
@theme inline { --color-background: var(--background); --color-foreground: var(--foreground); }
Semantic classes (bg-background, text-foreground) auto-switch — no dark: variants needed for themed colors.
References
- Component patterns — tailwind-variants slots, CVA, compound components
- Layout patterns — grid areas, container queries, z-index management, fluid typography
Files (whetstone)
-
references
-
component-patterns.md 3.4 KB
# Component Patterns ## tailwind-variants (tv) Type-safe component variants with slots, composition, and responsive support. ### Slots API ```typescript import { tv } from "tailwind-variants"; const card = tv({ slots: { base: "rounded-lg border shadow-sm", header: "flex flex-col space-y-1.5 p-6", title: "text-2xl font-semibold leading-none tracking-tight", content: "p-6 pt-0", footer: "flex items-center p-6 pt-0", }, variants: { elevated: { true: { base: "shadow-lg border-0" } }, }, }); const { base, header, title, content, footer } = card({ elevated: true }); ``` ### Composition ```typescript const baseButton = tv({ base: "rounded-lg font-medium transition-colors" }); const iconButton = tv({ extend: baseButton, base: "inline-flex items-center justify-center", variants: { size: { sm: "size-8", md: "size-10", lg: "size-12" }, }, }); ``` ### Responsive Variants ```typescript const grid = tv({ base: "grid gap-4", variants: { cols: { 1: "grid-cols-1", 2: "grid-cols-2", 3: "grid-cols-3", 4: "grid-cols-4" }, }, responsiveVariants: ["sm", "md", "lg"], }); // Usage: <div className={grid({ cols: { initial: 1, sm: 2, lg: 4 } })} /> ``` ## CVA (class-variance-authority) Alternative to tailwind-variants — simpler API, no slots. ```typescript import { cva, type VariantProps } from "class-variance-authority"; const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", outline: "border border-border bg-background hover:bg-accent", ghost: "hover:bg-accent hover:text-accent-foreground", }, size: { sm: "h-9 rounded-md px-3", default: "h-10 px-4 py-2", lg: "h-11 rounded-md px-8", icon: "size-10", }, }, defaultVariants: { variant: "default", size: "default" }, } ); export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {} export function Button({ className, variant, size, ...props }: ButtonProps) { return <button className={cn(buttonVariants({ variant, size, className }))} {...props} />; } ``` ## Compound Components (React 19) React 19 passes ref as a regular prop — no `forwardRef` needed. ```typescript export function Card({ className, ref, ...props }: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) { return <div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />; } export function CardHeader({ className, ref, ...props }: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) { return <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />; } ``` ## ESLint Integration Use `eslint-plugin-better-tailwindcss` for v4 class validation: | Rule | Purpose | |------|---------| | `no-conflicting-classes` | Detect classes that override each other | | `no-unknown-classes` | Flag classes not registered with Tailwind | | `enforce-shorthand-classes` | `size-6` not `w-6 h-6`; `p-6` not `px-6 py-6` | | `no-deprecated-classes` | Catch v3 class names used in v4 projects | -
layout-patterns.md 3.6 KB
# Layout Patterns ## Grid Template Areas Define reusable grid areas with `@utility`: ```css @utility grid-areas-dashboard { grid-template-areas: "header header header" "nav main aside" "nav footer footer"; } @utility area-header { grid-area: header; } @utility area-nav { grid-area: nav; } @utility area-main { grid-area: main; } ``` ```html <div class="grid grid-areas-dashboard grid-cols-[200px_1fr_250px] grid-rows-[60px_1fr_40px]"> <header class="area-header">Header</header> <nav class="area-nav">Nav</nav> <main class="area-main">Content</main> </div> ``` ## Auto-Responsive Grids ```html <!-- Auto-fit: cards stretch to fill --> <div class="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-6"> <!-- Auto-fill: maintains track size, leaves empty space --> <div class="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-4"> <!-- Safe minimum (handles container < minmax min) --> <div class="grid grid-cols-[repeat(auto-fill,minmax(min(100%,300px),1fr))] gap-4"> ``` ## Z-Index Management Define a z-index scale in `@theme` tokens instead of arbitrary numbers: ```css @theme { --z-dropdown: 100; --z-sticky: 200; --z-fixed: 300; --z-modal-backdrop: 400; --z-modal: 500; --z-popover: 600; --z-tooltip: 700; --z-toast: 800; } ``` Reference with `z-(--z-modal)` syntax — never use `z-[9999]`. ## Container Queries Component-level responsiveness independent of viewport. ```css @plugin "@tailwindcss/container-queries"; ``` ```html <article class="@container"> <div class="flex flex-col @sm:flex-row gap-4"> <img class="w-full @sm:w-32 @lg:w-48 aspect-video @sm:aspect-square object-cover" /> <div class="flex-1 min-w-0"> <h3 class="text-base @md:text-lg @lg:text-xl font-semibold truncate">Title</h3> <p class="text-sm @md:text-base line-clamp-2 @lg:line-clamp-3">Description</p> </div> </div> </article> ``` | Use Container Queries | Use Viewport Queries | |----------------------|---------------------| | Reusable components | Page-level layouts | | Sidebar widgets | Navigation bars | | Card grids | Hero sections | | Embedded/CMS content | Full-width sections | Named containers scope queries: `@container/sidebar` with `@lg/sidebar:flex-row`. ## Fluid Typography Eliminate breakpoint jumps with `clamp()`: ```css @theme { --text-fluid-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); --text-fluid-xl: clamp(1.25rem, 1rem + 1.25vw, 1.5rem); --text-fluid-3xl: clamp(1.875rem, 1.2rem + 3.375vw, 2.5rem); } ``` Always combine `vw` with `rem` — pure `vw` breaks when users zoom (WCAG violation). ## Custom Utilities ```css @utility scrollbar-none { scrollbar-width: none; -ms-overflow-style: none; } @utility text-gradient { @apply bg-linear-to-r from-primary to-accent bg-clip-text text-transparent; } ``` ## Native CSS Animations (v4) Define keyframes inside `@theme` and reference with `--animate-*` tokens: ```css @theme { --animate-slide-up: slide-up 0.3s ease-out; @keyframes slide-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } } ``` Use `@starting-style` for entry animations on native popovers/dialogs: ```css [popover]:popover-open { opacity: 1; transform: scale(1); } @starting-style { [popover]:popover-open { opacity: 0; transform: scale(0.95); } } ``` Respect motion preferences: `motion-safe:animate-bounce motion-reduce:animate-none`. ## Safe Area Handling (Notched Devices) ```css @utility safe-area-pt { padding-top: env(safe-area-inset-top); } @utility safe-area-pb { padding-bottom: env(safe-area-inset-bottom); } ``` Apply to fixed headers/footers on mobile.
-
-
manifest.json 1.3 KB
{ "query": "tailwind-css", "search_queries": ["tailwind css", "tailwindcss"], "generated": "2026-03-07", "token_count": 3471, "sources": [ { "id": "josiahsiegel/claude-plugin-marketplace/tailwindcss-advanced-layouts", "installs": 2024, "sha1": "cd1ecb96706bcb3eb7488a86d4d2cf7b1ec8f901" }, { "id": "giuseppe-trisciuoglio/developer-kit/tailwind-css-patterns", "installs": 1617, "sha1": "c6d404e65071f45ae621d78921e676da76fa8024" }, { "id": "josiahsiegel/claude-plugin-marketplace/tailwindcss-animations", "installs": 758, "sha1": "a609af0123ba23ebb1f50b694891cc55744a0dc0" }, { "id": "josiahsiegel/claude-plugin-marketplace/tailwindcss-mobile-first", "installs": 700, "sha1": "e254df52561eab10468845da97d99f0420cfc13b" }, { "id": "hairyf/skills/tailwindcss", "installs": 628, "sha1": "64f74030817905e193b6fe317a08f2c64311c445" }, { "id": "bobmatnyc/claude-mpm-skills/tailwind-css", "installs": 583, "sha1": "6b76508e3241efb5d701b39a874fba07e2071bac" }, { "id": "josiahsiegel/claude-plugin-marketplace/tailwindcss-advanced-design-systems", "installs": 204, "sha1": "78d13a00fad7110d216a774bdd42c894c12c1dcc" }, { "id": "martinholovsky/claude-skills-generator/tailwindcss", "installs": 190, "sha1": "0e0d7fde9a99592d4570d6ac939964b097eaeeb2" } ] } -
SKILL.md 5.2 KB
--- name: tailwind-css description: >- Tailwind CSS v4 patterns: CSS-first config, utility classes, component variants, v3 migration. Use when styling with Tailwind, configuring @theme tokens, using tailwind-variants/CVA, migrating v3 to v4, or fixing Tailwind styles and dark mode. --- # Tailwind CSS v4 ## CSS-First Configuration v4 eliminates `tailwind.config.ts`. All configuration lives in CSS. | Directive | Purpose | |-----------|---------| | `@import "tailwindcss"` | Entry point (replaces `@tailwind base/components/utilities`) | | `@theme { }` | Define/extend design tokens — auto-generates utility classes | | `@theme inline { }` | Map CSS variables to Tailwind utilities without generating new vars | | `@theme static { }` | Define tokens that don't generate utilities | | `@utility name { }` | Create custom utilities (replaces `@layer components` + `@apply`) | | `@custom-variant name (selector)` | Define custom variants | ```css @import "tailwindcss"; @theme { --color-brand: oklch(0.72 0.11 178); --font-display: "Inter", sans-serif; --animate-fade-in: fade-in 0.2s ease-out; @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } } @custom-variant dark (&:where(.dark, .dark *)); ``` Tokens defined with `@theme` become utilities automatically: `--color-brand` produces `bg-brand`, `text-brand`, `border-brand`. ## v3 to v4 Breaking Changes | v3 | v4 | Notes | |----|-----|-------| | `tailwind.config.ts` | `@theme` in CSS | Delete config file | | `@tailwind base/components/utilities` | `@import "tailwindcss"` | Single import | | `darkMode: "class"` | `@custom-variant dark (...)` | CSS-only | | `bg-gradient-to-r` | `bg-linear-to-r` | Also: `bg-radial`, `bg-conic` | | `bg-opacity-60` | `bg-red-500/60` | All `*-opacity-*` removed | | `rounded-md` (6px) | `rounded` (6px) | Radius scale shifted down | | `min-h-screen` | `min-h-dvh` | `dvh` handles mobile browser chrome | | `w-6 h-6` | `size-6` | Size shorthand for equal w/h | | `space-x-4` | `gap-4` | Gap handles flex/grid wrapping correctly | | `text-base leading-7` | `text-base/7` | Inline line-height modifier | | `require("tailwindcss-animate")` | `tw-animate-css` | CSS-only animations | | `forwardRef` | `ref` as prop | React 19 change (not Tailwind, but co-occurs) | ## Coding Rules - **`gap` over `space-x`/`space-y`** — gap handles wrapping; space-* breaks on wrap - **`size-*` over `w-* h-*`** — for equal dimensions - **`min-h-dvh` over `min-h-screen`** — dvh accounts for mobile browser chrome - **Opacity modifier** (`bg-black/50`) — `*-opacity-*` utilities are removed in v4 - **Design tokens over arbitrary values** — check `@theme` before using `[#hex]` - **Never construct classes dynamically** — `text-${color}-500` won't be detected; use complete class names - **`@utility` over `@apply` with `@layer`** — `@apply` on `@layer` classes fails in v4 ## Class Merging Use `cn()` combining `clsx` + `tailwind-merge` for conditional/dynamic classes. Use plain strings for static `className` attributes. ```typescript import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` ```typescript // Static: plain string <button className="rounded-lg px-4 py-2 font-medium bg-blue-600"> // Conditional: use cn() <button className={cn("rounded-lg px-4 py-2", isActive ? "bg-blue-600" : "bg-gray-700")} /> ``` ## Component Variants Use `tailwind-variants` (`tv()`) for type-safe variant components. Alternative: `class-variance-authority` (`cva()`). ```typescript import { tv } from "tailwind-variants"; const button = tv({ base: "rounded-lg px-4 py-2 font-medium transition-colors", variants: { color: { primary: "bg-blue-600 text-white", secondary: "bg-gray-200 text-gray-800" }, size: { sm: "text-sm px-3 py-1", md: "text-base", lg: "text-lg px-6 py-3" }, }, defaultVariants: { color: "primary", size: "md" }, }); ``` See [tailwind-variants patterns](references/component-patterns.md) for slots, composition, and responsive variants. ## Common Errors | Symptom | Fix | |---------|-----| | `bg-primary` doesn't work | Add `@theme inline { --color-primary: var(--primary); }` | | Colors all black/white | Double `hsl()` wrapping — use `var(--color)` not `hsl(var(--color))` | | `@apply` fails on custom class | Use `@utility` instead of `@layer components` | | Build fails after migration | Delete `tailwind.config.ts` | | Animations broken | Replace `tailwindcss-animate` with `tw-animate-css` | | `.dark { @theme { } }` fails | v4 does not support nested `@theme` — use `:root`/`.dark` CSS vars mapped via `@theme inline` | ## Dark Mode (v4 Pattern) ```css :root { --background: hsl(0 0% 100%); --foreground: hsl(222 84% 4.9%); } .dark { --background: hsl(222 84% 4.9%); --foreground: hsl(210 40% 98%); } @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); } ``` Semantic classes (`bg-background`, `text-foreground`) auto-switch — no `dark:` variants needed for themed colors. ## References - [Component patterns](references/component-patterns.md) — tailwind-variants slots, CVA, compound components - [Layout patterns](references/layout-patterns.md) — grid areas, container queries, z-index management, fluid typography
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.