An author who wants a rule off for one component has had two choices: put `data-impeccable-ignore` on every instance, or silence the rule (or the file) for the whole project. On impeccable-site #34 that meant eleven attributes on eleven copies of the same 10px label for `undersized-ui-text`, 25 opt-out attributes in all. The count is the problem: the markup carries noise, and the reviewer never sees how much was waived. `detector.ignoreSelectors` is the declared twin of that attribute. One entry, `{ rule, selector }`, waives the rule for every element the selector matches and for that element's subtree, the same waiver the attribute grants the element carrying it: impeccable ignores add-selector undersized-ui-text ".ks-tag" \ --reason "10px mono index labels, confirmed" The waiver is never silent. The engines stamp a waived finding with `ignoredBy: "<selector>"` instead of dropping it; the config layer drops and counts, and every scan prints one line per entry on stderr, in `--json` runs too, so stdout stays the findings array: 3 undersized-ui-text hits ignored by detector.ignoreSelectors on .ks-tag. Where it applies: the browser engine (`BrowserConfig.ignoreSelectors`, also readable from `window.__IMPECCABLE_CONFIG__`), the static HTML engine (`DetectHtmlOptions.ignore_selectors`, and the `ignoreSelectors` option of the wasm `detect_html_source_json` export), the detect CLI, and the design hook. The text engine has no DOM and ignores the key. Entries can be scoped with `files` globs like `ignoreValues`; `--no-config` disables them; `doctor` validates their rule ids alongside `ignoreRules`. Nothing changes for a project without the key: the engines stamp nothing, the CLI prints nothing, the config writer does not add an empty `ignoreSelectors`, and the per-instance attribute keeps working exactly as before. Coverage: `crates/html/tests/selector_ignores.rs` (component, subtree, wrong-rule, `*`, attribute parity), driver tests over the fake DOM, `crates/detect` config tests (normalize, merge, per-target narrowing, the tally), and oracle cases `detect-selector-ignore-*` / `ignores-selector-*` over a new workspace. The eight re-recorded context/doctor goldens differ only in the recognized-detector-keys sentence, which now lists the new key. Assisted-by: Claude Code Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LQBUunp8QttxZqihybNmtL
Live browser assets
Two generated files, both tracked, both rewritten by cargo xtask bundle.
Do not hand-edit either one.
detect-antipatterns-browser.js is the in-page detector bundle: the rule
core (crates/core) compiled to WebAssembly by crates/wasm, concatenated
with the page JS in browser-bundle/ and the module embedded as base64.
crates/live/src/browser_assets.rs embeds it with include_str! and the
live server hands it to the browser as /detect.js, so the binary has to
carry it.
antipatterns.json is the rule registry (crates/foundation/src/registry.rs)
as [{ id, name, category, description }], the same slice
cargo xtask bundle vendors into the extension's extension/detector/. It
is tracked because extension/detector/ is not: a consumer working from a
source checkout or a repo tarball (impeccable.style counts and renders the
rules from it) has no other way to read the registry without a Rust
toolchain.
cargo xtask bundle # rewrites both (and extension/detector/)
cargo xtask bundle --check # fails when either is stale
The other browser scripts the live server serves (live-browser*.js,
modern-screenshot.umd.js) are not copied here: they are embedded straight
from skill/scripts/, the one copy the build also ships to every provider.