| name | css-tailwind-styling |
| description | Expert guidance for writing clean, performant CSS and Tailwind CSS. Use when creating styles, designing components, optimizing performance, or establishing styling conventions. Covers modern CSS features, Tailwind utility patterns, responsive design, accessibility, and team collaboration standards. |
CSS and Tailwind CSS Styling Expert
Overview
This skill provides comprehensive best practices for writing maintainable, performant, and accessible styles using both traditional CSS and Tailwind CSS. It covers modern techniques, performance optimization, responsive design patterns, and team collaboration standards.
When to Use This Skill
- Creating or refactoring component styles
- Setting up a new project's styling architecture
- Optimizing CSS or Tailwind performance
- Establishing team styling conventions
- Reviewing code for style-related issues
- Implementing responsive designs
- Ensuring accessibility compliance
- Debugging style conflicts or specificity issues
Core Principles
General Styling Philosophy
- Maintainability First: Write styles that are easy to understand and modify
- Performance Conscious: Minimize bundle size and render-blocking
- Accessibility By Default: Ensure WCAG AA compliance (4.5:1 contrast ratio minimum)
- Mobile-First Responsive: Start with mobile and progressively enhance
- Consistency Over Cleverness: Establish and follow patterns
Version Detection & Decision Flow
Use this protocol before giving Tailwind-specific guidance.
1. Detect Tailwind Version (Dependency First)
Inspect package.json first:
tailwindcss major version 4 -> initial state: v4
tailwindcss major version 3 -> initial state: v3
- Missing/unclear dependency -> initial state:
unknown (continue with signal checks)
2. Cross-Check with Configuration and Style Signals
Use these signals to validate or refine the initial state:
- Prefer v4 when you find:
@import "tailwindcss", @theme, @utility, @custom-variant
- Prefer v3 when you find:
@tailwind base;, @tailwind components;, @tailwind utilities; with a config-driven setup
3. Resolve Final State
v4: dependency and signals align to v4
v3: dependency and signals align to v3
conflict: dependency and syntax/config signals disagree
unknown: insufficient evidence to identify a single version
4. Required Decision Behavior
v4: Provide v4 as Primary Path, include v3 as Alternate Path
v3: Provide v3 as Primary Path, include v4 as Alternate Path
conflict: Do not stop; provide both repair paths and a recommended path in Conflict Fix
unknown: Provide both paths, default recommendation is v4 for new projects
Tailwind CSS Best Practices
1. Prerequisites for Tailwind Projects
Prefer Tailwind when at least one of these is true:
- ✅ The team needs fast UI iteration with shared utility patterns
- ✅ The project already has reusable components or design tokens
- ✅ Consistent styling conventions are difficult to enforce with ad hoc CSS
Prefer traditional CSS (or CSS Modules) when the UI scope is small, mostly static,
or the team is not ready to maintain a shared utility vocabulary yet.
2. Class Ordering and Organization
Use the Concentric CSS ordering method for utility classes:
<div className="
relative z-10 // 1. Positioning
flex items-center // 2. Display & Box Model
w-full max-w-screen-lg mx-auto px-4 // 3. Sizing & Spacing
border border-gray-200 rounded-lg // 4. Borders
bg-white shadow-md // 5. Backgrounds
text-lg font-semibold text-gray-900 // 6. Typography
transition-all duration-200 // 7. Other
">
<div className="text-lg bg-white flex border w-full shadow-md px-4">
Automation: Always recommend the Prettier Plugin for Tailwind CSS:
npm install -D prettier prettier-plugin-tailwindcss
{
"plugins": ["prettier-plugin-tailwindcss"]
}
3. Minimize Utility Classes
<div className="ml-2 mr-2 pt-4 pb-4 pl-4 pr-4">
<div className="mx-2 py-4 px-4">
// ✅ BETTER: Combine where possible
<div className="mx-2 p-4">
// ❌ BAD: Unnecessary default values
<div className="block lg:flex flex-row justify-center">
// ✅ GOOD: Omit defaults (flex-row is default)
<div className="block lg:flex justify-center">
4. Responsive Design Prefixes
Use mobile-first defaults and add prefixes only for breakpoint-specific overrides:
<div className="flex flex-col justify-center lg:flex lg:flex-col lg:justify-center">
<div className="flex flex-col justify-center lg:flex-row lg:justify-between">
5. Component Abstraction Strategy
Priority: Components > @apply
.btn-primary {
@apply bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700;
}
function Button({ children, variant = 'primary' }) {
const variants = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white',
secondary: 'bg-gray-600 hover:bg-gray-700 text-white',
danger: 'bg-red-600 hover:bg-red-700 text-white',
};
return (
<button className={`px-4 py-2 rounded-lg font-medium transition-colors ${variants[variant]}`}>
{children}
</button>
);
}
When to use @apply:
- ✅ Truly duplicated utility patterns across multiple components
- ✅ Creating base resets or normalizations
- ❌ NOT for single-use component styles
- ❌ NOT chaining component classes (
.btn-blue { @apply btn; })
6. Tailwind Configuration (v3/v4)
Use version-appropriate configuration patterns and keep design tokens centralized.
v3 (config-first):
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./public/index.html',
],
theme: {
extend: {
colors: {
brand: {
primary: '#1DA1F2',
secondary: '#14171A',
accent: '#1DA1F2',
},
},
spacing: {
'128': '32rem',
'144': '36rem',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['Fira Code', 'monospace'],
},
},
},
plugins: [],
};
v4 (CSS-first):
@import "tailwindcss";
@theme {
--color-brand-primary: #1DA1F2;
--color-brand-secondary: #14171A;
--font-sans: Inter, system-ui, sans-serif;
}
@source "../packages/ui/src/**/*.{ts,tsx}";
Benefits for both versions:
- Team uses consistent tokens
- No random values scattered in code
- Single source of truth for design updates
7. Dynamic Classes - CRITICAL PATTERN
NEVER use string interpolation for class names:
<div className={`text-${color}-500`}>
<div className={`bg-${theme}-100 text-${theme}-900`}>
// ✅ GOOD: Complete class names
<div className={color === 'blue' ? 'text-blue-500' : 'text-red-500'}>
// ✅ BETTER: Object mapping
const colorClasses = {
blue: 'text-blue-500 bg-blue-50',
red: 'text-red-500 bg-red-50',
green: 'text-green-500 bg-green-50',
};
<div className={colorClasses[color]}>
// ✅ BEST: Use a library like clsx or classnames
import clsx from 'clsx';
<div className={clsx(
'px-4 py-2 rounded',
isActive && 'bg-blue-500 text-white',
isDisabled && 'opacity-50 cursor-not-allowed'
)}>
8. Style Variants Pattern
Define component variants explicitly rather than accepting arbitrary classes:
<Button className="bg-red-500" />
const Button = ({ variant = 'primary', size = 'md', children }) => {
const variants = {
primary: 'bg-blue-600 hover:bg-blue-700 text-white',
secondary: 'bg-gray-600 hover:bg-gray-700 text-white',
danger: 'bg-red-600 hover:bg-red-700 text-white',
ghost: 'bg-transparent hover:bg-gray-100 text-gray-700',
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
};
return (
<button className={`
rounded-lg font-medium transition-colors
${variants[variant]}
${sizes[size]}
`}>
{children}
</button>
);
};
9. Accessibility Requirements
Tailwind doesn't handle accessibility automatically. You must:
<button
className="bg-blue-500 text-white px-4 py-2 rounded"
aria-label="Submit form"
type="submit"
>
Submit
</button>
<div className="bg-gray-900 text-white"> // High contrast ✓
<div className="bg-gray-300 text-gray-400"> // Poor contrast ✗
// ✅ Focus states
<button className="
bg-blue-500 hover:bg-blue-600
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
">
// ✅ Screen reader only content
<span className="sr-only">Skip to main content</span>
10. Performance Optimization
module.exports = {
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./components/**/*.{js,jsx,ts,tsx}',
],
};
11. Team Collaboration
npm install -D eslint-plugin-tailwindcss
{
"extends": ["plugin:tailwindcss/recommended"],
"rules": {
"tailwindcss/classnames-order": "warn",
"tailwindcss/no-custom-classname": "warn",
"tailwindcss/no-contradicting-classname": "error"
}
}
12. Common Tailwind Pitfalls
Class Soup Problem:
<div className="px-4 py-2 bg-blue-500 text-white rounded-md shadow-md hover:bg-blue-600 transition duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-50">
<PrimaryButton />
Missing Accessibility:
<div className="cursor-pointer" onClick={handleClick}>Click</div>
<button className="cursor-pointer" onClick={handleClick}>Click</button>
Bundle Bloat:
module.exports = {
content: [],
}
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
}
Traditional CSS Best Practices
1. File Organization
styles/
├── base/
│ ├── reset.css # CSS reset or normalize
│ ├── typography.css # Font styles
│ └── variables.css # CSS custom properties
├── components/
│ ├── buttons.css
│ ├── cards.css
│ └── forms.css
├── layouts/
│ ├── grid.css
│ ├── header.css
│ └── footer.css
├── utilities/
│ └── helpers.css # Utility classes
└── main.css # Main entry point
2. Naming Conventions
BEM (Block Element Modifier) - Recommended:
.card {}
.card__header {}
.card__body {}
.card__footer {}
.card--featured {}
.card--compact {}
.card__header--large {}
SMACSS Alternative:
body, h1, p {}
.l-header {}
.l-sidebar {}
.l-main {}
.card {}
.button {}
.is-active {}
.is-hidden {}
.is-loading {}
.theme-dark {}
.theme-light {}
3. Property Ordering (Concentric CSS)
.element {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
background-image: url('...');
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
font-family: 'Inter', sans-serif;
font-size: 16px;
font-weight: 400;
line-height: 1.5;
color: #333;
text-align: center;
opacity: 1;
cursor: pointer;
transition: all 0.3s ease;
}
4. CSS Custom Properties (Variables)
:root {
--color-primary: #1DA1F2;
--color-secondary: #14171A;
--color-accent: #F91880;
--color-background: #FFFFFF;
--color-text: #0F1419;
--color-text-secondary: #536471;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--font-sans: 'Inter', system-ui, sans-serif;
--font-mono: 'Fira Code', monospace;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: #000000;
--color-text: #E7E9EA;
--color-text-secondary: #71767B;
}
}
.button {
background-color: var(--color-primary);
padding: var(--spacing-md);
border-radius: var(--radius-md);
font-family: var(--font-sans);
box-shadow: var(--shadow-md);
}
5. Selector Best Practices
header nav ul li a.active {}
.nav-link.is-active {}
article.main .content .sidebar p.intro {}
.sidebar-intro {}
div.card {}
p.description {}
.card {}
.description {}
Specificity Tips:
- Keep specificity as low as possible
- Prefer class selectors over ID selectors
- Avoid
!important (except for utility classes)
- Use one class name per element when possible
6. Modern Layout Techniques
Flexbox:
.flex-container {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
.flex-item {
flex: 1 1 300px;
}
CSS Grid:
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 24px;
}
.layout {
display: grid;
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
grid-template-columns: 250px 1fr 1fr;
gap: 20px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
Container Queries (2024):
.container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 500px) {
.card__content {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
7. Responsive Design
Mobile-First Approach:
.container {
padding: 10px;
font-size: 14px;
}
@media (min-width: 768px) {
.container {
padding: 20px;
font-size: 16px;
}
}
@media (min-width: 1024px) {
.container {
padding: 30px;
max-width: 1200px;
margin: 0 auto;
}
}
@media (min-width: 1440px) {
.container {
padding: 40px;
max-width: 1400px;
}
}
Common Breakpoints:
@media (min-width: 640px) {}
@media (min-width: 1024px) {}
@media (min-width: 1280px) {}
8. Avoiding Code Repetition
.button-primary {
padding: 10px 20px;
border-radius: 5px;
font-weight: 600;
background-color: blue;
}
.button-secondary {
padding: 10px 20px;
border-radius: 5px;
font-weight: 600;
background-color: gray;
}
.button {
padding: 10px 20px;
border-radius: 5px;
font-weight: 600;
}
.button-primary {
background-color: blue;
}
.button-secondary {
background-color: gray;
}
9. CSS Reset/Normalize
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
line-height: 1.5;
font-family: system-ui, -apple-system, sans-serif;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
Or use normalize.css:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css">
10. Comments and Documentation
.card {
}
.header {
min-height: 0;
}
Performance Optimization
1. Minimize and Compress
npm install -D cssnano postcss-cli
module.exports = {
plugins: [
require('cssnano')({
preset: 'default',
})
]
}
2. Critical CSS Inlining
<head>
<style>
body { margin: 0; font-family: sans-serif; }
.header { background: #fff; height: 60px; }
.hero { min-height: 100vh; }
</style>
<link rel="preload" href="main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="main.css"></noscript>
</head>
3. Avoid Expensive Properties
.expensive {
width: 100px;
height: 100px;
top: 100px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
}
.optimized {
transform: scale(1.1) translateY(10px);
opacity: 0.9;
}
Performance Tiers:
- ⚡ Cheapest:
opacity, transform, filter
- ⚠️ Moderate: color, background-color
- 🐌 Expensive: width, height, padding, margin, border
- 🔥 Very Expensive: properties triggering reflow on complex layouts
4. Use will-change Sparingly
* {
will-change: transform;
}
.element {
transition: transform 0.3s;
}
.element:hover {
will-change: transform;
transform: translateY(-5px);
}
.element:not(:hover) {
will-change: auto;
}
Accessibility Best Practices
1. Color Contrast
.text-on-dark {
background-color: #000;
color: #fff;
}
.text-low-contrast {
background-color: #ccc;
color: #ddd;
}
Tools:
2. Focus Indicators
button:focus {
outline: none;
}
button:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
}
button:focus-visible {
outline: 2px solid #4299e1;
outline-offset: 2px;
}
3. Screen Reader Only Content
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
Usage:
<button>
<span class="sr-only">Close modal</span>
<svg>...</svg>
</button>
4. Reduced Motion
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Browser Compatibility
1. Vendor Prefixes
Use Autoprefixer - don't write manually:
npm install -D autoprefixer
module.exports = {
plugins: [
require('autoprefixer')
]
}
2. Feature Detection
.element {
background-color: #1DA1F2;
background-color: oklch(59.69% 0.217 237.04);
}
@supports (display: grid) {
.layout {
display: grid;
}
}
@supports not (display: grid) {
.layout {
display: flex;
}
}
3. Check Browser Support
Always verify on Can I Use before using new features.
Avoid hardcoding percentages in guidance docs because support changes over time.
Current recommendation:
- Use modern features by default only when target browsers are explicitly supported by your product matrix.
- Add fallbacks (or progressive enhancement) for features like
:has() and subgrid when compatibility is uncertain.
Modern CSS Features
1. CSS Nesting
.card {
padding: 20px;
& .card-header {
font-size: 24px;
font-weight: bold;
}
& .card-body {
margin-top: 10px;
}
&:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
}
2. :has() Selector
.card:has(.card-image) {
display: grid;
grid-template-columns: 200px 1fr;
}
.form-group:has(input:invalid) {
border-color: red;
}
3. Subgrid
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
}
.grid-item {
display: grid;
grid-template-columns: subgrid;
}
Quick Reference Checklist
Before Writing Styles
While Writing Styles
Tailwind-Specific
Before Committing
Common Mistakes and Solutions
Mistake 1: Fighting Specificity Wars
.button { color: blue; }
.button.primary { color: white !important; }
.header .button { color: red !important; }
.button { color: blue; }
.button--primary { color: white; }
.header-button { color: red; }
Mistake 2: Not Using Variables
.header { background: #1DA1F2; }
.button { background: #1DA1F2; }
.link { color: #1DA1F2; }
:root { --color-primary: #1DA1F2; }
.header { background: var(--color-primary); }
.button { background: var(--color-primary); }
.link { color: var(--color-primary); }
Mistake 3: Ignoring Mobile
.container { width: 1200px; padding: 40px; }
@media (max-width: 768px) { .container { width: 100%; padding: 20px; } }
.container { width: 100%; padding: 20px; }
@media (min-width: 768px) { .container { width: 1200px; padding: 40px; } }
Mistake 4: Over-Nesting
.nav ul li a span.icon { }
.nav-icon { }
Tools and Resources
Essential Tools
- Prettier + Tailwind Plugin: Auto-format class ordering
- ESLint + Tailwind Plugin: Lint Tailwind classes
- Autoprefixer: Auto-add vendor prefixes
- cssnano: Minify CSS
- Can I Use: Check browser support
Testing Tools
- WebAIM Contrast Checker: Color contrast
- WAVE: Accessibility testing
- Lighthouse: Performance and accessibility audit
- BrowserStack: Cross-browser testing
Documentation
Output Format
When providing CSS/Tailwind guidance:
- Identify the context: What framework/approach is being used?
- Run version detection protocol: Dependency first, then syntax/config cross-check
- Output required fields:
Detected Version: v3 | v4 | conflict | unknown
Evidence: dependency + syntax/config evidence you used
Primary Path: the main recommendation for detected version
Alternate Path: compatible fallback path for the other version
Conflict Fix: required when state is conflict (include two repair options + recommended option)
- Provide specific code examples: Show good vs bad patterns
- Explain the reasoning: Why one approach is better
- Consider accessibility: Always mention a11y implications
- Check performance: Flag potential performance issues
- Suggest tools: Recommend automation where possible
Always prioritize:
- ✅ Maintainability and readability
- ✅ Performance and bundle size
- ✅ Accessibility compliance
- ✅ Team consistency
- ✅ Modern best practices
Version History
- v1.2 (2026-02-24): Added v3/v4 dual-track support with dependency-first detection flow, conflict handling, and structured output contract fields.
- v1.1 (2026-02-24): Clarified Tailwind adoption criteria, responsive prefix guidance, and compatibility wording.
- v1.0 (2025-01): Initial release covering Tailwind CSS + modern CSS best practices.