Three questions, in this order
Nearly every CSS problem is one of those three, and they have different fixes. Changing the value when the selector never matched is how an afternoon disappears.
- Did my rule match? Open the Styles pane. If your selector is not in the list at all, it did not match — that is a selector bug, not a cascade bug.
- Did it win? If it is listed but struck through, another rule beat it. The winner is above it in the list.
- Is the value doing what I think? The Computed pane shows the final value with an arrow to the rule that produced it.
The Styles pane
- Rules are listed strongest first. The top one wins.
- A struck-through property lost. Scroll up to find what beat it.
- Hover a selector to see its specificity.
element.styleat the top is inline CSS, which beats every stylesheet rule.- A greyed-out rule is inherited from an ancestor.
:hovlets you force:hover,:focus,:activeand:focus-visibleso you can inspect a state without holding the mouse still.
The box model panel
The diagram at the bottom of the Styles pane shows content, padding, border and margin with real numbers. It answers "why is there a gap there" faster than reading any stylesheet — the gap is in one of those four rings, and the panel tells you which.
The overlays
- Grid and Flexbox badges next to an element in the Elements panel draw the tracks, the gaps and the line numbers on the page.
- Paint flashing (Rendering panel) highlights everything that repaints. If large areas flash while you scroll, something expensive is being repainted.
- Layout Shift Regions highlights what jumps — the direct cause of a poor CLS score.
- Emulate CSS media features forces
prefers-color-scheme,prefers-reduced-motionandforced-colorswithout changing your system settings.
Symptoms and their usual causes
| Symptom | Look at |
|---|---|
| My rule is not in the Styles pane | The selector. A typo, the wrong element, a missing class, or the stylesheet did not load at all. |
| My rule is struck through | Specificity, layer order, or an inline style. Check the rule above it. |
| The page scrolls sideways | One element wider than the viewport. 100vw, a negative margin, a long string, a missing min-width: 0. |
z-index does nothing | A stacking context on an ancestor — an opacity, a transform, a filter. |
position: sticky does nothing | No threshold, or an ancestor with an overflow value. |
A fixed element scrolls with the page | A transform or filter on an ancestor. |
| Flex children will not shrink | min-width: auto. Set min-width: 0. |
| A gap I did not ask for | Collapsed margins, an inline-block whitespace gap, or a default line-height. |
height: 100% does nothing | The parent has no definite height. Use 100svh or a grid 1fr. |
| The styles look right and the page does not | The Elements panel, not the file — the DOM may not be what you wrote. |
Quick experiments from the console
$0 is the element selected in the Elements panel; $$("sel") is querySelectorAll. Both only exist in the console, which is exactly where you want them.
// what is wider than the page?
[...document.querySelectorAll("*")]
.filter(el => el.getBoundingClientRect().right > document.documentElement.clientWidth + 1);
// outline everything, to see the boxes
document.body.insertAdjacentHTML("beforeend",
"<style>*{outline:1px solid rgb(255 0 0 / .3)}</style>");
// which ancestors create a stacking context?
(function up(el) {
while (el) {
const s = getComputedStyle(el);
if (s.opacity !== "1" || s.transform !== "none" || s.filter !== "none" || s.isolation === "isolate")
console.log(el, { opacity: s.opacity, transform: s.transform, filter: s.filter });
el = el.parentElement;
}
})($0);
// every animation currently running
document.getAnimations().map(a => [a.animationName || a.id, a.playState]);
Binary search
When you cannot see the cause, halve the problem. Comment out half the stylesheet — does it still happen? Delete half the markup. Keep halving until one rule or one element is left.
It feels crude and it is usually faster than reading, because it produces a fact rather than a theory.
Reproduce it small
Copy the element and its CSS into an empty page. Either the problem follows it — and now you have a five-line test case — or it does not, and the cause is something outside the component: an inherited value, an ancestor's containment, a stacking context.
That answer alone usually tells you where to look next.
Habits that prevent most of it
- Inspect before you edit. Editing on a guess turns one bug into two.
- Change one thing at a time, in devtools, where it is free to undo.
- Keep selectors flat and specificity low. Most cascade bugs are self-inflicted.
- Use cascade layers so group order is explicit rather than emergent.
- When something finally works, understand why before you move on — otherwise you will meet it again.