Skip to content
cccskills
FreePublished on GitHub

tailwind

Use when styling with Tailwind CSS v4 - @theme syntax, design token architecture, dark mode strategy, bundle size optimization, component-layer discipline, or migrating from v3

The English summary is being prepared. Showing the original description.

See how to install

Files included (4)

  • SKILL.md12.9 KB
  • references/migration-v4.md4.2 KB
  • references/theme.md5.7 KB
  • references/utilities-variants.md5.2 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Overview

Tailwind CSS v4 is a complete rewrite built on the Rust-based Oxide engine. Configuration moves from JavaScript to CSS via @theme.

Key v4 shifts:

  • Config: tailwind.config.js → CSS @theme block
  • Import: @tailwind base/components/utilities → @import "tailwindcss"
  • Dark mode: automatic via @media (prefers-color-scheme)
  • Content detection: automatic, no content array needed

Browser support: Safari 16.4+, Chrome 111+, Firefox 128+

Installation

Vite (Recommended)

npm install tailwindcss @tailwindcss/vite
// vite.config.js
import tailwindcss from "@tailwindcss/vite";

export default {
  plugins: [tailwindcss()],
};

PostCSS

npm install -D tailwindcss @tailwindcss/postcss
// postcss.config.js
export default {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

CLI

npm install -D @tailwindcss/cli
npx @tailwindcss/cli -i input.css -o output.css --watch

Basic Setup

/* input.css */
@import "tailwindcss";

/* Your custom styles and @theme block below */

That's it. No @tailwind base/components/utilities directives—they're gone.

Design Token Architecture (v4)

Single source of truth: The @theme block in your main CSS file defines all design tokens. Every color, spacing value, and font becomes a CSS variable.

Token Definition Pattern

@import "tailwindcss";

@theme {
  /* Replace, don't extend, the default palette */
  --color-brand: oklch(65% 0.25 250);
  --color-brand-dark: oklch(55% 0.25 250);
  --color-bg: oklch(98% 0.01 250);
  --color-surface: oklch(100% 0 250);
  --color-text: oklch(20% 0.02 250);
  --color-text-muted: oklch(50% 0.02 250);

  /* Spacing scale */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;

  /* Typography */
  --font-display: "Clash Display", sans-serif;
  --font-body: "Satoshi", system-ui, sans-serif;
}

Why Replace Instead of Extend

The default Tailwind palette is generic. Replacing it with your semantic tokens:

  • Prevents bg-blue-500 from leaking into a design that uses bg-brand-500
  • Makes theme changes a token edit, not a class sweep
  • Keeps the design system coherent

Failure mode: Hardcoding a hex in a component class:

/* BAD: This breaks the theme system */
.card {
  background-color: #3b82f6; /* Can't change via @theme */
}

Correct:

/* GOOD: Theme change is one token edit */
@theme {
  --color-card-bg: var(--color-surface);
}

.card {
  background-color: var(--color-card-bg);
}

Token-to-Component Mapping

Component styles reference tokens, not raw values:

@layer components {
  .btn {
    background-color: var(--color-brand);
    color: var(--color-surface);
    padding: var(--spacing-sm) var(--spacing-md);
    font-family: var(--font-display);
  }

  .btn:hover {
    background-color: var(--color-brand-dark);
  }
}

Result: Changing --color-brand in @theme updates every button site-wide. No search-and-replace.

The --color-* Namespace Rule

Any --color-* variable in @theme automatically generates utility classes:

@theme {
  --color-primary: oklch(60% 0.18 250);
}

Now bg-primary, text-primary, border-primary all work. The engine maps:

  • --color-{name} → {prop}-{name} utilities

Dark Mode Decision Guide

v4 defaults to @media (prefers-color-scheme)—no config needed. But product requirements dictate the right strategy.

Three Strategies

StrategyMechanismBest For
Media query@media (prefers-color-scheme: dark)Static sites, blogs, no user preference
Manual toggle.dark class on <html>Apps with user theme preference
Data attribute[data-theme="dark"] on <html>Multiple themes (light/dark/sepia)

Decision Logic

Does the product need user-chosen theme that survives reload?
├─ Yes → Use `.dark` class or `data-theme` attribute
│        Persist choice in localStorage
│        Sync with `<html class="dark">` or `<html data-theme="dark">`
│
└─ No → Media query is enough. Do nothing.

Manual Toggle Implementation

<!-- HTML -->
<html class="dark">
  <div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
    Content
  </div>
</html>
// JavaScript toggle
function toggleDark() {
  const html = document.documentElement;
  const isDark = html.classList.toggle("dark");
  localStorage.setItem("theme", isDark ? "dark" : "light");
}

// Restore on load
const saved = localStorage.getItem("theme");
if (saved === "dark") {
  document.documentElement.classList.add("dark");
}

Avoiding Flash-of-Wrong-Theme

On first paint, before JS runs, the page may flash the wrong theme. Fix:

<!-- Inline script before any CSS/JS -->
<script>
  const saved = localStorage.getItem("theme");
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
  if (saved === "dark" || (!saved && prefersDark)) {
    document.documentElement.classList.add("dark");
  }
</script>

Place this in <head> before any stylesheets.

Why Not dark: on Every Color

Adding dark: to every utility duplicates tokens:

<!-- BAD: Duplicates token definitions -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 border-gray-200 dark:border-gray-800">

Better: Define semantic tokens that invert automatically:

@theme {
  --color-bg: oklch(100% 0 250);
  --color-bg-dark: oklch(15% 0.02 250);
  --color-text: oklch(20% 0.02 250);
  --color-text-dark: oklch(90% 0.02 250);
}
<!-- Use semantic tokens, fewer dark: prefixes -->
<div class="bg-bg text-text">

Or use CSS color-scheme with automatic contrast:

@layer base {
  :root {
    color-scheme: light dark;
  }
}

Bundle Size and Content Detection

Content Detection in v4 (@source)

v4 automatically scans your project. No content array needed. But you can explicitly add sources:

@import "tailwindcss";

@source "../components/**/*.{js,ts,jsx,tsx,vue,svelte}";
@source "../pages/**/*.{js,ts,jsx,tsx}";

Rule: Content scanning must cover all template files, not just JS. If you use Blade, EJS, Handlebars, or PHP templates, add them:

@source "../views/**/*.blade.php";
@source "../templates/**/*.html";

Why Unused Utilities Are Tree-Shaken

The Oxide engine generates only the utilities you actually use. Unused classes are never emitted.

What inflates output:

  • Arbitrary values: bg-[#3b82f6] prevents some optimizations because each arbitrary value is unique
  • Icon libraries: SVG icons in HTML add bulk
  • Plugin CSS: Custom plugins that emit raw CSS (not utilities)
  • Preflight: The base reset (~15KB)

Measuring Bundle Size

# Build and measure
npx @tailwindcss/cli -i input.css -o output.css
wc -c output.css  # Byte count

# Compare with and without @source directives

Target: A typical v4 build is 10–30KB gzipped for a medium app.

Reducing Bundle Size

  1. Use semantic tokens instead of arbitrary values:

    <!-- BAD: Arbitrary value -->
    <div class="bg-[#3b82f6]">
    
    <!-- GOOD: Token -->
    <div class="bg-brand">
    
  2. Exclude unused plugin CSS:

    /* Don't import full plugins if you only need one utility */
    @plugin "@tailwindcss/typography"; /* Only if you need prose */
    
  3. Use @reference for component styles:

    <style>
    @reference "../app.css";
    /* Only what you @apply here */
    </style>
    

Component-Layer Discipline

When a utility pattern repeats, decide between three approaches:

1. Plain Class (Default)

@layer components {
  .btn {
    display: inline-flex;
    align-items: center;
    padding: var(--spacing-sm) var(--spacing-md);
    border-radius: 0.5rem;
    font-weight: 600;
  }
}

Use when: The pattern is used in 3+ places and has no variants.

2. @utility Directive (v4)

@utility btn {
  display: inline-flex;
  align-items: center;
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: 0.5rem;
  font-weight: 600;
}

Use when: You want the pattern to work with variants (hover:btn, dark:btn).

Failure in JSX-heavy codebases: @apply re-opens the specificity fight utilities were meant to end:

/* BAD: @apply in a component library */
.btn {
  @apply bg-blue-500 text-white px-4 py-2;
}

Why it fails:

  • The component CSS may load after Tailwind, overriding your utilities
  • Specificity becomes unpredictable
  • You're back to fighting CSS cascade instead of avoiding it

Correct: Define the component in @layer components with raw CSS, or use @utility if you need variant support.

3. Variant with @variant

@variant elevated {
  box-shadow: var(--shadow-card);
  background-color: var(--color-surface);
}

Use when: A state (like "elevated", "pressed", "selected") applies across multiple utilities.

Migration to v4 Checklist

Symptomv3v4Fix
Build fails, unknown directive@tailwind base@import "tailwindcss"Replace all @tailwind directives
Config changes ignoredtailwind.config.js@theme in CSSMove config to CSS @theme block
extend not workingtheme.extend in JSCSS variablesDefine variables directly in @theme
Custom utilities missing@layer utilities@utilityRewrite with @utility directive
Old config neededN/A@configAdd @config "../../tailwind.config.js" (legacy only)
Plugin not loadingplugins: [] in JS@pluginUse @plugin "@tailwindcss/typography"
corePlugins errorcorePlugins: []Not supportedRemove from config, use @theme flags
separator errorseparator: "_"Not supportedUse new arbitrary value syntax

One-line summary:

  • @tailwind base/components/utilities → @import "tailwindcss"
  • tailwind.config.js → CSS @theme block
  • extend → CSS variables in @theme
  • @apply → @utility (for variant support)
  • Plugins → @plugin directive

Framework Integration

Vue / Svelte Component Styles

In v4, styles in separate files don't see theme variables by default. Use @reference:

<template>
  <h1>Hello</h1>
</template>

<style>
@reference "../app.css";

h1 {
  @apply text-2xl font-bold text-red-500;
}
</style>

Next.js / Vite

No special config needed if using the official plugin:

// next.config.js or vite.config.js
import tailwindcss from "@tailwindcss/vite";

export default {
  plugins: [tailwindcss()],
};

Common Issues

Missing Classes After Build

  • Ensure @source directives cover all template files
  • Check that the CSS file with @theme is imported by your entry point

Dark Mode Not Applying

  • For manual toggle: add class="dark" to <html>, not <body>
  • For media: no config needed, just use dark: utilities

Custom Utilities Not Working

  • Use @utility directive, not @layer utilities
  • Ensure the CSS file with @theme is imported

Arbitrary Values Not Parsing

  • v3: bg-[--my-var]
  • v4: bg-(--my-var) (parentheses, not brackets)

Best Practices

Do:

  • Replace the default color palette with semantic tokens in @theme
  • Use @utility for reusable patterns that need variants
  • Let dark mode be automatic unless user preference is required
  • Use @source to explicitly scan non-JS templates
  • Define tokens once, reference everywhere via CSS variables

Don't:

  • Hardcode hex values in component CSS
  • Use @apply in JSX-heavy component libraries
  • Add dark: to every color utility
  • Create a tailwind.config.js for new projects
  • Use Sass/Less/Stylus—they don't work with v4

Deep Dives

Load these reference files for detailed information:

  • Theme Configuration — @theme directive syntax, design tokens, colors, spacing, breakpoints, animations — references/theme.md
  • Custom Utilities & Variants — @utility, @variant directives, utility classes reference — references/utilities-variants.md
  • Migration from v3 — Breaking changes, upgrade tool, new features, Preflight changes — references/migration-v4.md

References

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Use when building Python async HTTP services or clients with aiohttp - web server routing, middleware, WebSocket, SSE, streaming, client sessions, pytest-aiohttp testing, or troubleshooting SSL and timeout issues

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

Use when building GBA games with the BPCore Lua engine - entity, sprite and tilemap functions, SRAM save and load, link cable multiplayer protocol, camera and scrolling, or optimization patterns

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

celery

Free

Use when running background tasks with Celery - worker and broker configuration (Redis, RabbitMQ), task routing by name vs queue, chains/groups/chords, retry patterns (autoretry_for, retry_backoff), acks_late semantics, failure detection, and monitoring with Flower

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

django

Free

Use when building Django applications - security hardening, authentication and permissions, ORM optimization, PostgreSQL features, Django 6.0, migrations, testing, and ecosystem libraries

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

Use when customizing Django Admin - save_formset, get_search_results, formsets, queryset optimization, db_index, custom URLs

The English summary is being prepared. Showing the original description.

CodeAtCode/oss-ai-skills24Updated Oct 9, 2026

See all skills by CodeAtCode

Report a problem with this skill