Quick Answer
CSS breaks for predictable reasons: file paths are wrong, selectors don't match, specificity is too low, or cascade rules override your styles. The fastest way to find the problem is to open DevTools, inspect the element, and check three things: (1) Is the rule applied? (2) Is it being overridden? (3) Does the selector actually target this element? Once you know which of these is failing, the fix is usually obvious.
Why CSS Breaks (And Why It's Hard to Spot)
CSS doesn't throw errors. It silently fails.
You write a rule. Nothing happens. No warning. No stack trace. Just silence.
This is why CSS debugging feels different from JavaScript debugging. You can't rely on error messages. You have to become a detective.
The frustration comes from this: CSS problems usually aren't about the CSS itself. They're about:
- Files not loading at all
- Selectors that don't match what you think they match
- Specificity wars where a more specific rule wins
- The cascade working exactly as designed, just not how you expected
Most developers waste time tweaking values (margin: 20px, margin: 25px, margin: 30px) when the real problem is that the rule isn't being applied at all.
The solution is a diagnostic workflow. Once you know where to look, CSS debugging becomes fast and predictable.
The Diagnostic Workflow: Where to Look First
The order matters. Check file loading first, then selectors, then specificity.
Before you change a single line of code, answer these questions in order:
1. Is the CSS file loading?
Open DevTools (F12 or right-click → Inspect). Go to the Network tab. Reload the page. Look for your CSS file. If it's red or missing, the file path is wrong or the server isn't serving it.
2. Is the rule being applied to this element?
Right-click the element. Click Inspect. In the Styles panel, you'll see all rules that apply to this element. If your rule isn't listed, the selector doesn't match. If it's listed but crossed out, something else is overriding it.
3. Is the value actually changing anything?
Sometimes a rule applies but doesn't visually change the element because the value is already the default, or the property doesn't apply to that element type. For example, width doesn't work on inline elements without display: block or display: inline-block.
This three-step workflow solves 90% of CSS problems in under two minutes.
File Path and Import Issues (The Silent Killer)
The most common CSS problem isn't a CSS problem at all. It's a file path problem.
Your stylesheet isn't loading. The browser never even sees your rules.
How to spot it:
Open DevTools → Network tab → reload → look for your CSS file. If it shows a 404 status or doesn't appear at all, the path is wrong.
Common mistakes:
<!-- Wrong: relative path assumes CSS is in same folder as HTML -->
<link rel="stylesheet" href="styles.css">
<!-- Right: use correct relative path -->
<link rel="stylesheet" href="./css/styles.css">
<!-- Right: use absolute path from root -->
<link rel="stylesheet" href="/css/styles.css">
If you're using a build tool (Webpack, Vite, etc.), the path might be correct in your source but wrong in the compiled output. Check the actual HTML file that gets served, not your source code.
For @import statements:
/* Wrong: path is relative to the CSS file location, not HTML */
@import url("styles.css");
/* Right: if importing from same folder */
@import url("./styles.css");
/* Right: absolute path */
@import url("/css/styles.css");
If a CSS file imports another CSS file, the path is relative to the importing file, not the HTML. This trips up a lot of developers.
Cascade, Specificity, and Selector Problems
Once you know the file is loading, the next problem is usually specificity or selectors.
Specificity wins. Always.
If two rules target the same element, the more specific one wins, regardless of order.
/* Specificity: 0,0,1 (one element selector) */
div { color: blue; }
/* Specificity: 0,1,0 (one class selector) */
.text { color: red; }
/* Specificity: 1,0,0 (one ID selector) */
#main { color: green; }
If all three rules apply to the same element, it will be green. The ID wins.
The problem: Developers often write low-specificity rules and then wonder why they don't work. Or they write high-specificity rules and then can't override them later.
How to debug it:
Open DevTools. Inspect the element. Look at the Styles panel. Rules are listed in order of specificity (most specific at top). If your rule is crossed out, a more specific rule is overriding it. If your rule isn't listed at all, the selector doesn't match.
Selector matching is the real culprit:
/* This targets all divs inside .container */
.container div { color: blue; }
/* This targets divs with class "text" inside .container */
.container .text { color: red; }
/* This targets divs with class "text" that are direct children of .container */
.container > .text { color: green; }
If your element is nested deeper than you think, the child combinator (>) won't work. If your element doesn't have the class you're targeting, the rule won't apply.
Test your selector:
In DevTools console, run:
document.querySelectorAll('.your-selector')
If it returns an empty list, your selector doesn't match anything. If it returns elements you didn't expect, your selector is too broad.
Browser DevTools: Your Real Debugging Partner
DevTools is where CSS debugging actually happens. Learn to use it well.
The Styles panel is your main tool:
- Right-click any element → Inspect
- Look at the Styles panel (usually on the right)
- You'll see every rule that applies to this element, in order of specificity
- Crossed-out rules are being overridden
- Hover over a rule to see which file it comes from
The Computed tab shows the final result:
After all cascade and specificity rules are applied, what's the actual computed value? This tab shows it. It's useful when you're confused about which rule is winning.
Edit in DevTools to test:
You can click any value in the Styles panel and edit it. Changes are temporary (they disappear on reload), but they let you test fixes instantly without saving code.
Use the color picker:
Click the color swatch next to any color value. A color picker opens. This is faster than guessing hex codes.
Check computed values for inherited properties:
Some properties inherit from parent elements. If you set color: red on a parent, all children inherit it unless they override it. The Computed tab shows you which rule is providing each value.
Common CSS Mistakes and How to Spot Them
Mistake 1: Forgetting display property
/* This won't work on inline elements */
span { width: 100px; }
/* Fix: change display */
span { display: block; width: 100px; }
Inline elements ignore width and height. Check the Computed tab. If display: inline, that's your problem.
Mistake 2: Margin collapse
/* Parent and child margins collapse into one */
.parent { margin: 20px; }
.child { margin: 20px; }
/* Result: only 20px margin, not 40px */
/* Fix: use padding on parent or overflow */
.parent { padding: 20px; overflow: hidden; }
This is a CSS feature, not a bug, but it surprises developers. Check the Computed tab to see actual margins.
Mistake 3: z-index doesn't work without position
/* This does nothing */
div { z-index: 999; }
/* Fix: add position */
div { position: relative; z-index: 999; }
z-index only works on positioned elements (position: relative, absolute, fixed, sticky). Check the Computed tab. If position: static, that's why z-index isn't working.
Mistake 4: Overflow hidden clips content
/* This hides anything that overflows the box */
.box { overflow: hidden; }
/* If your content is cut off, this is why */
Check if a parent has overflow: hidden. If so, content outside the parent's bounds gets clipped.
Mistake 5: Units matter
/* These are different */
width: 100%; /* 100% of parent width */
width: 100vw; /* 100% of viewport width (includes scrollbar) */
width: 100px; /* exactly 100 pixels */
If your element is wider than expected, check if you used vw instead of %.
Using Element Armory to Inspect and Compare Styles
When you need to copy styles from a working example or compare your styles to a reference, Element Armory speeds up the inspection process.
Instead of manually reading through DevTools and copying values, you can capture the computed styles of any element instantly. This is especially useful when:
- You're trying to match styles from a design reference
- You want to see all applied styles at once without scrolling through DevTools
- You need to extract styles from a live website to understand how something works
Open Element Armory, click any element, and you get clean HTML and computed CSS. This saves time when debugging by giving you a complete picture of what's actually applied, not just what's in your source files.
Testing Across Browsers and Devices
CSS behaves differently in different browsers. A rule that works in Chrome might not work in Safari or Firefox.
Common cross-browser issues:
- Flexbox and Grid have different default behaviors
- Vendor prefixes (
-webkit-,-moz-) are sometimes required - Some CSS features are newer and not supported in older browsers
How to test:
- Open DevTools → Device toolbar (or press Ctrl+Shift+M)
- Test on different screen sizes
- Test in different browsers if possible
For older browser support:
Check Can I Use before using a new CSS feature. If you need to support older browsers, add vendor prefixes:
display: -webkit-flex; /* Safari, older Chrome */
display: flex; /* Modern browsers */
Performance Issues That Look Like Bugs
Sometimes CSS "doesn't work" because it's too slow to render.
Expensive CSS operations:
- Complex selectors (deeply nested, many combinators)
- Large box-shadows or filters
- Animations on many elements
- Gradients with many color stops
If your page feels sluggish, open DevTools → Performance tab → record → interact with the page → stop. Look for long rendering times. If CSS is the culprit, simplify your selectors or reduce animations.
Building a Personal CSS Debugging Checklist
Save this checklist. Use it every time CSS doesn't work:
- File loading? Network tab → look for CSS file → check status code
- Rule applied? Inspect element → Styles panel → is your rule listed?
- Being overridden? Styles panel → is your rule crossed out?
- Selector matches? DevTools console →
document.querySelectorAll('.your-selector') - Specificity high enough? Count selectors → compare to overriding rule
- Display property correct? Computed tab → check
displayvalue - Position set (for z-index)? Computed tab → check
positionvalue - Vendor prefix needed? Check Can I Use
- Parent overflow hidden? Inspect parent → check
overflowvalue - Units correct? Check if using
%,vw,px, etc.
Most CSS problems are solved by step 4. The rest are edge cases.
Next Steps
Once you've diagnosed the problem, the fix is usually obvious. But if you're building a design system or need to extract styles from a reference, use Element Armory to capture clean, reusable CSS. This turns debugging into a faster workflow where you can see exactly what's applied and copy it when needed.
The key is this: stop guessing. Start diagnosing. DevTools is your tool. The workflow is your method. Speed comes from repetition.
