---
title: "Remove selectors that match nothing · Agent Wranglers"
url: https://agentwranglers.se/playbook/html-css/checks-in-code/unused-selectors
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Remove selectors that match nothing

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/html-css/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Checks in code](https://agentwranglers.se/playbook/html-css/checks-in-code)
- Rule: Fail the build when a class or attribute selector in the stylesheets matches no element in any built page. The only exceptions sit on one list of states and classes that scripts add.
- How: Dead CSS makes every later change slower, because nobody dares delete what they cannot trace. Collect every class used in the built HTML and the templates, and compare them with the selectors. Keep the exception list in one file and give each entry its reason. The check that one class styles one kind of element is in [Give every module its own class names](https://agentwranglers.se/playbook/website/site-architecture/module-class-names).
- Check: The build reports 0 classes that appear in the stylesheets but in no built page and not on the exception list. Adding `.unused-test { }` to a component file stops the build.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import { readFileSync, readdirSync } from 'node:fs';
const ALLOWED_UNUSED = new Set(['basket-toast']); // added by basket.js after a purchase
const pages = readdirSync('dist').filter(f => f.endsWith('.html')).map(f => readFileSync(`dist/${f}`, 'utf8')).join('\n');
const used = new Set([...pages.matchAll(/class="([^"]+)"/g)].flatMap(([, list]) => list.split(/\s+/)));
const styled = new Set([...readFileSync('dist/main.css', 'utf8').matchAll(/\.([a-z][\w-]*)/g)].map(([, name]) => name));
const unused = [...styled].filter(name => !used.has(name) && !ALLOWED_UNUSED.has(name));
unused.forEach(name => console.log(`FAIL .${name} matches no element`));
process.exit(unused.length ? 1 : 0);
```

### Python example

```python
import pathlib, re, sys
ALLOWED_UNUSED = {"basket-toast"}  # added by basket.js after a purchase
pages = "\n".join(p.read_text() for p in pathlib.Path("dist").glob("*.html"))
used = {name for group in re.findall(r'class="([^"]+)"', pages) for name in group.split()}
styled = set(re.findall(r"\.([a-z][\w-]*)", pathlib.Path("dist/main.css").read_text()))
unused = sorted(styled - used - ALLOWED_UNUSED)
for name in unused:
    print(f"FAIL .{name} matches no element")
sys.exit(1 if unused else 0)
```

## Related rules

- [Give every module its own class names](https://agentwranglers.se/playbook/website/site-architecture/module-class-names) in Website

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/html-css/checks-in-code/unused-selectors">Agent Wranglers HTML and CSS playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Fail on unknown and duplicate CSS](https://agentwranglers.se/playbook/html-css/checks-in-code/unknown-and-duplicate-css)
- Next: [Lint the built markup](https://agentwranglers.se/playbook/html-css/checks-in-code/markup-linter)
- [Checks in code](https://agentwranglers.se/playbook/html-css/checks-in-code)
