Think of it as a playbook: identify the symptom, pick the right metric to inspect, and make one controlled change at a time.
If you only remember one thing: CSS problems usually aren’t “random”—they’re often one number quietly cascading into everything else.
The playbook at a glance (the loop you’ll repeat)
When something looks wrong, run this loop instead of guessing.
- Freeze the symptom: what exactly is wrong—size, spacing, overflow, alignment, layering, or text?
- Find the controlling box: which element’s box is causing it (the one that’s too big/small/positioned)?
- Identify the “unit family”: is the issue coming from font-relative units (em/rem), viewport units (vh/vw), percentages, or fixed lengths (px)?
- Check computed values: what numbers did the browser actually end up using?
- Change one lever: adjust a single value (or swap unit types) and re-check the computed result.
- Lock in a rule: once fixed, make it resilient (avoid magic numbers, avoid fragile dependencies).
You’ll use different “metrics” depending on the category of bug.
Units explained like you’re busy: px, %, em, rem, vw, vh
- px: “A fixed length.” On modern phones it’s not literally one physical pixel; it’s a consistent CSS pixel. Great for borders and small visual details. Risk: can feel too rigid for text sizing and responsive layouts.
- %: “A fraction of the parent.” Works well for widths. For heights it’s trickier because the parent often doesn’t have a definite height.
- em: “Relative to this element’s font size.” Powerful but can compound: nesting can make numbers balloon (or shrink) faster than you expect.
- rem: “Relative to the root (html) font size.” Usually steadier than em for spacing and type scales.
- vw: “A slice of the viewport width.” Useful for responsive sizing, but can create extremes on very narrow/wide screens.
- vh: “A slice of the viewport height.” Useful for full-height sections, but on mobile the browser UI (address bar) makes ‘viewport height’ behave in surprising ways.
Workflow tip: when spacing looks inconsistent across screen sizes, first ask: “Is this tied to font size (em/rem), container size (%), or the viewport (vw/vh)?” That tells you which lever is likely miscalibrated.
Font metrics that quietly change layouts: font-size, line-height, and “why it wraps”
Many “layout” bugs are actually typography math.
- font-size: the base number everything else may depend on (especially em-based spacing).
- line-height: the vertical spacing inside lines of text. In plain English: how tall each line’s “lane” is.
- letter-spacing: can push text to wrap earlier than expected, especially in buttons and nav items.
Two practical rules:
- Use unitless line-height (often): e.g., line-height: 1.4. That means “1.4× the font size” and tends to scale more predictably.
- When text wraps unexpectedly: check the computed font-size, letter-spacing, and the container’s width (including padding).
On mobile, a tiny change in font metrics can be the difference between one line and two—and that can push entire sections down.
Box model metrics: padding, border, margin, gap (and where space is really coming from)
- padding: space inside the box, between content and border.
- border: the outline. Borders add to the element’s size unless you’re accounting for it.
- margin: space outside the box. Margins can collapse (especially vertical margins), which can make spacing feel inconsistent.
- gap: space between items in flex/grid. Often cleaner than margins for consistent spacing.
A simple debugging move: if you can’t tell where the extra whitespace is coming from, temporarily set outline or add a temporary background color to suspect elements. You’re trying to answer: “Is the space inside the box, outside it, or between siblings?”
Viewport terms that cause “jumpy” mobile layouts: 100vh, safe areas, and scrollbars
Mobile browsers can change the visible viewport while you scroll (address bars show/hide). That’s why elements sized with vh sometimes feel like they resize mid-scroll.
Plain-English mapping:
- Viewport height (vh): “What the browser considers the screen height right now.” On mobile, that can shift.
- Overflow: “What happens when content doesn’t fit.” If you see clipped content or accidental horizontal scrolling, check overflow-x, large fixed widths, and long unbroken strings.
Workflow tip: if you’re using height: 100vh for a hero section and it feels unstable on phones, test a version that uses content-driven height plus padding. Many designs don’t truly need a full viewport lock.
Specificity and the cascade: why your change “does nothing”
- Cascade: “Which rule is considered later/more relevant.” Order matters.
- Specificity: “How targeted the selector is.” More targeted selectors beat broader ones.
- Computed styles: “The final truth.” This is the number/value actually applied after the cascade.
A practical approach:
- Start by checking the computed value (not your source file) for the property you care about.
- If your rule is crossed out, it lost. Look at the winning selector and decide whether to (a) simplify the competing rules, (b) increase specificity carefully, or (c) change the order/import structure.
Try to avoid solving everything with !important. It works, but it often turns into a long-term “why is this overridden?” tax.
Layering terms: z-index, stacking contexts, and “why is it behind?”
If a dropdown, toast, or sticky header shows up behind something, you’re in stacking-context land.
- z-index: “Who’s on top.” But it only applies to positioned elements (or those that create stacking contexts).
- Stacking context: “A local layer universe.” Some properties (like transforms, opacity less than 1, certain positioning) can create a new context that traps children.
Workflow tip: when z-index “doesn’t work,” stop increasing the number. Instead, find the nearest ancestor that created a stacking context and decide whether that context should exist.
Takeaway: a calm way to use CSS numbers
When you see a CSS term or metric you don’t recognize, treat it as a clue to the controlling system: font-relative, container-relative, viewport-relative, or cascade/layering.
Run the loop: isolate the symptom, check computed values, adjust one lever, and make the fix resilient.