Skip to content
AgileLogics
6 min read

Testing and Debugging CSS

Prerequisites: CSS architecture and BEM

CSS bugs are silent — nothing throws an error. A good toolbox finds them anyway.

Debugging in DevTools

When a style "doesn't work", ask in order:

  1. Does the rule match? Select the element; if the rule isn't in the Styles panel, the selector is wrong (or the file didn't load — check the Network panel).
  2. Is the declaration valid? Invalid ones show a warning icon.
  3. Is it overridden? Crossed-out declarations lost the cascade; the Computed tab shows which rule won and why (layer, specificity, order).
  4. Does the property apply here? Chrome flags "inactive" CSS — for example justify-content on a non-flex element, or width on an inline element — and explains why.
  5. Is layout doing something unexpected? Use the Flexbox and Grid overlays, the box-model diagram, and the stacking-context hints.

A quick trick for layout problems:

* { outline: 1px solid rgb(255 0 0 / 40%); }

Outlines take no space, so they show every box without changing the layout.

Linting with Stylelint

npm install --save-dev stylelint stylelint-config-standard
echo '{ "extends": "stylelint-config-standard" }' > .stylelintrc.json
npx stylelint "**/*.css"

Stylelint catches unknown properties, invalid values, duplicate selectors, overly specific selectors, and enforces conventions (naming patterns, no !important, property order via plugins). Run it in your editor and in CI. The W3C validator (or our CSS Validator) is a second opinion on validity — though, as this course noted, validators can lag behind new features.

Visual regression testing

CSS changes can break pages you didn't look at. Visual regression tests take screenshots and compare them with approved ones:

import { test, expect } from "@playwright/test";

test("pricing page looks right", async ({ page }) => {
  await page.setViewportSize({ width: 1280, height: 800 });
  await page.goto("http://localhost:8000/pricing");
  await expect(page).toHaveScreenshot("pricing.png", { maxDiffPixelRatio: 0.01 });
});

Test several viewport widths, dark mode (page.emulateMedia({ colorScheme: "dark" })), and reduced motion. Services like Percy and Chromatic, and Storybook for component states, do the same at scale.

Testing computed styles

For logic that matters — a theme switch, a responsive breakpoint — assert on computed values in an end-to-end test:

<style>
  :root { --bg: rgb(255, 255, 255); }
  [data-theme="dark"] { --bg: rgb(13, 17, 23); }
  body { background: var(--bg); }
</style>
<main><p>Theme test</p></main>
<script>
  const bg = () => getComputedStyle(document.body).backgroundColor;
  console.log("light:", bg());
  document.documentElement.dataset.theme = "dark";
  console.log("dark:", bg());
  console.assert(bg() === "rgb(13, 17, 23)", "dark theme background");
</script>
light: rgb(255, 255, 255)
dark: rgb(13, 17, 23)

The same check in Playwright is await expect(page.locator("body")).toHaveCSS("background-color", "rgb(13, 17, 23)").

Cross-browser checks

Test in Chromium, Firefox, and WebKit (Safari's engine) — Playwright runs all three — and on at least one real phone. Most differences today are in newly shipped features, form controls, and font rendering.

What's next

Try the Advanced Projects, then read What You Should Know Now — and What's Next for where to go from here.