General
Covers: general markup and Tailwind CSS authoring rules not specific to one component.
Coding Rules
Markup
- Never apply
text-*(font size) orleading-*(line height) to inline elements (<span>,<a>,<strong>,<em>,<code>); apply them to containing block-level elements (<div>,<p>,<h1>,<h6>,<li>,<td>) - Never add display classes matching an element's default display: no
blockon<div>/<p>/<h1>/<h6>; noinlineon<span>/<a>; noinline-blockon<input>/<button>/<select>; notableon<table>. Only applies to classes that don't change child layout:flex,grid,inline-flex,inline-gridare never redundant - Never apply conflicting classes for the same property on one element without a distinguishing variant: no
outline-1 outline-2, nooutline-black/5 outline-white; keep only the intended value - Always add
role="list"to<ul>and<ol>unless alist-style-*class (e.g.list-disc,list-decimal) is applied - Never add
hover:*to non-interactive elements: reserve for buttons, links, and other clickables - Never add
transition-*for hover color/background changes: reserve transitions for elements that move or transform
Tailwind CSS
- Always apply
antialiasedto the root element - Always apply
isolateto the main app container (getsinertwhen dialogs open): prevents z-index conflicts with portalled elements - Place
@importstatements with remote URLs (http/https) orurl()at the very top of the CSS file, before@import "tailwindcss"(but after@charsetif present) - Add
tabular-numsto elements displaying numbers, especially values that change over time (counters, timers, prices, stats): prevents layout shift as digits update - Never use
mt-*/mb-*/ml-*/mr-*/mx-*/my-*between flex/grid children: usegap-*on the parent instead - Prefer
size-{n}overh-{n} w-{n}when both values are the same - Prefer shorthand over split axis classes:
p-8notpx-8 py-8,inset-0notinset-x-0 inset-y-0; keep them split when a variant overrides one axis, e.g.p-8 md:px-10 - Use
--spacing(…)for arbitrary spacing values:--padding: --spacing(2)not--padding: 8px - Never use
calc(var(--spacing)*…): use--spacing(…)instead - Never use
theme(spacing.…): use--spacing(…)instead - Never use
theme()for colors or other tokens in arbitrary values: use CSS variables instead;[stop-color:var(--color-emerald-500)]not[stop-color:theme(colors.emerald.500)] - Use
remfor arbitrary font sizes:text-[0.8125rem]nottext-[13px] - Pixels are fine for properties that use pixels natively in Tailwind:
border-*,outline-* - Use theme variable references for arbitrary radii:
--radius: var(--radius-xl)not--radius: 16px - Never use named line-height values (
tight,snug,relaxed): not inleading-tight, not intext-6xl/tight; only spacing scale values (e.g.leading-6,text-sm/5), and only when a custom line height is specifically required - Never use inline
stylefor static CSS properties lacking a utility class: use arbitrary property syntax instead;class="[animation-delay:300ms]"notstyle="animation-delay: 300ms" - Set CSS variables with arbitrary property syntax, not inline styles:
class="[--padding:--spacing(3)]"notstyle="--padding: --spacing(3)"(unless the value is dynamic) - For dynamic values, prefer CSS variables over CSS properties in
style:class="w-(--progress)" style="--progress: 72%"notstyle="width: 72%"; name the variable descriptively relative to the context - Prefer bare values over arbitrary values for integers and multiples of
0.25:z-999notz-[999] - Prefer bare opacity modifiers on color utilities:
bg-neutral-950/2notbg-neutral-950/[0.02]; use[…]only for non-0.25-increment values - Negate
hiddenwith a single conditional variant instead of settinghiddenthen re-applying the display class:flex items-center gap-x-6 max-lg:hiddennothidden lg:flex lg:items-center lg:gap-x-6;not-dark:hiddennothidden dark:block - Prefer
not-*variants over a base value with conditional override:group-not-has-checked:opacity-0notgroup-has-checked:opacity-100 opacity-0; placenot-directly before the negated state, notnot-group-has-checked:…(fires without agroupparent) orgroup-has-not-checked:…(matches any unchecked element) - Use bare values in variants over arbitrary values in variants:
data-closed:…notdata-[closed]:…,group-data-open:…notgroup-data-[open]:… - Always use
min-h-dvh/svh/lvh, nevermin-h-screen(screenis deprecated) - Always use
bg-linear-*for gradients, neverbg-gradient-*(deprecated) - Use
shrink-*notflex-shrink-*,grow-*notflex-grow-*(deprecated) - Prefer whole-number ratios in arbitrary grid/flex values:
grid-cols-[21fr_19fr]notgrid-cols-[1.05fr_0.95fr]; multiply all values by the same factor to eliminate decimals - Prefer
@utility my-utility { … }over plain class selectors (.my-utility { … }): utilities work with all Tailwind variants (hover:my-utility,lg:my-utility) - Use
@utility my-utility-* { … }with--value()and--modifier()for parameterized utilities that accept arguments - Use
@variant the-variant { … }inside@utilitydefinitions to apply an existing variant: don't manually write the media query or selector - Use
@custom-variantto define new custom variants when the built-in set doesn't cover the case - Never nest
@utilityinside another at-rule (@media,@supports): move the at-rule inside the@utilityblock instead