CSS Checker — Example Report Run Your Own Audit →
📋 Example Report — Demo Data

CSS Checker Example: Syntax, Performance and A11y Issues

This example shows the aiwebpageseo CSS Checker for a marketing site stylesheet. The report finds 47 issues including a missing closing brace causing a cascade failure, 8 overuses of !important, missing focus styles and 3 non-composited animations that cause layout reflow.

3
Errors
28
Warnings
16
Info
61
Passed
Errors (3)
ERROR

Missing closing curly brace — line 847

A missing } on line 847 causes all subsequent rules to be parsed incorrectly. This is a cascade failure — multiple styles will not apply as intended. Fix: Add the missing } after the .hero-section rule on line 847.

ERROR

@import rule not at top of stylesheet — line 234

@import inside a stylesheet that has already been parsed is ignored by some browsers. Move all @import rules to the very top of the CSS file, before any other rules. Better: replace @import with a <link> tag in HTML to allow parallel loading.

ERROR

Missing semicolon after declaration — line 312

color: #ffffff is missing a semicolon, causing the next declaration to be silently dropped. Fix: Add semicolons after every CSS declaration.

Performance Issues
WARN

3 non-composited animations (top, left, width)

Animating top, left and width properties forces layout reflow on every frame. Replace with transform: translate() and transform: scaleX() to use the GPU compositor and eliminate jank.

WARN

8 uses of !important — specificity conflicts

!important overrides the normal cascade and makes styles impossible to override cleanly. Refactor affected selectors to use proper specificity instead: .parent .child > div is more specific without !important.

WARN

outline: none on :focus without replacement

Removing focus outlines without providing an alternative makes the page completely unusable for keyboard users. Replace with outline: 2px solid var(--focus-color) or a box-shadow equivalent on all interactive elements.

Why one missing brace destroys everything below it

The error on line 847 is described as a "cascade failure", and the mechanism is worth understanding because it explains why CSS bugs are so much harder to find than JavaScript ones.

CSS never throws an error. There is no console message, no red text, no failed build. The parser encounters something it cannot understand, silently discards it, and attempts to recover — and its recovery rule is crude: skip forward until you find something that looks like a valid rule again.

With an unclosed brace, the parser believes it is still inside .hero-section. Everything that follows is read as a declaration within that rule rather than as new rules of its own. The next selector is treated as gibberish and thrown away, and the next, until the parser stumbles back into sync — possibly hundreds of lines later, possibly never.

signature

The symptom this produces

Everything above line 847 works. Everything below it mysteriously does not — including rules that are perfectly written and that you can see with your own eyes in the file. Developers lose entire afternoons to this, adding !important to rules that are being discarded, not overridden. If a whole region of your stylesheet has stopped applying, look for an unclosed brace above it before you look at anything else.

The missing semicolon on line 312 is the same failure in miniature: the parser reads color: #ffffff and the next declaration as one malformed value, discards the pair, and moves on. One character removed, two declarations gone, no error anywhere. This is what a linter in your editor is for — it catches both before they ever reach a browser.

@import: worse than the report says

The rule about placement is correct — @import must precede all other rules or it is ignored. But the placement is the smaller problem. @import is a performance defect wherever it appears, and it is one of the most expensive lines you can put in a stylesheet.

Here is the chain it creates. The browser's preload scanner — the thing that races ahead of the parser to start downloading resources early — reads your HTML. It finds <link rel="stylesheet"> and fetches the file. It cannot see inside that file. Only once the CSS has arrived and been parsed does the browser discover the @import, and only then can it start the second request.

That is a serial round trip that could have been parallel, and every byte of it is on the critical rendering path, because CSS is render-blocking: the browser will not paint until the CSSOM is complete. Nest an @import inside an imported file and you have three sequential round trips before a single pixel appears.

The fix is the one the report mentions in passing and it is the whole answer: replace every @import with a <link> tag in the HTML. The preload scanner sees them all at once and fetches them in parallel. There is no case in which @import is the better choice for a production stylesheet.

The !important fix — and why the suggested one makes it worse

Eight uses of !important is a symptom of a specificity war, and the instinct the report offers — "use proper specificity instead: .parent .child > div is more specific" — is the instinct that caused the problem in the first place.

Escalating specificity is not the cure for escalating specificity. Write .parent .child > div to beat .child, and the next person must write something more specific still to beat you. Repeat this for two years and every rule in the file is four selectors deep, nothing can be overridden without cloning its ancestry, and !important starts to look like the reasonable option — which is exactly how you got here.

The direction of the fix is downward, not upward. Reduce specificity, do not raise it. Modern CSS gives you three tools for this and they are all widely supported:

ToolWhat it does
@layerCascade layers. A rule in a later layer beats a rule in an earlier one regardless of specificity. Put third-party CSS in a low layer and your overrides in a high one, and a single class beats their four-selector monster — no !important, no arms race.
:where()Contributes zero specificity. :where(.a .b) .c is as specific as .c alone. Ideal for base styles you intend to be overridden.
:is()Groups selectors, taking the specificity of its most specific argument. Convenient, and unlike :where() it does not lower anything — know the difference.

The one legitimate use of !important: overriding inline styles you do not control — typically injected by a third-party widget or an ad script. Inline styles beat every selector, so a stylesheet cannot reach them any other way. That is a genuine exception, and it is not eight rules deep in your own CSS.

Non-composited animations: the pipeline explains the fix

Animating top, left or width is expensive and animating transform is cheap. The reason is the browser's rendering pipeline, and once you see it, every performance rule about animation follows:

Style → Layout → Paint → Composite

The consequence is not just smoothness. A layout-triggering animation is occupying the main thread — the same thread that has to respond when the user taps something. So a janky animation is not merely ugly, it is directly inflating your INP, because when the tap arrives the thread is busy recalculating layout for the twenty-third frame of a slide-in.

two more

will-change and prefers-reduced-motion

will-change: transform promotes an element to its own compositor layer ahead of time — useful, and not free: each layer consumes memory, and applying it to dozens of elements degrades performance rather than improving it. Use it on the specific thing that is about to animate, and remove it afterwards.

And wrap non-essential motion in @media (prefers-reduced-motion: reduce). For users with vestibular disorders, a large parallax or slide animation can cause genuine nausea. Honouring the setting is a WCAG consideration and it is three lines.

outline: none, and the modern replacement

The report is right that this makes the page unusable by keyboard, and it is right about the fix. The refinement worth knowing is which pseudo-class to use, because it resolves the argument that caused the problem.

outline: none is almost always applied because a designer disliked the focus ring appearing when a button was clicked with a mouse. That is a legitimate complaint, and removing the outline entirely is a catastrophic response to it.

:focus-visible { outline: 2px solid …; outline-offset: 2px; }

:focus-visible shows the ring only when the browser judges it useful — keyboard navigation, yes; mouse click on a button, no. Everybody gets what they wanted, and the ring survives.

Two further points. The focus indicator itself has a contrast requirement — WCAG 1.4.11, minimum 3:1 against the adjacent background — so a pale grey ring on a white card is still a failure even though it exists. And under WCAG 2.2, a focused element must not be obscured by other content: a sticky header that covers the element you have just tabbed to is a new, separate failure, and it is extremely common.

CSS is render-blocking, and that is the SEO connection

This is why a CSS report belongs in an SEO tool at all. The browser will not paint a single pixel until it has built the CSSOM — which means every stylesheet in your <head> sits directly on the critical path to First Contentful Paint and Largest Contentful Paint, and LCP is a Core Web Vital.

never

Never block CSS in robots.txt

Googlebot renders pages. Deny it the stylesheet and it sees an unstyled document — it cannot determine what is above the fold, what is visible, or whether the page is mobile-friendly. Old configurations that blocked /assets/ or /wp-includes/ wholesale are still out there, and this is the single most damaging robots.txt mistake there is.

Frequently asked questions

What CSS errors hurt SEO performance?

Render-blocking CSS delays First Contentful Paint and LCP. Large CSS files increase page weight. Non-composited animations (animating top, left, width instead of transform and opacity) cause layout reflow on every frame, degrading INP scores. The CSS Checker identifies all of these automatically.

Why is !important bad for CSS?

Overusing !important creates specificity conflicts that are impossible to override without more !importants, creating a cascade of escalating specificity. It makes stylesheets brittle and hard to maintain. The CSS Checker flags each occurrence with the selector and line number.

What is a non-composited animation?

Animating CSS properties like top, left, width or height forces the browser to recalculate layout on every animation frame (layout reflow). Animating transform and opacity instead uses the GPU compositor, which runs independently of the main thread and produces smooth 60fps animations without blocking interaction.

Why does one missing brace break everything below it?

Because CSS never throws an error. The parser silently discards what it cannot understand and recovers by skipping forward until something looks like a valid rule again. With an unclosed brace it believes it is still inside the previous rule, so every selector after it is read as gibberish and thrown away — possibly for hundreds of lines. The signature is that everything above the line works and everything below it does not, including rules you can see are correct. Look for an unclosed brace before you look at anything else.

How do I get rid of !important without adding more specificity?

Reduce specificity rather than raising it — escalating specificity is what caused the problem. Use @layer, where a rule in a later cascade layer beats one in an earlier layer regardless of specificity, so a single class can override a third-party monster. Use :where(), which contributes zero specificity, for base styles you intend to be overridden. The one legitimate use of !important is overriding inline styles injected by third-party widgets, which no selector can otherwise reach.

Is a janky animation only a cosmetic problem?

No — it directly inflates INP. An animation that changes geometry forces the browser through Layout, Paint and Composite on the main thread, sixty times a second. That is the same thread that must respond when a user taps something, so when the tap arrives the thread is busy recalculating layout. Animating transform and opacity instead lets the browser skip to compositing, which runs on the GPU, off the main thread.

How should I remove the focus ring without breaking keyboard access?

Use :focus-visible rather than :focus. The browser then shows the ring when it is useful — keyboard navigation — and not when a button is clicked with a mouse, which is the complaint that leads people to write outline: none in the first place. Note also that the focus indicator itself needs 3:1 contrast against the adjacent background under WCAG 1.4.11, so a pale ring is still a failure, and under WCAG 2.2 a focused element must not be obscured by other content such as a sticky header.

Related Demo Reports

Run CSS Checker on Your Own Site

Get your real audit report with specific issues, fixes and actionable improvements — free to start.

⚡ Run Free Audit View Plans