tailwind-css-coding
Applies consistent Tailwind CSS patterns and prevents common styling antipatterns.
Install
mkdir -p .claude/skills/tailwind-css-coding && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12774" && unzip -o skill.zip -d .claude/skills/tailwind-css-coding && rm skill.zipInstalls to .claude/skills/tailwind-css-coding
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Apply when writing or editing Tailwind CSS classes in any template or component file. Behavioral corrections for dynamic styling, class composition, responsive design, dark mode, interaction states, accessibility, and common antipatterns. Project conventions always override these defaults.Key capabilities
- →Apply Tailwind CSS classes in template files
- →Correct dynamic styling issues
- →Ensure safe class composition
- →Implement responsive design with Tailwind
- →Handle dark mode and interaction states
How it works
This skill applies and corrects Tailwind CSS class usage by enforcing conventions for dynamic styling, class composition, responsive design, and interaction states.
Inputs & outputs
When to use tailwind-css-coding
- →Fix Tailwind class composition
- →Refactor dynamic styles
- →Ensure accessibility in UI components
About this skill
Tailwind CSS Coding
Match the project's existing conventions. When uncertain, read 2-3 existing components to infer the local style. Check package.json for the tailwindcss version. v4 signals: @tailwindcss/postcss or @tailwindcss/vite in deps, @import "tailwindcss" in CSS, @theme {} blocks. v3 signals: tailwind.config.js with module.exports, @tailwind base; directives, autoprefixer as separate dep. These defaults apply only when the project has no established convention.
Never rules
These are unconditional. They prevent broken builds, invisible bugs, and inaccessible UI regardless of project style.
- Never construct class names dynamically -- Tailwind's compiler scans source files as plain text with regex. It never executes code. Template literals, string concatenation, and interpolation produce classes the compiler cannot find. Use lookup maps of complete static strings.
// Wrong: compiler cannot extract "bg-red-500" from this
const cls = `bg-${color}-500`;
// Correct: every class is a complete static string
const bgMap = {
red: "bg-red-500",
blue: "bg-blue-500",
} as const;
const cls = bgMap[color];
- Never use template literal concatenation for class composition -- CSS source order determines which class wins when two utilities target the same property, not HTML attribute order.
p-4 p-6is unpredictable. Usecn()(clsx + tailwind-merge) to merge classes safely.
// Wrong: conflicting padding, last-in-source wins (not last-in-string)
className={`p-4 ${isLarge ? "p-6" : ""}`}
// Correct: tailwind-merge resolves conflicts deterministically
import { cn } from "@/lib/utils";
className={cn("p-4", isLarge && "p-6")}
-
Never use arbitrary values when a design token exists --
p-[16px]isp-4.bg-[#3b82f6]isbg-blue-500.w-[100%]isw-full.text-[14px]istext-sm. Arbitrary values bypass the design system and create inconsistency. Likewise, prefer semantic tokens over raw palette colors when the design system defines them (bg-primary, notbg-blue-500) -- see shadcn-ui-coding. -
Never omit interaction states on interactive elements -- every button and link needs
hover:,focus-visible:, anddisabled:states. Addtransition-colorsfor smooth feedback. In v4: addcursor-pointerexplicitly -- Preflight no longer sets it on buttons.
// Wrong: no interaction feedback
<button className="bg-blue-500 text-white px-4 py-2 rounded">Save</button>
// Correct: full interaction states (v4: include cursor-pointer)
<button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors cursor-pointer">
Save
</button>
-
Never use
@applyfor patterns extractable to components -- extract a React/Vue/Svelte component instead.@applyis only for third-party library overrides, CMS/Markdown HTML, and non-component template languages. -
Never leave colors unthemed when the project supports dark mode -- prefer semantic tokens/CSS variables, which theme both modes in one declaration with no
dark:variants needed (see Dark mode below); with raw palette utilities, everybg-,text-, andborder-color needs adark:counterpart. Projects without dark mode need neither. -
Never use
sm:thinking it means "small screens" --sm:means 640px AND ABOVE. Unprefixed utilities apply to all screens (mobile-first). Write base styles for mobile, then layer breakpoints upward.
<!-- Wrong mental model: "sm means small phones" -->
<div class="hidden sm:block">Only on small screens</div>
<!-- Correct: sm:block means "show at 640px and above" -->
<div class="block sm:hidden">Mobile only</div>
<div class="hidden sm:block">Desktop only</div>
-
Never hallucinate class names -- common fakes:
flex-center(useflex items-center justify-center),text-bold(usefont-bold),bg-grey-500(American spelling:bg-gray-500),d-flex(Bootstrap, not Tailwind). -
Never output conflicting utilities --
p-4 p-6is unpredictable. One value per CSS property. Don't redundantly set defaults (flex flex-row,flex flex-nowrap). -
Never forget accessibility -- use
sr-onlyfor icon-only button labels,focus:not-sr-onlyfor skip links. Every interactive element needs visible focus indication viafocus-visible:.
Dynamic styling
For conditional classes, use cn() (clsx + tailwind-merge). For truly dynamic values that cannot be enumerated (user-set colors, computed positions), use inline style props -- these bypass the compiler entirely and always work.
// Conditional classes: cn()
<div className={cn("rounded p-4", isActive && "ring-2 ring-blue-500")} />
// Truly dynamic: inline style
<div className="rounded p-4" style={{ backgroundColor: user.brandColor }} />
For variant-driven styling, use a lookup map with complete static strings:
const sizeClasses = {
sm: "px-2 py-1 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg",
} as const;
function Button({ size = "md", className, ...props }: ButtonProps) {
return <button className={cn(sizeClasses[size], className)} {...props} />;
}
For components with 2+ variant dimensions, consider cva from class-variance-authority.
When the compiler must see classes that only appear in dynamic data (CMS content, database values), safelist them. In v4: @source inline("bg-{red,blue}-500") -- brace expansion generates each class; use one pattern per directive. In v3: safelist array in tailwind.config.js.
Class composition
The cn() helper combines clsx (conditional joining) with tailwind-merge (conflict resolution). Standard setup:
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Use cn() whenever merging external className props with internal defaults -- raw concatenation silently breaks when both sides set the same property.
// Wrong: parent's p-6 may or may not override internal p-4
function Card({ className }: { className?: string }) {
return <div className={`rounded bg-white p-4 ${className}`} />;
}
// Correct: tailwind-merge ensures parent overrides win
function Card({ className }: { className?: string }) {
return <div className={cn("rounded bg-white p-4", className)} />;
}
Responsive design
Mobile-first: write base styles for the smallest screen, then add breakpoints upward. Write breakpoint variants in ascending order (sm: -> md: -> lg: -> xl: -> 2xl:) -- a readability convention (what the Prettier plugin enforces); order within the class string has zero CSS effect. Never skip to lg: without considering the gap.
<!-- Single column on mobile, two on tablet, three on desktop -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
In v4: container queries are built-in (no plugin). Use @container for component-scoped responsive design:
// Parent declares a container
<div className="@container">
{/* Child responds to container width, not viewport */}
<div className="flex flex-col @md:flex-row @lg:grid @lg:grid-cols-3 gap-4">
{children}
</div>
</div>
Dark mode
Cover every visible color. A component with bg-white text-gray-900 needs dark:bg-gray-900 dark:text-white. Missing a single dark: variant causes unreadable text or invisible elements.
Better approach -- CSS variable theming. Define colors once, switch palettes:
/* v4: @theme block */
@theme {
--color-surface: #ffffff;
--color-on-surface: #111827;
}
@custom-variant dark (&:where(.dark, .dark *));
.dark {
--color-surface: #111827;
--color-on-surface: #f9fafb;
}
Then use bg-surface text-on-surface everywhere -- no dark: variants needed per component.
Interaction states
Minimum states for buttons: hover:, focus-visible:, disabled:, transition-colors -- full button example under Never rules. Minimum for inputs: focus:, disabled:, placeholder:. Minimum for links: hover:, focus-visible:.
Complete input pattern (semantic tokens -- both modes themed with no dark: variants):
<input className={cn(
"w-full rounded border px-3 py-2 transition-colors",
"border-input bg-background text-foreground",
"placeholder:text-muted-foreground",
"focus:border-ring focus:outline-none focus:ring-1 focus:ring-ring",
"disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-50",
)} />
In v4: hover: only fires on hover-capable devices (@media (hover: hover)). Touch-only devices skip hover styles entirely -- don't rely on hover for critical information.
Accessibility
Icon-only buttons need screen reader text:
<button className="p-2 rounded hover:bg-gray-100">
<SearchIcon className="h-5 w-5" aria-hidden="true" />
<span className="sr-only">Search</span>
</button>
Skip links use sr-only that becomes visible on focus:
<a href="#main" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-white focus:text-black focus:rounded">
Skip to main content
</a>
Every focusable element needs a visible focus indicator. focus-visible: is preferred over focus: -- it only shows for keyboard navigation, not mouse clicks.
Ensure sufficient color contrast on interactive states. A hover:bg-blue-700 on bg-blue-600 is barely visible -- test both light and dark modes.
v4 configuration
In v4, configuration lives in CSS, not JavaScript. Entry point is @import "tailwindcss". Autoprefixer is built-in (don't add it separately).
@import "tailwindcss";
@theme {
--font-display: "Inter", sans-serif;
--color-brand: #4f46e5;
--breakpoint-3xl: 1920px;
}
@utility card {
background: var(--color-sur
---
*Content truncated.*
When not to use it
- →When constructing class names dynamically with template literals
- →When using template literal concatenation for class composition
- →When using arbitrary values where a design token exists
Limitations
- →Project conventions always override these defaults
- →It warns against dynamic class string construction
- →It requires using cn() for safe class merging
How it compares
This skill provides specific 'never rules' and best practices for Tailwind CSS coding, preventing common anti-patterns and ensuring consistent, maintainable styling, unlike generic CSS application.
Compared to similar skills
tailwind-css-coding side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| tailwind-css-coding (this skill) | 0 | 5mo | No flags | Intermediate |
| frontend-design | 481 | 2mo | No flags | Advanced |
| screenshot-to-code | 204 | 2mo | No flags | Beginner |
| web-artifacts-builder | 49 | 3mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by Dynokostya
View all by Dynokostya →You might also like
frontend-design
anthropics
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
screenshot-to-code
OneWave-AI
Convert UI screenshots into working HTML/CSS/React/Vue code. Detects design patterns, components, and generates responsive layouts. Use this when users provide screenshots of websites, apps, or UI designs and want code implementation.
web-artifacts-builder
anthropics
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
svelte-ui-design
XIYO
ALWAYS use this skill for ANY Svelte component styling, design, or UI work. Svelte 5 UI design system using Tailwind CSS 4, Skeleton Labs design tokens/presets/Tailwind Components, and Bits UI headless components. Covers class composition, color systems, interactive components, forms, overlays, and all visual design.
skeleton-svelte
martinemde
Use this skill when working with Skeleton UI components in Svelte projects. It provides guidelines for Skeleton's component composition pattern, theme-aware color system, design presets, and layout patterns. Trigger when building UI components, styling elements, creating layouts, or working with Skeleton-specific features in Svelte 5 and SvelteKit 2+ projects.
responsive-design
wshobson
Implement modern responsive layouts using container queries, fluid typography, CSS Grid, and mobile-first breakpoint strategies. Use when building adaptive interfaces, implementing fluid layouts, or creating component-level responsive behavior.