Agent skill
tailwind-theming
Tailwind CSS theming system for this Next.js application. Covers CSS variables, semantic tokens, dark mode, buildSectionClasses, and theme build process. Use this skill when implementing UI styling or working with theme configurations.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/tailwind-theming
SKILL.md
Tailwind Theming Skill
Patterns for working with the Tailwind CSS v4 theming system, CSS variables, and semantic tokens.
Architecture Overview
TAILWIND THEMING (v4):
Source Files:
contents/themes/{THEME}/
├── styles/
│ ├── globals.css # CSS variables (light + dark)
│ └── components.css # Component-specific styles
├── config/
│ └── theme.config.ts # Theme settings
└── public/
└── theme-assets/ # Images, fonts
Generated Files (DO NOT EDIT):
├── core/theme-styles.css # Compiled theme CSS
└── public/theme/ # Copied assets
Build Script:
└── core/scripts/build/theme.mjs
When to Use This Skill
- Styling components with semantic tokens
- Understanding CSS variable system
- Implementing dark mode support
- Using buildSectionClasses in blocks
- Working with background color presets
CSS Variables (Design Tokens)
Core Semantic Colors
/* Light Mode (:root) */
:root {
/* Backgrounds */
--background: oklch(1 0 0); /* Page background */
--card: oklch(1 0 0); /* Card surfaces */
--popover: oklch(1 0 0); /* Dropdowns, popovers */
/* Foregrounds (text) */
--foreground: oklch(0.145 0 0); /* Primary text */
--card-foreground: oklch(0.145 0 0); /* Card text */
--popover-foreground: oklch(0.145 0 0); /* Popover text */
/* Interactive */
--primary: oklch(0.205 0 0); /* Primary buttons/links */
--primary-foreground: oklch(0.985 0 0); /* Text on primary */
--secondary: oklch(0.97 0 0); /* Secondary elements */
--secondary-foreground: oklch(0.205 0 0);
/* States */
--muted: oklch(0.97 0 0); /* Muted backgrounds */
--muted-foreground: oklch(0.556 0 0); /* Muted/placeholder text */
--accent: oklch(0.97 0 0); /* Accent highlights */
--accent-foreground: oklch(0.205 0 0);
/* Feedback */
--destructive: oklch(0.577 0.245 27.325); /* Error/danger */
--destructive-foreground: oklch(1 0 0);
/* Borders & Inputs */
--border: oklch(0.922 0 0); /* Border color */
--input: oklch(0.922 0 0); /* Input borders */
--ring: oklch(0.708 0 0); /* Focus rings */
/* Radius */
--radius: 0.5rem; /* Base border radius */
}
Dark Mode Variables
/* Dark Mode (.dark selector) */
.dark {
/* Inverted - dark backgrounds, light text */
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
/* Destructive stays similar but adjusted for dark */
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.985 0 0);
}
Sidebar Variables
:root {
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
Chart Colors
:root {
--chart-1: oklch(0.81 0.1 252);
--chart-2: oklch(0.62 0.19 260);
--chart-3: oklch(0.55 0.22 263);
--chart-4: oklch(0.49 0.22 264);
--chart-5: oklch(0.42 0.18 266);
}
Semantic Token Usage
Correct Usage
// ✅ ALWAYS use semantic tokens
<div className="bg-background text-foreground">
<div className="bg-card text-card-foreground">
<p className="text-muted-foreground">
<button className="bg-primary text-primary-foreground">
<span className="text-destructive">
// ✅ Borders and inputs
<div className="border border-border">
<input className="border-input focus:ring-ring">
// ✅ Radius
<div className="rounded-lg"> {/* Uses --radius */}
<div className="rounded-sm"> {/* calc(--radius - 4px) */}
Forbidden Patterns
// ❌ NEVER hardcode colors
<div className="bg-white text-black">
<div className="bg-gray-100">
<p className="text-gray-500">
<button className="bg-blue-500 text-white">
// ❌ NEVER use arbitrary color values
<div className="bg-[#ffffff]">
<p className="text-[rgb(107,114,128)]">
Background Color Presets (Blocks)
Available Values
type BackgroundColor =
| 'transparent' // bg-transparent
| 'white' // bg-white
| 'gray-50' // bg-gray-50
| 'gray-100' // bg-gray-100
| 'gray-900' // bg-gray-900 text-white
| 'primary' // bg-primary text-primary-foreground
| 'primary-light' // bg-primary/10
| 'primary-dark' // bg-primary-dark text-white
| 'secondary' // bg-secondary text-secondary-foreground
| 'accent' // bg-accent text-accent-foreground
getBackgroundClasses Function
// core/types/blocks.ts
export function getBackgroundClasses(backgroundColor?: BackgroundColor): string {
const bgMap: Record<BackgroundColor, string> = {
transparent: 'bg-transparent',
white: 'bg-white',
'gray-50': 'bg-gray-50',
'gray-100': 'bg-gray-100',
'gray-900': 'bg-gray-900 text-white',
primary: 'bg-primary text-primary-foreground',
'primary-light': 'bg-primary/10',
'primary-dark': 'bg-primary-dark text-white',
secondary: 'bg-secondary text-secondary-foreground',
accent: 'bg-accent text-accent-foreground',
}
return bgMap[backgroundColor || 'transparent']
}
buildSectionClasses Helper
Function Signature
// core/types/blocks.ts
export function buildSectionClasses(
baseClasses: string,
props: Partial<{
backgroundColor?: BackgroundColor
className?: string
}>
): string
Usage in Blocks
import { buildSectionClasses } from '@/core/types/blocks'
import type { HeroProps } from './schema'
export function HeroBlock({
title,
content,
backgroundColor,
className,
id,
}: HeroProps) {
// Combine base classes + background + custom
const sectionClasses = buildSectionClasses(
'relative flex min-h-[600px] items-center justify-center px-4 py-20',
{ backgroundColor, className }
)
return (
<section id={id} className={sectionClasses}>
{/* ... */}
</section>
)
}
Output Examples
buildSectionClasses('py-16 px-4', {})
// "py-16 px-4"
buildSectionClasses('py-16 px-4', { backgroundColor: 'gray-900' })
// "py-16 px-4 bg-gray-900 text-white"
buildSectionClasses('py-16 px-4', {
backgroundColor: 'primary',
className: 'custom-class'
})
// "py-16 px-4 bg-primary text-primary-foreground custom-class"
Dark Mode Implementation
Architecture
1. CSS Variables → Define light/dark values in :root and .dark
2. @custom-variant → Tell Tailwind to use .dark class (NOT prefers-color-scheme)
3. next-themes → Manages .dark class on <html>
4. Tailwind → Reads CSS variables and applies dark: variants via class
CRITICAL: Tailwind v4 Dark Mode Configuration
By default, Tailwind v4 uses @media (prefers-color-scheme: dark) for dark: variants.
This causes a mismatch with next-themes which uses the .dark class on <html>.
Required in every theme's globals.css:
@import "tailwindcss";
/* REQUIRED: Use class selector instead of prefers-color-scheme */
@custom-variant dark (&:where(.dark, .dark *));
/* ... rest of imports */
Without this configuration:
- User sets theme to "light" in app
- OS has dark mode enabled
- Tailwind ignores the HTML class and uses OS preference
dark:bg-slate-800applies even though app should be light
With this configuration:
- Tailwind respects the
.darkclass from next-themes - App theme setting is honored regardless of OS preference
Theme Provider Setup
// core/providers/theme-provider.tsx
import { ThemeProvider as NextThemeProvider } from 'next-themes'
export function ThemeProvider({ children }) {
return (
<NextThemeProvider
attribute="class" // Adds .dark to <html>
defaultTheme={defaultTheme} // light | dark | system
enableSystem // Respects OS preference
disableTransitionOnChange // No color flash
>
{children}
</NextThemeProvider>
)
}
Theme Toggle
import { useTheme } from 'next-themes'
function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
)
}
Default Theme Resolution
// Priority order:
// 1. User preference (stored in DB: uiPreferences.theme)
// 2. Theme config default (theme.config.ts → defaultMode)
// 3. System preference
// 4. Fallback: 'light'
Theme Build Process
Build Commands
# Rebuild theme CSS
pnpm theme:build
# Or manually
node core/scripts/build/theme.mjs
# Watch mode (auto-rebuild)
node core/scripts/build/theme.mjs --watch
What Build Does
- Reads
NEXT_PUBLIC_ACTIVE_THEMEfrom.env - Finds
contents/themes/{theme}/styles/globals.css - Finds
contents/themes/{theme}/styles/components.css - Copies assets from
contents/themes/{theme}/public/→public/theme/ - Copies block thumbnails →
public/theme/blocks/{slug}/ - Generates
core/theme-styles.css
Generated File
/*
* Generated Theme CSS
* Theme: default
* Build time: 2025-12-30T12:00:00.000Z
* DO NOT EDIT - Auto-generated by theme.mjs
*/
/* Contents of globals.css */
:root {
--background: ...
}
/* Contents of components.css */
.custom-component {
...
}
@theme Mapping (Tailwind v4)
/* In globals.css - Maps CSS vars to Tailwind */
@theme inline {
/* Colors */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-primary: var(--primary);
/* ... */
/* Fonts */
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
/* Radius */
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
/* Shadows */
--shadow-sm: var(--shadow-sm);
--shadow-md: var(--shadow-md);
}
Theme File Structure
contents/themes/default/
├── config/
│ ├── theme.config.ts # Theme metadata
│ ├── app.config.ts # App settings
│ ├── permissions.config.ts # Role permissions
│ └── plans.config.ts # Billing plans
├── styles/
│ ├── globals.css # CSS variables (light + dark)
│ └── components.css # Custom component styles
├── blocks/
│ └── {slug}/
│ └── thumbnail.png # Block preview image
├── public/
│ └── theme-assets/ # Static assets
└── messages/
├── en.json # English translations
└── es.json # Spanish translations
Anti-Patterns
// NEVER: Hardcode colors
<div className="bg-white text-gray-900">
// CORRECT: Use semantic tokens
<div className="bg-background text-foreground">
// NEVER: Use arbitrary colors
<div className="bg-[#1a1a1a]">
// CORRECT: Define in CSS variables if needed
<div className="bg-background">
// NEVER: Hardcode dark mode classes
<div className="bg-white dark:bg-gray-900">
// CORRECT: Use variables that auto-switch
<div className="bg-background"> {/* Switches automatically */}
// NEVER: Skip buildSectionClasses in blocks
<section className={`py-16 ${backgroundColor === 'gray-900' ? 'bg-gray-900 text-white' : ''}`}>
// CORRECT: Use helper function
<section className={buildSectionClasses('py-16', { backgroundColor })}>
// NEVER: Modify generated CSS files
// core/theme-styles.css - DO NOT EDIT
// CORRECT: Modify source files and rebuild
// contents/themes/default/styles/globals.css
Checklist
Before finalizing theme implementation:
- Using semantic tokens (bg-background, not bg-white)
- No hardcoded colors
- Dark mode works automatically (variables switch)
- Blocks use buildSectionClasses helper
- Background color uses BackgroundColor type
- Theme rebuilt after CSS changes
- Assets copied to public/theme/
- No modifications to generated files
Related Skills
shadcn-components- Component styling patternspage-builder-blocks- Block styling with buildSectionClassesaccessibility- Color contrast requirements
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?