CSS Patterns

Classic CSS interview questions with live previews next to the rule that produces them. Toggle techniques to compare behaviour side by side.

Core concepts

EasyMastered

Center a div without flex or grid

Classic positioning tricks interviewers still love — know when translate vs auto margins wins.

Center a div without flex or grid

Four classic ways to center a child inside a parent box — useful when an interviewer asks you to center without reaching for display: flex or display: grid.

Pin the child to the parent center, then pull it back by half its own size. Works without knowing the child width or height.

centered
.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
EasyIn progress

CSS position values

static, relative, absolute, fixed, sticky — containing blocks and scroll behaviour.

CSS position values

static → relative → absolute → fixed → sticky. Switch tabs to see how each one places the green box (B) inside the parent.

The default. The box stays in normal flow; top/right/bottom/left and z-index are ignored.

A
B
C
.box {
  position: static; /* default */
  top: 20px;        /* ignored */
  left: 40px;       /* ignored */
}
MediumIn progress

Box model: content-box vs border-box

Explain rendered width when padding and borders join the party.

Box model: content-box vs border-box

Both boxes declare width: 200px, padding: 20px, and border: 8px solid. Only the rendered size differs.

rendered outer width: 256px
content area: 200px
.box {
  box-sizing: content-box; /* default */
  width: 200px;
  padding: 20px;
  border: 8px solid;
  /* outer width = 200 + 20*2 + 8*2 = 256px */
}
MediumNot started

Specificity: which rule wins?

IDs, classes, inline styles, !important — tie-break order without guessing.

Specificity: which rule wins?

Toggle the rules and watch the heading colour change. Specificity is a four-part score (inline, id, class, type) — and !important still beats them all.

<p id="hello" class="text title">Hello, specificity!</p>
activeselectorspecificitycolor
p(0,0,0,1)#94a3b8
.text(0,0,1,0)#5b9dff
.text.title(0,0,2,0)#a78bfa
#hello(0,1,0,0)#34d399
style="color: …"(1,0,0,0)#fbbf24
.text { color: red !important }!important#f87171
EasyMastered

Truncate text with an ellipsis

overflow, white-space, text-overflow — plus flexGotchas.

Truncate text with an ellipsis

Two reliable recipes: a single-line text-overflow: ellipsis and a multi-line -webkit-line-clamp.

CSS truncation interview answer: when content is longer than the available space, you can clip it and append an ellipsis instead of letting it wrap or overflow. The single-line trick relies on three properties working together; the multi-line variant uses the WebKit line-clamp pattern that has shipped in every modern browser for years.

.cell {
  white-space: nowrap;     /* keep text on one line */
  overflow: hidden;        /* hide what doesn't fit */
  text-overflow: ellipsis; /* draw the … */
  max-width: 240px;        /* needs a constrained width */
}
HardIn progress

Stacking context & z-index traps

Why z-index: 9999 sometimes loses — opacity, transforms, and isolation.

Stacking context: the z-index: 9999 trap

z-index is only compared inside the same stacking context. The child below tries to climb above the orange banner, but as soon as the parent becomes its own stacking context, the child is sealed inside.

orange banner — z-index: 5
.parent
child — z-index: 9999

Parent is NOT a stacking context — the child can rise above the banner.

.sibling {
  position: absolute;
  z-index: 5;          /* a banner that sits above page content */
}

.parent {
  position: relative;
  /* nothing — parent is not a stacking context */
}

.child {
  position: absolute;
  z-index: 9999;       /* "I should be on top!" */
}
MediumNot started

Display values & hiding elements

none vs visibility vs opacity vs off-screen patterns for accessibility.

Display values & how to hide an element

Two of the most asked questions: "What's the difference between block, inline, and inline-block?" and "What's the difference between display: none, visibility: hidden, and opacity: 0?"

1. Display values on the middle box

Takes the full available width, starts on a new line. Respects width / height / vertical margin / vertical padding.

some text — Amiddle (set width: 80px, height: 36px)C — more text
.middle {
  display: block;
}

2. Four ways to hide

Removed from layout AND accessibility tree.

techniquetakes layout space?read by screen readers?focusable / clickable?
display: nonenonono
visibility: hiddenyesnono
opacity: 0yesyesyes ⚠️
visually-hidden (sr-only)noyesno
.target { display: none; }
EasyMastered

Pseudo-class vs pseudo-element

:hover vs ::before — syntax, specificity hooks, and interview narrations.

Pseudo-class vs pseudo-element

One colon (:) selects elements in a particular state. Two colons (::) generate or address pieces of an element that aren't in the DOM.

:hover — pseudo-class (state)

.btn {
  background: #5b9dff;
  color: white;
  transition: background 150ms;
}
.btn:hover {            /* state-based */
  background: #2170e4;
}
HardNot started

Vertical margin collapsing

Adjacent siblings, empty blocks, and padding hacks that reset collapse.

Vertical margin collapsing

Two block siblings: A has margin-bottom: 20px, B has margin-top: 30px. By default the gap is 30px (the larger one wins, not the sum). Toggle the parent or children below to see what breaks the collapse.

A — margin-bottom: 20px
B — margin-top: 30px
rendered gap: 30px (max(20, 30) — collapsed)
.parent {
  /* no border */
  /* no padding */
  /* overflow: visible */
}

.a { margin-bottom: 20px;  }
.b { margin-top:    30px;  }

/* rendered gap = 30px (max(20, 30) — collapsed) */
MediumIn progress

CSS custom properties (variables)

Theme tokens, inheritance, fallbacks, and runtime updates vs preprocessor vars.

CSS custom properties (variables)

Custom properties cascade and inherit like normal properties. Move the sliders to update --demo-accent and --demo-radius on a wrapper — every descendant that reads them via var(...) updates instantly.

badge inherits the same hue
color: var(--missing, hotpink) — fallback wins when the variable is unset
:root {
  --accent: hsl(220 70% 55%);
  --radius: 8px;
}

.btn {
  background: var(--accent);
  color: white;
  border-radius: var(--radius);
  /* fallback if --accent ever goes missing */
  border: 1px solid var(--accent, hotpink);
}

/* Override on a subtree — children inherit the new value */
.danger {
  --accent: hsl(0 80% 55%);
}
Read & write from JavaScript
// Read at runtime
const v = getComputedStyle(el).getPropertyValue('--accent').trim();

// Write from JS — propagates to every var(--accent) in the subtree
el.style.setProperty('--accent', 'hsl(280 80% 55%)');