CSSTidy CSS Formatter

CSS Cheat Sheet: Selectors, Specificity, and the Properties Worth Memorising

CSSTidy — CSS Formatter & Minifier Guides · Updated 2026-10-12 · All guides

Print it. The left column is a spec fact; the right column is what you actually look up.

Specificity, once

Four-part count (a, b, c, d), compared left to right:

AddsExampleWeight
a — ID selectors#nav1,0,0,0
b — class, attribute, pseudo-class.btn, [type=button], :hover, :not() contents count0,1,0,0
c — type and pseudo-elementdiv, p, ::before0,0,1,0
d — universal / combinator*, +, >, ~, 0,0,0,0

Inline styles beat all of it except !important; !important reverses normal order, and an !important in a stylesheet beats an inline !important. A :where() wrapper contributes zero — that is why reset libraries use it. Cascade order is decided before specificity: origin, then @layer, then specificity, then source order. Specificity loses to layers, which surprises people who have read the specificity table and stopped.

Selectors you need

SelectorMatches
.a.bboth classes on one element
.a .bany descendant
.a > .bdirect child only
.a ~ .blater sibling, any distance
.a + .bimmediately next sibling
:is(.a, .b) .ctakes the specificity of its most specific argument
:where(.a, .b) .ccontributes zero specificity
:not(.a)anything not .a; contents still count toward specificity
:has(> input:checked)parent based on child — the selector CSS never had
p::first-line, ::selectionpseudo-elements; they add to c, not b
[attr=value i]attribute match, case-insensitive

Box model and layout

*, *::before, *::after { box-sizing: border-box; }   /* width = total width */

.parent { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1rem; }
.parent { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; }
.center  { display: grid; place-items: center; }      /* both axes, one line */
.stack   { display: flex; flex-direction: column; gap: .75rem; }  /* margins not needed */
.scroll  { overflow: auto; overscroll-behavior: contain; }
.clamp   { width: min(92vw, 68ch); margin-inline: auto; }         /* fluid, capped */
.trunc   { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lines   { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }

gap works on flex as well as grid. Percentage heights need a definite parent height; min-height: 100dvh on an ancestor fixes most "my flex child will not stretch" reports, and dvh avoids the mobile URL-bar jump that vh has.

Typography and space

html { font-size: 100%; }                     /* rem = user's preference */
body { line-height: 1.5; max-width: 68ch; }
h1   { font-size: clamp(1.6rem, 1.2rem + 2vw, 2.6rem); text-wrap: balance; }
p    { text-wrap: pretty; hyphens: auto; }
.gap { margin-block: 0 1rem; }                /* logical property: works in RTL */

text-wrap: balance and pretty are supported in current Safari, Chrome, and Firefox; without them they are inert declarations, so they are free to add today.

Color and state

:root { --surface: light-dark(#f7f8fa, #0d1117); --accent: oklch(70% .16 245); color-scheme: light dark; }
.btn:is(:hover, :focus-visible) { background: color-mix(in oklab, var(--accent) 82%, transparent); }
.btn:disabled { cursor: not-allowed; opacity: .55; }

color-mix() gives you hover states without a preprocessor. :focus-visible is the correct focus hook — :focus fires on mouse click and makes pages look broken.

@-rules

@media (prefers-color-scheme: dark) { … }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } }
@media (width >= 48rem) { … }                       /* range syntax, no min/max-width */
@layer base, components, utilities;                  /* declaration order = priority order */
@supports (display: grid) and (not (-moz-display: grid)) { … }
@container (min-width: 30rem) { … }                 /* needs container-type: inline-size */

What a formatter may and may not touch

Safe: whitespace outside tokens, comments you asked to drop. Never safe to automate:

  • !important — semantic, not decoration.
  • Duplicate selectors — merging them changes behaviour with equal specificity if the later rule's declarations differ; a minifier that merges can break a page.
  • Whitespace inside a quoted string or inside url() without quotes — that is content. A formatter doing this is producing valid-looking CSS with corrupted values; see minify safety.
  • Vendor prefix order — -webkit- must precede the unprefixed declaration or the unprefixed one wins.

The beautifier and minifier here operate on whitespace and comments only; the linked page lists exactly where that rule breaks, measured.

Developer Sponsor / Partner
Copied to clipboard!