Command Palette

Search for a command to run...

Hectal
PHASE 11Advanced ~22 min· topic 6 of 8

Topic 11.6

Debugging CSS

In one line

Debugging CSS is a routine, not guesswork: inspect the element, check whether the rule matches and wins, read the computed value and the box model, use the Flexbox and Grid overlays, and know the handful of bugs that cause most layout problems.

Think of it like this

A doctor's check-up. A good doctor doesn't guess; she asks questions in a fixed order (temperature, pulse, blood pressure) and each answer narrows down the cause. CSS debugging works the same way. Instead of changing random values until it looks right, you ask the same questions every time: Does my rule match this element? Does it win? What is the final value? What is the box size, and who decides it?

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

DevTools
The developer tools built into every browser, opened with F12 or right-click and Inspect.
Styles pane
The DevTools list of every CSS rule that matches the selected element, strongest first.
Computed tab
The DevTools view of each property's final value and which rule set it.
Struck-through declaration
A declaration that matched but lost the cascade to a stronger one.
Overlay
Lines and labels DevTools draws over the page to show a grid, flex container or box.
Margin collapsing
Vertical margins that touch merge into one margin, which can make a child's margin push its parent.
Stacking context
A group of elements stacked together as one layer; z-index only compares elements inside the same group.
Reduced test case
The smallest piece of HTML and CSS that still shows the bug.
Bisecting
Finding a problem by removing half the suspects at a time until one is left.

Step by step

01Ask: does my rule match?

Inspect the element and look for your rule in the Styles pane. If it isn't listed at all, the selector doesn't match this element or the file didn't load. Check the class in the Elements panel against the selector, letter by letter.

02Ask: does it win?

If your declaration is struck through, open the Computed tab, expand the property, and you'll see every candidate with the winner first. Compare origin, layer, specificity and order (Phase 2) to see why.

Ask: does it win?diagram
Rendering diagram…

03Ask: what does the box look like?

Read the box model diagram. Hover the element in the Elements panel and look at the coloured areas on the page. Unexpected orange is a margin, unexpected green is padding.

04Turn on the layout overlays

Click the grid or flex badge next to the container. The overlay shows track lines, gaps and item sizes, so you can see whether a column is 1fr or stuck at its content size.

05Hunt overflow with a snippet

Paste this in the Console to list every element whose right edge is past the viewport. Then fix the widest one first.

consolewhole filejavascript
const vw = document.documentElement.clientWidth;
[...document.querySelectorAll("body *")]
  .filter((el) => el.getBoundingClientRect().right > vw)
  .forEach((el) => console.log(el));

06Reduce, fix, and copy back

Strip the case down until the bug is obvious, fix it in DevTools, then open the Changes tab to copy exactly what you changed into your source files.

Try it yourself

  1. 1

    Find the culprit yourself

    In the first example, add <p>https://example.com/a-very-long-address-with-no-spaces-at-all-that-will-not-wrap-anywhere-on-a-phone-screen</p> and give the banner max-width: 100%. Predict whether the page still scrolls sideways in a narrow window, then check. (Fix it with overflow-wrap: anywhere, Phase 4.)

  2. 2

    Fix the escaping margin another way

    In the second example, instead of flow-root, give .wrap padding-top: 1px. Predict the gap the console prints, then run it.

  3. 3

    Use the overlay

    Open DevTools on the third example, find the flex badge next to <header> and click it. Predict where the free space appears with justify-content: space-between, then compare with the overlay.

Code & diagrams

Find the element that makes the page scroll sideways New tab

Change `width: 5000px` to `max-width: 100%` and run again: the console finds nothing and the page no longer scrolls sideways.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

page scrolls sideways: true
too wide: div.banner
Two classic bugs: percentage height and an escaping margin New tab

`height: 50%` needs a parent with a definite height, so it resolves to `auto` (0px for an empty box) until the parent gets one. The paragraph's 24px margin collapses through `.wrap` and pushes the whole yellow box down; `display: flow-root` makes `.wrap` contain it.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

child height, parent auto: 0px
child height, parent 200px: 100px
gap above .wrap: 24
gap above .wrap with flow-root: 0
The outline trick: see every box New tab

Each red line is one element's border box. Notice the small gap under the green image: images are inline and sit on the text baseline. Add `img { display: block; }` and the gap disappears. Delete the outline rule when you're done.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

A flex item that refuses to shrink

Put a long unbroken word inside a flex item next to a fixed-width sidebar: .row { display: flex } .side { width: 200px } .main { flex: 1 }.

terminal
$ Narrow the window
── what you'll see ──
`.main` stays wide and the row overflows its container; the page scrolls sideways.

Break #2

Using borders to debug

In the third example, replace the outline debug rule with * { border: 1px solid red; }.

terminal
$ Look at the page
── what you'll see ──
Every box grows by 2px in each direction, so the layout shifts, and a nearly-fitting row may wrap: you are now debugging a different layout.

Myth vs fact

Myth

If a style doesn't apply, add !important.

Fact

That hides the cause and starts a specificity war. Find out in DevTools whether the rule doesn't match, loses, or has no effect, and fix that.

Myth

What you see in View Source is what the browser is styling.

Fact

View Source shows the HTML as downloaded. The Elements panel shows the live DOM after scripts changed it, which is what CSS actually matches.

Myth

Layout bugs are usually browser bugs.

Fact

Nearly all of them are spec behaviour that surprised someone: margin collapse, min-width: auto, stacking contexts, percentage heights. Reduce the case first; real browser bugs are rare.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Chrome shows why a declaration has no effect: hover the info icon on a dimmed property to read messages such as "The display: block property prevents justify-content from having an effect". Firefox's inactive CSS feature does the same.

  • ▸

    getComputedStyle() returns the resolved value, while getBoundingClientRect() returns the real painted rectangle including transforms. When they disagree (a transform: scale() for example), the transform is the reason.

  • ▸

    The Rendering tab's Layout Shift Regions and the Performance panel find layout shifts, which hurt the Cumulative Layout Shift metric (Core Web Vitals). Common causes are images without width/height, late web fonts and content injected above other content.

  • ▸

    Container queries, :has() and layers all appear in the Styles pane with their conditions (@container, @layer names), so check the conditions there when a rule seems to switch on and off.

Remember this

  1. 1

    Step 1: inspect the right element. Right-click it and choose Inspect, or use the element picker in DevTools. Check you selected the element you think you did; many bugs are styles on a wrapper while you're looking at the child. The Elements panel shows the DOM tree as it is now, after JavaScript changed it, not your original HTML.

  2. 2

    Step 2: read the Styles pane. It lists every rule that matches, strongest first. If your rule is missing, the selector doesn't match (check spelling, classes, nesting, or whether the stylesheet loaded at all). If it's there but struck through, another declaration won; hover the property in the Computed tab to see which rule won and why. A warning icon means the value is invalid (a typo like colr or 20 px) and was ignored. A greyed-out declaration with an info icon means it is valid but has no effect here, for example justify-content on an element that isn't a flex or grid container; Chrome explains why when you hover it.

  3. 3

    Step 3: check the box. The box model diagram in the Computed tab shows content, padding, border and margin. Hovering an element in the Elements panel paints its margin (orange), padding (green) and content (blue) on the page. Most "why is there a gap" questions are answered here: an unexpected margin, padding from the browser's own styles, or margin collapsing (two vertical margins merging, or a child's margin escaping its parent, Phase 5).

  4. 4

    Step 4: use the layout tools. Elements with display: flex or grid get a flex or grid badge in the Elements panel; clicking it draws an overlay with lines, gaps and track sizes. The Layout pane lists every grid and flex container on the page. Chrome's flex editor and Firefox's Flexbox inspector even show how much each item grew or shrank and why. For stacking problems, the Layers panel and the Rendering tab's options (paint flashing, layout shift regions) show what the browser repaints and what moves.

  5. 5

    A few bugs cause most CSS pain, so learn their signatures. Horizontal scroll: something is wider than the screen (a fixed width, a long URL, 100vw plus a scrollbar). Flex item won't shrink: flex items have min-width: auto, so long content keeps them wide; add min-width: 0 (Phase 7). Percentage height does nothing: the parent has no definite height, so height: 50% behaves like auto. z-index ignored: the element isn't positioned, or its parent created a stacking context (Phase 6). Gap below an image: images are inline and sit on the text baseline; use display: block. Margin escaping the parent: margin collapse; use padding, display: flow-root, or flex/grid on the parent.

  6. 6

    Two quick tricks help when you can't see the boxes. * { outline: 1px solid rgb(255 0 0 / 0.4); } outlines every element without changing layout (outlines take no space, unlike borders). And when you don't know which element breaks the layout, bisect: delete or hide half of the suspects, see if the bug stays, and repeat on the half that has it. A short JavaScript snippet can also list elements wider than the viewport, as the first example shows.

  7. 7

    Finally, reduce the case. Copy the broken part into a small file (or a Hectal live example) with the fewest elements and rules that still show the bug. Most bugs become obvious in a 10-line case, and if it's really a browser bug, a reduced case is what you'd attach to a bug report. Use DevTools' Changes tab to collect the edits you made while experimenting, then copy them back into your source files.

Explain it without notes

01

Walk through how you debug "my CSS isn't applying".

02

A page scrolls horizontally on mobile. How do you find and fix the cause?

03

Why might z-index: 9999 not bring an element to the front?

04

Why does a child's margin-top sometimes move its parent, and how do you stop it?

Practice

01

A card in a flex row overflows when its title is a long word. Write the CSS fix and explain it.

02

Write a debug stylesheet rule that shows the boxes of every element inside .layout without changing the layout.

Trade-offs

  • ↔

    Fixing in DevTools is fast but temporary; always copy the change back to source (the Changes tab helps) and check it in the real file's context.

  • ↔

    overflow: hidden can hide a symptom quickly, but it also clips focus rings, shadows and dropdowns, and the real cause is still there.

Done when you can

  • I can tell whether a rule doesn't match, loses, is invalid, or has no effect.

  • I can read the Computed tab and the box model diagram.

  • I can use the Flexbox and Grid overlays.

  • I can find what causes horizontal scrolling.

  • I know the signatures of the common bugs: min-width: auto, percentage heights, z-index, image gaps, margin collapse.

  • I can reduce a bug to a small test case.