Should I test that?

Should I test CSS?

Verdict

No

Do not write automated tests for CSS in a typical web app; lint stylesheets with Stylelint, scope styles to components, and look at each changed screen at a phone width and a desktop width before merging.

Why

Do not write automated tests for CSS in a typical web app; lint the stylesheets and look at each changed screen instead. Blast radius is users, because a broken layout reaches customers, and Change frequency is regularly, because a page's styles change about once a month. Detectability is same-day, because at the widths most visitors use, users report a button pushed off screen within a day, and Reversibility is trivial, because a reverted stylesheet leaves nothing behind. Test cost is heavy: a useful CSS test needs a real browser and screenshot baselines that differ between machines and change with every intentional restyle. Rule R13 gives Do not test. The case is borderline: when the result compares exactly and Test cost falls to moderate, the decision becomes Test minimally.

When the decision changes
WhenDecisionWhy
The stylesheet styles a checkout page, where a sticky element can cover the Pay button on a phoneTest mandatory: an end-to-end test at a phone width that scrolls to the Pay button and clicks itBlast radius rises to money and Reversibility to costly: orders lost while the button is covered do not come back
A cookie consent banner must show its Reject button next to Accept on every screen sizeTest mandatory: an end-to-end test at two widths that asserts both banner buttons are in the viewportBlast radius rises to safety-or-legal: a banner with its Reject button off screen collects consent that EU privacy rules reject
A design-system stylesheet styles components on dozens of screens, and each component renders alone with fixed data in a pinned browser imageTest minimally: one screenshot per component variant, reviewed in each pull requestDetectability moves to eventually and Test cost falls to moderate: a component change reaches dozens of screens, a small spacing or colour shift on a rarely visited screen draws no complaint, and fixed data renders the same pixels on every run
The CSS decides whether a control is visible, such as a menu or a form step, and a Playwright end-to-end suite already covers the flowTest minimally: add a visibility assertion for the control to the existing end-to-end testTest cost falls to moderate: visibility is a yes-or-no result, and the browser setup already exists
The layout breaks only in a browser or at a width nobody on the team opens, such as Safari on iPhone, and no end-to-end suite runs thereTest it differently: track completed signups per browser and per screen width, and alert when one falls behind the othersDetectability moves to eventually: nobody opens that browser or width before merging, and complaints rarely name either, which hides the cause for weeks

What breaks if you don't test

A rule leaks through the cascade. A developer raises the z-index of the sticky header to fix a dropdown, and on phones the header now covers the first field of the settings form, a page users open daily. A user reports it the next morning, and a revert fixes it in minutes.

What you lose if you over-test

A full-page screenshot suite fails on every intentional change, such as a new brand colour. The Playwright documentation warns that rendering varies with the host OS, hardware and headless mode, so baselines made on a Mac fail on Linux CI. Within weeks the team runs --update-snapshots without looking, and the suite passes whatever the pages show.

What to do instead

  1. Run Stylelint in CI. Rules such as property-no-unknown catch the typo that silently drops a declaration.
  2. Scope styles with CSS Modules or Vue scoped styles, so a change reaches only the component you edit.
  3. Before merging, open each changed screen at phone and desktop widths, and attach both screenshots to the pull request.
  4. When the CSS decides whether a control shows, add toBeInViewport from Playwright's assertions to the existing end-to-end test for the flow.

When the answer changes

  • The stylesheet styles a checkout or a consent banner.
  • The styles live in a design system that other teams' screens use.
  • The layout breaks only in a browser or at a width that nobody on the team opens.

Code example

The test that restates the stylesheet

The Stack Overflow question behind this page asks whether to check CSS values with QUnit. The jsdom README says layout is out of its scope. The first test below fails when you change the colour on purpose and passes when the header covers the button. The second runs in a real browser, where Playwright's click fails with a TimeoutError when another element covers the button. I delete the first; the second belongs only in an existing end-to-end suite.

// SettingsForm.test.tsx (Jest + jsdom): guards nothing a user sees
import { render, screen } from '@testing-library/react';
import { SettingsForm } from './SettingsForm';

test('save button is green', () => {
  render(<SettingsForm />);
  const save = screen.getByRole('button', { name: 'Save' });
  expect(getComputedStyle(save).backgroundColor).toBe('rgb(22, 163, 74)');
});

// settings.spec.ts (Playwright): fails when a phone user cannot reach or press Save
import { test, expect } from '@playwright/test';

test.use({ viewport: { width: 390, height: 844 } });

test('Save is reachable on a phone', async ({ page }) => {
  await page.goto('/settings');
  const save = page.getByRole('button', { name: 'Save' });
  await save.scrollIntoViewIfNeeded();
  await expect(save).toBeInViewport();
  await save.click();
});

FAQ

Is it necessary to test CSS styles?

Testing CSS styles is not necessary in a typical web app, because users report a broken layout within a day and a revert removes it. Lint the stylesheets, scope styles to components, and look at each changed screen at two widths.

Should I unit test CSS?

Do not unit test CSS values, because a test that asserts a colour or a margin repeats the stylesheet and fails on every intentional change. Lint the stylesheet with Stylelint instead, and when CSS hides or shows a control, assert toBeInViewport in an existing end-to-end test.

Can Jest test CSS layout?

Jest cannot test CSS layout, because jsdom does not calculate where elements appear. Check layout in a real browser, with Playwright or by eye.

How do I catch CSS regressions without tests?

Catch CSS regressions with Stylelint in CI, component-scoped styles, and pull request screenshots of each changed screen at phone and desktop widths.