๐Ÿ”งAutoSkills
All items
skillv1.0.0

tailwind-conventions

Tailwind class ordering, design tokens, and when to extract components vs. utilities

tailwindcssfrontend

Install

$npx autoskills --items tailwind-conventions

Or scan + install everything matching your stack with npx autoskills.

Tailwind Conventions

Tailwind only stays maintainable if you have conventions. Without them, every component becomes a 400-character className string nobody can diff.

Class ordering

Use Prettier with prettier-plugin-tailwindcss. It sorts classes deterministically. No team debates about order.

Logical groups, in this order (the plugin's default):

  1. Layout โ€” block, flex, grid, hidden, inline-flex
  2. Positioning โ€” relative, absolute, top-0, inset-x-0, z-10
  3. Box model โ€” m-4, p-2, w-full, h-screen
  4. Border โ€” border, rounded-lg, border-gray-200
  5. Background โ€” bg-white, bg-gradient-to-r
  6. Typography โ€” text-sm, font-medium, leading-tight
  7. Effects โ€” shadow, opacity-50, blur-sm
  8. Transitions โ€” transition, duration-150
  9. Interactivity โ€” cursor-pointer, select-none
  10. States โ€” hover:bg-blue-600, focus:ring-2, disabled:opacity-50
  11. Responsive โ€” md:flex, lg:p-8

Design tokens, not arbitrary values

<!-- bad: magic numbers everywhere -->
<div class="text-[14.5px] mt-[13px] bg-[#3b82f6]">
 
<!-- good: extends to the design system -->
<div class="text-sm mt-3 bg-blue-500">

If you need a non-default value, add it to tailwind.config.js, don't use [arbitrary] inline. Arbitrary values are escape hatches; using them everywhere means there is no system.

When to extract a component vs. a utility

  • Use a component (React/Vue/Blade) for repeated UI, not @apply. @apply creates a CSS layer Tailwind has to scan; components are how your framework thinks.
  • Use @apply only for primitive design tokens shared across the app (e.g. .prose-default, .input-base) โ€” and only in a CSS file, not inline.
  • Don't extract too early. Three uses of similar classes โ‰  a component yet. Wait for the abstraction to be obvious.

Conditional classes

Use clsx or tailwind-merge for conditionals. Don't string-concatenate.

// good
import { cn } from '@/lib/utils'; // re-exports clsx + twMerge
 
<button className={cn(
  'rounded px-4 py-2',
  isPrimary ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-900',
  isDisabled && 'opacity-50 cursor-not-allowed',
  className, // allow override
)}>

tailwind-merge resolves conflicts: cn('p-4', 'p-6') โ†’ 'p-6'. Critical for component prop overrides.

Dark mode

  • darkMode: 'class' in tailwind.config.js โ€” gives you a dark: variant gated by a .dark class on <html>.
  • Don't sprinkle dark: everywhere. Define dark mode at the design token level when possible (bg-background/text-foreground with CSS vars).

Spacing

  • Stick to the default scale (p-1 to p-32). Custom values like p-[7px] are red flags.
  • Use space-y-* / space-x-* for vertical/horizontal rhythm in flex/grid children. Cleaner than margin-on-each-child.
  • gap-* on flex/grid containers is preferable to space-* for modern layouts.

Common anti-patterns

  • Inline style={{}} for things Tailwind already has โ€” pick Tailwind or pick inline, not both.
  • Mixing Tailwind with utility libraries that fight it โ€” Bootstrap, MUI, etc. Pick one design system.
  • 400+ char className โ€” extract a component.
  • Repeating the exact same class string in 5 places โ€” extract a component.
  • Using !important (!bg-blue-500) โ€” almost always a sign you have a specificity conflict you should fix instead.

Quick check before commit

  1. Did Prettier sort classes? (run it, don't argue manually)
  2. Any arbitrary values? If yes, should they be in the config?
  3. Same long string in multiple files? Extract.
  4. Any !important (!)? Why?