Tailwind CSS v4

SkillDev tools

v4 eliminates tailwind.config.ts. All configuration lives in CSS.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Tailwind CSS v4 skill

About this capability

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.

What this skill tells your AI

The instructions your AI receives, as published by iliaal/whetstone in distillery/generated-skills/tailwind-css/SKILL.md and read by ahel’s review.

CSS-First Configuration

v4 eliminates tailwind.config.ts. All configuration lives in CSS.

DirectivePurpose
@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

v3v4Notes
tailwind.config.ts@theme in CSSDelete config file
@tailwind base/components/utilities@import "tailwindcss"Single import
darkMode: "class"@custom-variant dark (...)CSS-only
bg-gradient-to-rbg-linear-to-rAlso: bg-radial, bg-conic
bg-opacity-60bg-red-500/60All *-opacity-* removed
rounded-md (6px)rounded (6px)Radius scale shifted down
min-h-screenmin-h-dvhdvh handles mobile browser chrome
w-6 h-6size-6Size shorthand for equal w/h
space-x-4gap-4Gap handles flex/grid wrapping correctly
text-base leading-7text-base/7Inline line-height modifier
require("tailwindcss-animate")tw-animate-cssCSS-only animations
forwardRefref as propReact 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 dynamicallytext-${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

SymptomFix
bg-primary doesn't workAdd @theme inline { --color-primary: var(--primary); }
Colors all black/whiteDouble hsl() wrapping — use var(--color) not hsl(var(--color))
@apply fails on custom classUse @utility instead of @layer components
Build fails after migrationDelete tailwind.config.ts
Animations brokenReplace tailwindcss-animate with tw-animate-css
.dark { @theme { } } failsv4 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

Signals

GitHub stars
33
Forks
3
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
tailwind-css-iliaal
Source
github.com/iliaal/whetstone