All Skills

Use when writing, reviewing, or refactoring Tailwind CSS code. Covers tailwind, tailwindcss, utility classes, responsive design, dark mode, CSS, frontend, className, tw-merge, clsx, color system, typography, spacing, borders, rings, outlines, state variants, hover, focus, focus-visible, group-hover, data attributes, container queries, breakpoints, mobile-first, text-pretty, text-balance, opacity modifiers, @theme directive, @utility, Tailwind v4 CSS-first configuration, and common anti-patterns.

R
$npx skills add peixotorms/odinlayer-skills --skill tailwind-guidelines

Tailwind CSS Guidelines

Overview

Foundational Tailwind CSS guidelines for utility-first styling. Covers class composition, responsive design, dark mode, color systems, typography, spacing, state variants, and Tailwind v4 changes. Prioritize utility classes over custom CSS, compose with clsx/tw-merge, and follow mobile-first responsive patterns.


Utility-First Methodology

RuleDetail
Default to utilitiesWrite className="mt-4 text-sm font-medium" before reaching for custom CSS
Custom CSS only for complex selectorsAnimations, ::before/::after, or styles utilities cannot express
Never use @apply in componentsDefeats utility-first purpose; use clsx/tw-merge composition instead
@apply only in base layerAcceptable for resetting third-party styles or global typography
One semantic class per componentExtract via component abstraction, not CSS abstraction

Class Composition

ToolPurposeWhen to Use
clsxConditional class joiningToggling classes based on props/state
tw-mergeTailwind-aware merge with conflict resolutionOverriding default styles in component variants
clsx + tw-mergeCombined via cn() helperStandard pattern for component libraries
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

function cn(...inputs: ClassValue[]): string {
  return twMerge(clsx(inputs));
}

// Usage: last class wins for conflicts
cn("px-4 py-2 bg-blue-500", isActive && "bg-blue-700", className)
PatternCorrectIncorrect
Conditional classescn("text-sm", isLarge && "text-lg")String interpolation: `text-${size}`
Dynamic valuesUse style prop or CSS variablestext-[${dynamicValue}px]
Component variantscn(baseStyles, variants[variant])Deeply nested ternaries
Override defaultscn("px-4", className) via tw-mergeManual class deduplication

Responsive Design

Mobile-first: base styles apply to all screens, breakpoint prefixes apply at that width and above.

BreakpointMin WidthTypical Target
(none)0pxMobile (default)
sm:640pxLarge phones, small tablets
md:768pxTablets
lg:1024pxLaptops
xl:1280pxDesktops
2xl:1536pxLarge screens

Common Responsive Patterns

PatternClassesEffect
Stack to rowflex flex-col md:flex-rowColumn on mobile, row on desktop
Column countgrid grid-cols-1 md:grid-cols-2 lg:grid-cols-3Responsive grid columns
Hide on mobilehidden md:blockVisible only at md and above
Show on mobile onlyblock md:hiddenVisible only below md
Responsive texttext-sm md:text-base lg:text-lgScale text with screen
Responsive paddingpx-4 md:px-6 lg:px-8Increase padding with screen
Full to constrainedw-full max-w-md mx-autoFull mobile, centered container on desktop
Responsive gapgap-4 md:gap-6 lg:gap-8Increase spacing with screen

Container Queries

RuleDetail
Use @container for component-level responsivenessWhen component lives in varying-width contexts
Apply @container on parentParent gets the container class
Query with @sm:, @md:, etc.Breakpoints relative to container, not viewport
<div class="@container">
  <div class="flex flex-col @md:flex-row">...</div>
</div>

Dark Mode

RuleDetail
Use dark: variant for every colorPair every light color with its dark counterpart
v4: Use @custom-variant@custom-variant dark (&:where(.dark, .dark *)) for class-based toggle
v3: Set strategy in configdarkMode: 'class' (toggle) or 'media' (system preference)
Test both modesEvery component must be visually verified in both modes
Use semantic tokensPrefer CSS variables over hardcoded dark/light pairs for theming

Color Pairing Strategy

ElementLightDark
Page backgroundbg-whitedark:bg-gray-950
Card/surfacebg-white or bg-gray-50dark:bg-gray-900
Primary texttext-gray-900dark:text-white
Secondary texttext-gray-600dark:text-gray-400
Muted texttext-gray-500dark:text-gray-500
Bordersborder-gray-200dark:border-gray-700
Subtle bordersborder-gray-900/10dark:border-white/10
Input backgroundbg-whitedark:bg-white/5
Hover surfacehover:bg-gray-50dark:hover:bg-gray-800

Opacity-Based Dark Mode Colors

PatternUse Case
bg-white/5Subtle elevated surface in dark mode
bg-white/10Hover state overlay in dark mode
border-white/10Subtle border in dark mode
ring-white/15Decorative ring in dark mode
text-white/70De-emphasized text in dark mode

Accessibility

RuleDetail
forced-colors: variantOverride styles when user has forced-colors enabled
Maintain 4.5:1 contrast ratioFor body text in both modes
Maintain 3:1 contrast ratioFor large text and UI components
Test with prefers-contrast: moreEnsure readability in high-contrast mode

Color System

Color Hierarchy

RoleDefault ShadeUsage
Primary actionindigo-600 / dark:indigo-500Buttons, links, active states
Primary hoverindigo-500 / dark:indigo-400Hover on primary actions
Heading textgray-900 / dark:whitePage titles, section headings
Body textgray-600 / dark:gray-300Paragraphs, descriptions
Muted/captiongray-500 / dark:gray-400Timestamps, helper text
Placeholdergray-400 / dark:gray-500Input placeholders
Successgreen-600 / dark:green-400Confirmation, valid states
Warningyellow-600 / dark:yellow-500Caution, attention
Dangerred-600 / dark:red-500Errors, destructive actions

Opacity Modifiers

SyntaxEffect
bg-red-500/10Red background at 10% opacity (subtle tint)
bg-indigo-600/90Slightly transparent primary
text-gray-900/80Slightly muted text
border-gray-900/10Very subtle border

CSS Variable Theming

<!-- Inline theme overrides -->
<button class="bg-[var(--btn-bg)] text-[var(--btn-text)]"
        style="--btn-bg: var(--color-indigo-600); --btn-text: white;">
  Submit
</button>

<!-- Tailwind v4: @theme for custom tokens -->
Theming ruleDetail
Use CSS variables for brand colorsAllows runtime theme switching
Keep utility classes for standard colorsUse variables only when dynamic theming is needed
Arbitrary value syntax for variablesbg-[var(--color-brand)] or bg-(--color-brand) in v4

Typography

Text Size Scale

ClassSizeDefault Line HeightCommon Pairing
text-xs0.75rem1remCaptions, badges
text-sm0.875rem1.25remSecondary text, labels
text-base1rem1.5remBody text
text-lg1.125rem1.75remSubheadings
text-xl1.25rem1.75remSection headings
text-2xl1.5rem2remPage titles
text-3xl1.875rem2.25remHero subheadings
text-4xl2.25rem2.5remHero headings

Line Height Slash Syntax (v4)

SyntaxEffect
text-sm/6text-sm with line-height: 1.5rem
text-base/7text-base with line-height: 1.75rem
text-xl/8text-xl with line-height: 2rem
text-sm/[17px]Custom arbitrary line height

Font Weight Pairing

ElementWeightClass
HeadingsSemibold or boldfont-semibold or font-bold
BodyNormalfont-normal (default)
LabelsMediumfont-medium
CaptionsNormal or mediumfont-normal or font-medium
EmphasisSemiboldfont-semibold

Text Wrapping

ClassUse Case
text-prettyAvoids orphan words at end of paragraphs (preferred for body text)
text-balanceEvenly balanced line lengths (preferred for headings)
truncateSingle line with ellipsis overflow
line-clamp-3Multi-line truncation (3 lines)

Responsive Text

<h1 class="text-2xl font-bold tracking-tight sm:text-3xl lg:text-4xl">
  Page Title
</h1>
<p class="text-sm/6 text-gray-600 md:text-base/7">
  Description text that scales with viewport.
</p>

Spacing and Sizing

Spacing Scale Consistency

SpacingValueCommon Use
10.25rem (4px)Tight icon gaps
1.50.375rem (6px)Small vertical padding
20.5rem (8px)Compact element padding
30.75rem (12px)Standard small padding
41rem (16px)Standard padding and gap
61.5rem (24px)Section spacing
82rem (32px)Large section spacing
102.5rem (40px)Content block separation
123rem (48px)Major section separation
164rem (64px)Page-level spacing

Gap vs Space

UtilityUse When
gap-*Flex/grid layouts (preferred, handles wrapping correctly)
space-y-*Vertical stack of direct children, no flex/grid
space-x-*Horizontal stack of direct children, no flex/grid

Prefer gap over space-* in flex/grid contexts. space-* uses margins and breaks with flex-wrap.

Common Padding Pairs

ContextHorizontalVerticalCombined
Small buttonpx-2.5py-1.5px-2.5 py-1.5
Standard buttonpx-3py-2px-3 py-2
Large buttonpx-4py-2.5px-4 py-2.5
Cardpx-4 or px-6py-4 or py-5p-4 or px-6 py-5
Input fieldpx-3py-1.5 or py-2px-3 py-1.5
Sectionpx-4 sm:px-6 lg:px-8py-12 sm:py-16Responsive both axes

Negative Margins

PatternUse Case
-mx-4Bleed content to edge of padded parent
-mt-1Pull element up for optical alignment
-ml-pxOverlap 1px borders between siblings

Borders, Rings, and Outlines

When to Use Each

UtilityPurposeTypical Use
borderStructural boundaryCard edges, input fields, dividers
ringDecorative/state highlightFocus states, selection indicator
outlineAccessibility focus indicatorKeyboard focus visibility

Focus Patterns

PatternClassesUse Case
Standard focus ringfocus:outline-2 focus:outline-offset-2 focus:outline-indigo-600Buttons, links
Input focusfocus:ring-2 focus:ring-inset focus:ring-indigo-600 or focus:outline-2 focus:outline-indigo-600Form fields
Focus-visible onlyfocus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600Interactive elements (keyboard only)
No focus ringNever remove without replacementAccessibility violation

Subtle Borders with Opacity

PatternClasses
Subtle card borderborder border-gray-900/10 dark:border-white/10
Separatorborder-t border-gray-900/5
Elevated surfacering-1 ring-gray-900/5 dark:ring-white/5

Inset Rings

PatternClassesUse Case
Inset input ringring-1 ring-inset ring-gray-300Default input border
Inset focus ringring-2 ring-inset ring-indigo-600Focused input
Inset error ringring-2 ring-inset ring-red-500Validation error

State Variants

Standard States

VariantApplies WhenCommon Use
hover:Mouse overBackground/text color change
focus:Element focused (any method)Outline/ring for inputs
focus-visible:Keyboard focus onlyOutline for buttons/links
active:Being clicked/pressedScale down, darken
disabled:Element disabledOpacity reduction, cursor change
visited:Link visitedSubtle color change

Disabled Pattern

<button class="bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50
               disabled:cursor-not-allowed disabled:hover:bg-indigo-600">
  Submit
</button>

Group and Peer States

PatternTriggerClasses
Group hoverParent hoveredParent: group, child: group-hover:text-white
Group focusParent focusedParent: group, child: group-focus:ring-2
Group data stateParent has data attrParent: group, child: group-data-[open]:rotate-180
Named groupsMultiple nested groupsParent: group/item, child: group-hover/item:text-white
Peer checkedSibling checkedSibling: peer, target: peer-checked:bg-indigo-600
Peer focusSibling focusedSibling: peer, target: peer-focus:ring-2
Peer invalidSibling invalidSibling: peer, target: peer-invalid:text-red-500

Data Attribute Styling

PatternClasses
Toggle checkeddata-[checked]:bg-indigo-600
Open statedata-[open]:bg-gray-50
Active tabdata-[selected]:border-indigo-500 data-[selected]:text-indigo-600
State text colordata-[disabled]:text-gray-400
Headless UI integrationdata-[open]:rotate-180 on disclosure chevron

Tailwind v4 Changes

Changev3v4
Configurationtailwind.config.jsCSS-first: @theme in CSS file
Custom utilitiesPlugin API in config@utility directive in CSS
Color opacitybg-red-500/50Same syntax, now also bg-red-500/[0.5]
Line heighttext-sm leading-6text-sm/6 (slash syntax)
CSS variablestheme() functionvar(--color-*) native access
Import@tailwind base/components/utilities@import "tailwindcss"
Arbitrary properties[--my-var:value]Same, plus (--my-var) shorthand

@theme Directive

@import "tailwindcss";

@theme {
  --color-brand: #4f46e5;
  --color-brand-light: #818cf8;
  --font-display: "Inter", sans-serif;
  --breakpoint-3xl: 1920px;
}

@utility Directive

@utility scrollbar-hidden {
  scrollbar-width: none;
  &::-webkit-scrollbar {
    display: none;
  }
}

/* Usage: class="scrollbar-hidden" */

@custom-variant Directive

/* Class-based dark mode toggle */
@custom-variant dark (&:where(.dark, .dark *));

/* Custom theme variants */
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));

@variant Directive (Apply Variants in Custom CSS)

.my-element {
  background: white;
  @variant dark {
    background: black;
  }
}

@source Directive (Content Detection)

@import "tailwindcss";
/* Include external library templates */
@source "../node_modules/@my-company/ui-lib";
/* Exclude legacy code */
@source not "./src/legacy";
/* Safelist specific utilities */
@source inline("underline");
@source inline("{hover:,}bg-red-{50,{100..900..100},950}");

Built-in Container Queries (No Plugin)

<div class="@container">
  <div class="grid grid-cols-1 @sm:grid-cols-3 @lg:grid-cols-4">...</div>
</div>
<!-- Named containers, max-width, ranges -->
<div class="@container/main">
  <div class="@sm:@max-md:grid-cols-2">...</div>
</div>

v4 New Features

FeatureSyntaxUse Case
Dynamic spacingw-17, pr-29, grid-cols-15Any numeric value without config
Gradient renamebg-linear-to-r (was bg-gradient-to-r)Linear gradients
Gradient anglesbg-linear-45Arbitrary gradient angles
Radial/conicbg-radial-*, bg-conic-*Radial and conic gradients
OKLCH colorsDefault paletteWider gamut, more vivid
3D transformsrotate-x-*, rotate-y-*, perspective-*3D effects
Not variantnot-hover:opacity-100Negation
In variantin-data-open:blockImplicit group (ancestor match)
Nth variantnth-3:bg-gray-50Child targeting
Starting stylestarting:opacity-0Entry animations (@starting-style)
Inert variantinert:opacity-50Non-interactive elements

v4.1 Features

FeatureSyntaxUse Case
Text shadowstext-shadow-sm, text-shadow-lgText readability on images
Text shadow colortext-shadow-sky-300/50Colored text glow
Masksmask-b-from-50%, mask-radial-*Image fade/reveal effects
Colored drop shadowsdrop-shadow-cyan-500/50Colored SVG/element shadows
Overflow wrapwrap-break-word, wrap-anywhereLong word handling
Input devicepointer-fine:, pointer-coarse:Mouse vs touch adaptation
Safe alignmentjustify-center-safe, items-center-safeAuto-fix overflow alignment
User validationuser-valid:, user-invalid:Post-interaction form states
No-JS fallbacknoscript:blockStyles when JS disabled

v4 Migration Checklist

ActionDetail
Replace config fileMove theme tokens to @theme in CSS
Update imports@import "tailwindcss" replaces three @tailwind directives
Test slash syntaxtext-sm/6 replaces text-sm leading-6
Audit pluginsMove custom utilities to @utility, variants to @custom-variant
Check color referencestheme() calls become var(--color-*)
Rename gradientsbg-gradient-to-rbg-linear-to-r
Content detectionRemove content array, use @source for non-auto paths
Container queriesRemove @tailwindcss/container-queries plugin, use built-in @container
Run upgrade toolnpx @tailwindcss/upgrade for automated migration

Common Anti-Patterns

Anti-PatternWhy It Is WrongCorrect Approach
@apply in component filesDefeats utility-first; hides styles from scanningUse cn() or direct utility classes
Dynamic class construction: `text-${color}-500`Tailwind cannot scan dynamic strings; classes get purgedUse complete class names: cn(colorMap[color])
Inline style for Tailwind-expressible valuesBypasses design system, no responsive/state variantsUse utility classes or arbitrary values w-[72px]
Nesting Tailwind in CSS filesCreates specificity issues and maintenance burdenUse utilities in markup, CSS only for base styles
Hardcoded colors without dark modeBroken in dark mode, inconsistent themingAlways pair: bg-white dark:bg-gray-950
Using !important via ! prefixSpecificity hack that masks structural issuesFix class order or use tw-merge for overrides
Redundant classes: flex flex-rowflex-row is the default flex directionJust use flex
Missing focus-visible on interactive elementsKeyboard users cannot see focus indicatorAlways add focus-visible styles
Using space-* with flex-wrapMargins from space-* break wrapped layoutsUse gap-* instead
Deeply nested arbitrary values: [calc(100%-var(--x))]Unreadable, hard to maintainExtract to CSS variable or custom utility
Using pixel values everywhere: w-[347px]Breaks responsiveness and spacing consistencyUse Tailwind scale values: w-80, max-w-sm
Not using tw-merge for component overridesLater classes do not reliably override earlier onesWrap with cn() using tw-merge
Applying text-* color to SVGtext-* sets CSS color, SVGs need fill/strokeUse fill-current or stroke-current with text-*
Forgetting responsive prefixes on layoutLayout breaks on different screen sizesAlways test and apply sm:, md:, lg: as needed
Using sm: to target mobilesm: means 640px+, not "small screens"Unprefixed = mobile; sm: = tablet+
Stacking conflicting utilities: grid flexLater in stylesheet wins, not later in class stringUse conditional: isGrid ? "grid" : "flex"
Arbitrary values when token existsBypasses design system consistencyUse w-80 not w-[320px] when token matches

MCP Component Library

The frontend-components MCP server provides browsable component libraries for reference and reuse:

FrameworkIDWhat it provides
HyperUIhyperui481 pure HTML/Tailwind components (application, marketing, neobrutalism)
DaisyUIdaisyui65 component class references with examples
FlyonUIflyonui49 CSS components + 24 JS plugins
HeadlessUI Reactheadlessui-react38 accessible React component examples
HeadlessUI Vueheadlessui-vue30 accessible Vue component examples

Key tools: list_frameworks, list_components(framework), get_component(framework, ...), search_components(query), get_component_by_path(path)

Use search_components(query: "keyword") to find components across all libraries, or filter by framework.