Claude Skill

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.

LLM Mart · 0 points · 8 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download iliaal-whetstone-distillery_generated-skills_tailwind-css-bccd699.zip · 6 KB
Part of iliaal/whetstone — 62 skills

Install

skills CLI npx skills add https://github.com/iliaal/whetstone/tree/master/distillery/generated-skills/tailwind-css
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install iliaal-whetstone@llmmart
Git 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

  • 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.

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

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.

No comments yet.

Reviews (0)

No reviews yet.

Related