EthicsComputerCourses
Join training +91 90532 10052

Debugging CSS with DevTools

CSS · Lesson 64 of 68 Advanced 9 min read Updated

Every developer writes CSS that does not work the first time. Professionals are not the ones with fewer bugs; they are the ones who find the cause fast. Guessing ("maybe add !important?") wastes hours, while the browser's developer tools (DevTools) show exactly which rule won and why.

This lesson teaches you to read the Styles and Computed panes, understand the icons that mark overridden, invalid and inactive declarations, use the box model and layout overlays, emulate dark mode, print and reduced motion, and follow a step-by-step routine for any CSS bug. The steps use Chrome and Edge, which share the same DevTools; Firefox has the same features with slightly different names.

Opening DevTools on the right element

  • Press F12 or Ctrl+Shift+I (Cmd+Option+I on a Mac) to open DevTools.
  • Press Ctrl+Shift+C and click any element on the page to select it in the Elements panel. Right-clicking an element and choosing Inspect does the same.
  • In the Elements panel, press Ctrl+F to search the HTML by text or by CSS selector, for example .card__price.
  • With an element selected, press H to hide it (and again to show it). This is a quick way to find which element is covering another.

The selected element is also available in the Console as $0, which is handy for quick checks:

JavaScript
// In the Console, after selecting an element in the Elements panel
getComputedStyle($0).display;                       // for example "inline"
$0.getBoundingClientRect().width;                   // the rendered width in pixels
document.querySelectorAll('.card__price').length;   // 0 means the selector matches nothing

Reading the Styles pane

The Styles pane lists every rule that matches the selected element, from the winning rule at the top down to the browser's own defaults ("user agent stylesheet") at the bottom. Inherited rules appear further down under headings such as "Inherited from body". Each rule shows its file and line number; click it to jump to the source.

Learn to read these signals:

What you seeWhat it means
A declaration with a line through itIt matched, but another declaration won the cascade
Line through plus a warning iconInvalid: unknown property or a value the browser cannot parse
A greyed declaration with an info iconValid, but it has no effect here, for example width on an inline element
A rule missing completelyThe selector does not match this element at all
A layer name above a ruleThe rule lives in that cascade layer, which affects who wins

Each signal points at a different cause. A struck-through declaration means "go and find what beats it": look higher in the list for the same property. A warning icon means a typo. An info icon means a missing condition, and hovering the icon explains it, for example that justify-content needs display: flex or grid on the same element. A missing rule means the selector is wrong or the stylesheet did not load.

You can click any value to edit it live, tick boxes to switch declarations on and off, and click inside a rule to add a declaration. Nothing is saved to your files; the Changes tool (open it from the three-dot menu under "More tools") lists what you changed so you can copy it back into your code.

Practice: four bugs in one card

Open this example in the editor, then open DevTools on the result and find the cause of each problem before reading the explanation below it. The comments in the CSS describe what the developer wanted.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Find the bugs</title>
  <style>
    body { font-family: system-ui, sans-serif; padding: 24px; }

    /* Wanted: a green price */
    #shop .card p { color: #334155; }
    .card__price { color: #15803d; font-weight: 700; }

    /* Wanted: a rounded card with a light background */
    .card { max-width: 18rem; padding: 16px; border-radius: 12px; backgound: #f0fdf4; }

    /* Wanted: the badge 80px wide and centred */
    .card__badge { width: 80px; text-align: center; background: #fde68a; }

    /* Wanted: the two buttons spread to the two ends of the row */
    .card__actions { justify-content: space-between; margin-top: 12px; }
  </style>
</head>
<body>
  <main id="shop">
    <article class="card">
      <span class="card__badge">New</span>
      <h2>Jaipur block-print bedsheet</h2>
      <p class="card__price">₹1,299</p>
      <div class="card__actions">
        <button type="button">Wishlist</button>
        <button type="button">Add to cart</button>
      </div>
    </article>
  </main>
</body>
</html>

What DevTools shows for each bug:

  1. The price is grey. Select the price: color: #15803d is struck through. The rule above it, #shop .card p, wins because an ID beats any number of classes. Fix it by lowering that selector, for example by setting the grey on .card itself so paragraphs inherit it, or by putting general rules in an earlier layer, not by adding !important. Watch out: .card p would still win, because one class plus one type selector (0-1-1) beats one class (0-1-0).
  2. The card has no background. Select the card: backgound is struck through with a warning icon. It is a typo, so the browser ignored it.
  3. The badge is not 80px wide. Select the badge: width is greyed with an info icon, because a <span> is inline and inline boxes ignore width. Add display: inline-block.
  4. The buttons are not spread out. Select .card__actions: justify-content is greyed with an info icon. It only works on a flex or grid container, so add display: flex.

Four bugs, four different signals, and no guessing.

The Computed pane and the box model

The Computed pane shows the final value of every property after the cascade, inheritance and unit conversion. 2rem appears as 32px, inherit appears as the real colour. Click the arrow next to a property to see every rule that tried to set it, with the winner first. When the Styles pane is crowded, this is the fastest way to answer "where does this value come from?".

At the top of the Computed pane (or the Layout area in some versions) is the box model diagram: content, padding, border and margin with their exact sizes. Hover over each part to highlight it on the page. Mysterious gaps between elements are usually margins, and this diagram makes them visible at once. You can double-click any number to change it.

Layout tools: grid, flexbox and containers

In the Elements panel, elements that are grid, flex or container query containers show a small badge such as grid, flex or container next to their tag. Click the badge to draw an overlay on the page:

  • The grid overlay shows line numbers, track sizes, gaps and area names. Perfect for checking grid-column: 2 / 4 (see Grid Placement & Areas).
  • The flex overlay shows the free space and how items grow or shrink.

Emulating states, media and devices

Many bugs only appear in a particular state. DevTools can fake them:

  • :hover, :focus and friends. In the Styles pane, click :hov and tick a state to force it on the selected element. Now the hover style stays while you edit it.
  • Classes. Click .cls to add or remove classes and see the effect immediately.
  • Phone sizes. Press Ctrl+Shift+M for device mode and choose a width (see Responsive Design & Media Queries).
  • Rendering settings. Press Ctrl+Shift+P, type "Rendering" and open the Rendering tool. It can emulate the print media type, prefers-color-scheme: dark, prefers-reduced-motion: reduce and several colour vision deficiencies. It can also highlight areas that repaint and regions that cause layout shift.

These settings also apply to the Try-it result frame.

A debugging routine for any CSS bug

  1. Select the exact element that looks wrong, not its parent.
  2. Is the rule there? If not, the selector does not match: check spelling, the HTML classes, and whether the file loaded (the Network panel shows a failed CSS request in red).
  3. Is it struck through? Find the winner. Compare layers first, then specificity, then order.
  4. Is it greyed or marked invalid? Read the tooltip and fix the condition or the typo.
  5. Does the value look right in Computed, but the page still looks wrong? The problem is elsewhere: often the parent's layout, overflow, or another element on top. Check the parent, then use H to hide suspects.
  6. Still stuck? Reduce the problem: copy the HTML and CSS into a Try-it editor and delete things until the bug disappears. The last thing you deleted is involved.

Common mistakes

  • Adding !important before understanding the cascade. It hides the real cause and starts a specificity war.
  • Editing in DevTools and forgetting to copy the change. Reloading the page throws it away. Use the Changes tool.
  • Debugging the wrong element. Padding that looks like it is on a card may belong to a wrapper. Check the box model of each level.
  • Testing only your own screen. Use device mode, the Rendering emulations and a real phone.
  • Ignoring browser caching. If a CSS change does not appear, do a hard reload with Ctrl+Shift+R, or tick "Disable cache" in the Network panel while DevTools is open.

Key points

  • Ctrl+Shift+C selects an element; the Styles pane shows every matching rule, winner first.
  • Struck through means overridden, a warning icon means invalid, greyed with an info icon means it has no effect here.
  • The Computed pane shows final values and which rule set them; the box model diagram reveals hidden margins.
  • Grid, flex and container badges draw overlays that make layout problems visible.
  • :hov, .cls, device mode and the Rendering tool emulate states, classes, screen sizes and user preferences.
  • Follow a routine: select, check the rule exists, find what overrides it, read tooltips, then reduce the problem.

Exercise

Copy the four-bug card into the Try-it editor and fix all four problems without using !important and without an ID selector in your fixes. Then use DevTools to force the :hover state on the "Add to cart" button and design a hover style for it while the state is forced. Finally, emulate prefers-color-scheme: dark in the Rendering tool and confirm whether the card changes (it should not, because it has no dark styles yet).

Show solution
CSS
body { font-family: system-ui, sans-serif; padding: 24px; }

.card__price { color: #15803d; font-weight: 700; }

/* the grey moved onto the card and is inherited (was #shop .card p) */
.card { max-width: 18rem; padding: 16px; border-radius: 12px; background: #f0fdf4; color: #334155; }

.card__badge { display: inline-block; width: 80px; text-align: center; background: #fde68a; }

.card__actions { display: flex; justify-content: space-between; margin-top: 12px; }

.card__actions button:hover { background: #15803d; color: white; }

The grey now comes from the card by inheritance, and any rule that targets the price directly beats an inherited value. The typo is fixed, the badge can take a width as an inline-block, and the actions row is a flex container, so justify-content works.

Quick Quiz

1. In the Styles pane, a declaration is struck through with no warning icon. What does it mean?

2. width: 200px on a <span> is greyed out with an info icon. Why?

3. Which tool lets you see your dark mode without changing your operating system setting?

4. Your CSS change does not appear after saving the file. What should you try first?

Learn CSS with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call