diff --git a/.gitignore b/.gitignore index ae7ee4424..a111620a5 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,4 @@ tmp/ # PNGs, the old card backup). The canonical generator is `bun run og-image` # (scripts/generate-og-image.js); this dir is throwaway and safe to delete. .og-build/ +tests/oracle/vectors/calls/ diff --git a/tests/oracle/DELTAS.md b/tests/oracle/DELTAS.md new file mode 100644 index 000000000..5daf4a3c5 --- /dev/null +++ b/tests/oracle/DELTAS.md @@ -0,0 +1,7 @@ +# Accepted deltas + +Cases listed here differ from their JS golden on purpose. Each entry names the +case id, what differs, and why it is an improvement. Nothing gets on this list +without review. + +Format: `- \`\`: ()` diff --git a/tests/oracle/README.md b/tests/oracle/README.md new file mode 100644 index 000000000..415f79687 --- /dev/null +++ b/tests/oracle/README.md @@ -0,0 +1,19 @@ +# Oracle: behavior goldens for every `impeccable` verb + +`lib.mjs` runs each case (verb + args + staged workspace + stdin) against an +implementation and captures stdout, stderr, exit code, and named files, with +machine-specific paths and timestamps normalized. + +- `record.mjs` writes goldens from the JS scripts (`skill/scripts`, `cli/bin`). +- `run.mjs` replays the corpus against `$IMPECCABLE_BIN` (or `--js` for a + self-check) and diffs. Byte-equal is the bar; `DELTAS.md` lists reviewed + exceptions. +- `cases/*.mjs` define the corpus (default export: array or async function + returning an array). `workspaces/` holds project fixtures that are copied to + a temp dir per run, so cases can write freely. + +Adding a case: append to the matching `cases/*.mjs`, run +`node tests/oracle/record.mjs `, commit the golden. + +Verb names are the binary's subcommands. `cli-help` and `cli-version` map to +`impeccable --help` / `--version`. diff --git a/tests/oracle/cases/detect.mjs b/tests/oracle/cases/detect.mjs new file mode 100644 index 000000000..ed3c75048 --- /dev/null +++ b/tests/oracle/cases/detect.mjs @@ -0,0 +1,83 @@ +/** + * `impeccable detect` corpus. + * + * Every antipattern fixture is scanned individually in JSON and text mode with + * --no-config (the repo's own .impeccable config ignores tests/fixtures), plus + * directory scans, project-config / DESIGN.md / inline-ignore behaviour from + * the detect-config workspace, and the flag surface (help, scope, quiet, + * no-advisory, errors). + */ +import fs from 'node:fs'; +import path from 'node:path'; +import { REPO_ROOT } from '../lib.mjs'; + +const FIXTURES = path.join(REPO_ROOT, 'tests', 'fixtures', 'antipatterns'); + +export default function cases() { + const out = []; + const entries = fs.readdirSync(FIXTURES, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name)); + + for (const ent of entries) { + const rel = `tests/fixtures/antipatterns/${ent.name}`; + const id = ent.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase(); + out.push({ + id: `detect-fixture-json-${id}`, + verb: 'detect', + args: ['--no-config', '--json', `/${rel}`], + isolateHome: false, + }); + out.push({ + id: `detect-fixture-text-${id}`, + verb: 'detect', + args: ['--no-config', `/${rel}`], + isolateHome: false, + }); + } + + out.push( + { id: 'detect-dir-json-all-fixtures', verb: 'detect', args: ['--no-config', '--json', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-dir-text-all-fixtures', verb: 'detect', args: ['--no-config', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-dir-quiet-all-fixtures', verb: 'detect', args: ['--no-config', '--quiet', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-scope-type', verb: 'detect', args: ['--no-config', '--json', '--scope', 'type', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-scope-layout-text', verb: 'detect', args: ['--no-config', '--scope', 'layout', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-scope-both', verb: 'detect', args: ['--no-config', '--json', '--scope', 'type,layout', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-scope-unknown', verb: 'detect', args: ['--no-config', '--json', '--scope', 'nope', `/tests/fixtures/antipatterns/blinking-cursor.html`], isolateHome: false }, + { id: 'detect-no-advisory-json', verb: 'detect', args: ['--no-config', '--no-advisory', '--json', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-no-advisory-text', verb: 'detect', args: ['--no-config', '--no-advisory', `/tests/fixtures/antipatterns`], isolateHome: false, timeoutMs: 180_000 }, + { id: 'detect-multifile-json', verb: 'detect', args: ['--no-config', '--json', `/tests/fixtures/antipatterns/multifile`], isolateHome: false }, + { id: 'detect-multifile-text', verb: 'detect', args: ['--no-config', `/tests/fixtures/antipatterns/multifile`], isolateHome: false }, + { id: 'detect-framework-vite-json', verb: 'detect', args: ['--no-config', '--json', `/tests/fixtures/antipatterns/framework-vite`], isolateHome: false }, + { id: 'detect-framework-next-tailwind-json', verb: 'detect', args: ['--no-config', '--json', `/tests/fixtures/antipatterns/framework-next-tailwind`], isolateHome: false }, + { id: 'detect-framework-next-modules-text', verb: 'detect', args: ['--no-config', `/tests/fixtures/antipatterns/framework-next-modules`], isolateHome: false }, + { id: 'detect-framework-next-cssinjs-json', verb: 'detect', args: ['--no-config', '--json', `/tests/fixtures/antipatterns/framework-next-cssinjs`], isolateHome: false }, + + // Flag surface and errors + { id: 'detect-help', verb: 'detect', args: ['--help'] }, + { id: 'detect-no-args', verb: 'detect', args: [] }, + { id: 'detect-missing-file', verb: 'detect', args: ['--no-config', 'does-not-exist.html'] }, + { id: 'detect-missing-file-json', verb: 'detect', args: ['--no-config', '--json', 'does-not-exist.html'] }, + { id: 'detect-unknown-flag', verb: 'detect', args: ['--bogus', `/tests/fixtures/antipatterns/blinking-cursor.html`], isolateHome: false }, + { id: 'detect-bad-viewport', verb: 'detect', args: ['--viewport', 'wide', `/tests/fixtures/antipatterns/blinking-cursor.html`], isolateHome: false }, + { id: 'cli-help', verb: 'cli-help', args: [] }, + { id: 'cli-version', verb: 'cli-version', args: [] }, + + // Project config, DESIGN.md, inline ignores (detect-config workspace) + { id: 'detect-config-page-json', verb: 'detect', workspace: 'detect-config', args: ['--json', 'src/page.html'] }, + { id: 'detect-config-page-text', verb: 'detect', workspace: 'detect-config', args: ['src/page.html'] }, + { id: 'detect-config-page-no-config', verb: 'detect', workspace: 'detect-config', args: ['--no-config', '--json', 'src/page.html'] }, + { id: 'detect-config-page-no-design-system', verb: 'detect', workspace: 'detect-config', args: ['--no-design-system', '--json', 'src/page.html'] }, + { id: 'detect-config-dir-json', verb: 'detect', workspace: 'detect-config', args: ['--json', 'src'] }, + { id: 'detect-config-dir-text', verb: 'detect', workspace: 'detect-config', args: ['src'] }, + { id: 'detect-config-dir-dot', verb: 'detect', workspace: 'detect-config', args: ['--json', '.'] }, + { id: 'detect-config-inline-json', verb: 'detect', workspace: 'detect-config', args: ['--json', 'src/inline.html'] }, + { id: 'detect-config-inline-disabled', verb: 'detect', workspace: 'detect-config', args: ['--no-inline-ignores', '--json', 'src/inline.html'] }, + { id: 'detect-config-css-json', verb: 'detect', workspace: 'detect-config', args: ['--json', 'src/styles.css'] }, + { id: 'detect-config-css-text', verb: 'detect', workspace: 'detect-config', args: ['src/styles.css'] }, + { id: 'detect-config-vendor-ignored', verb: 'detect', workspace: 'detect-config', args: ['--json', 'src/vendor/ignored.html'] }, + { id: 'detect-config-from-subdir', verb: 'detect', workspace: 'detect-config', cwd: 'src', args: ['--json', 'page.html'] }, + // A file in one project must not pick up another project's DESIGN.md + { id: 'detect-config-cross-project', verb: 'detect', workspace: 'detect-config', args: ['--json', `/tests/fixtures/antipatterns/blinking-cursor.html`], isolateHome: false }, + ); + + return out; +} diff --git a/tests/oracle/golden/cli-help.json b/tests/oracle/golden/cli-help.json new file mode 100644 index 000000000..146e336dc --- /dev/null +++ b/tests/oracle/golden/cli-help.json @@ -0,0 +1,7 @@ +{ + "stdout": "Usage: impeccable [options]\n\nCommands:\n detect [file-or-dir-or-url...] Scan for UI anti-patterns and design quality issues\n ignores Manage detector ignore rules, files, and values\n help List all available skills and commands\n install Install impeccable skills into your project or global harness\n link Symlink skills from a local checkout or submodule\n update Update skills to the latest version\n check Check if skill updates are available\n\nOptions:\n --help Show this help message\n --version Show version number\n\nCompatibility:\n impeccable skills Legacy namespace; still supported.\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/cli-version.json b/tests/oracle/golden/cli-version.json new file mode 100644 index 000000000..6080c9090 --- /dev/null +++ b/tests/oracle/golden/cli-version.json @@ -0,0 +1,7 @@ +{ + "stdout": "3.6.0\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-bad-viewport.json b/tests/oracle/golden/detect-bad-viewport.json new file mode 100644 index 000000000..9dfbb4549 --- /dev/null +++ b/tests/oracle/golden/detect-bad-viewport.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "Error: --viewport requires a WxH value, e.g. --viewport 390x844\n", + "exit": 1, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-cross-project.json b/tests/oracle/golden/detect-config-cross-project.json new file mode 100644 index 000000000..a228c5b73 --- /dev/null +++ b/tests/oracle/golden/detect-config-cross-project.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/blinking-cursor.html\",\n \"line\": 20,\n \"snippet\": \"Undocumented color #33d17a is outside DESIGN.md colors\",\n \"ignoreValue\": \"#33d17a\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/blinking-cursor.html\",\n \"line\": 58,\n \"snippet\": \"Undocumented color #999 is outside DESIGN.md colors\",\n \"ignoreValue\": \"#999\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-css-json.json b/tests/oracle/golden/detect-config-css-json.json new file mode 100644 index 000000000..f79cfb5b5 --- /dev/null +++ b/tests/oracle/golden/detect-config-css-json.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/styles.css\",\n \"line\": 1,\n \"snippet\": \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/styles.css\",\n \"line\": 2,\n \"snippet\": \"Undocumented color rgba(99, 102, 241, 0.6) is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgba(99, 102, 241, 0.6)\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-css-text.json b/tests/oracle/golden/detect-config-css-text.json new file mode 100644 index 000000000..9804b5af6 --- /dev/null +++ b/tests/oracle/golden/detect-config-css-text.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "\n/src/styles.css\n line 1: [bounce-easing] cubic-bezier(0.68, -0.55, 0.265, 1.55)\n → Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\n line 2: [design-system-color] Undocumented color rgba(99, 102, 241, 0.6) is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n\n2 anti-patterns found.\n", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-dir-dot.json b/tests/oracle/golden/detect-config-dir-dot.json new file mode 100644 index 000000000..e03c4470a --- /dev/null +++ b/tests/oracle/golden/detect-config-dir-dot.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 5,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"faint text\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on p \\\"x\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 3,\n \"snippet\": \"div \\\"Hi\\\" uses verdana; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"verdana\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on div \\\"Hi\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 4,\n \"snippet\": \"p \\\"Brand\\\" uses inter; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"inter\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 6,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"Off palette\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/styles.css\",\n \"line\": 1,\n \"snippet\": \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/styles.css\",\n \"line\": 2,\n \"snippet\": \"Undocumented color rgba(99, 102, 241, 0.6) is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgba(99, 102, 241, 0.6)\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-dir-json.json b/tests/oracle/golden/detect-config-dir-json.json new file mode 100644 index 000000000..e03c4470a --- /dev/null +++ b/tests/oracle/golden/detect-config-dir-json.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 5,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"faint text\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on p \\\"x\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 3,\n \"snippet\": \"div \\\"Hi\\\" uses verdana; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"verdana\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on div \\\"Hi\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 4,\n \"snippet\": \"p \\\"Brand\\\" uses inter; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"inter\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 6,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"Off palette\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"bounce-easing\",\n \"name\": \"Bounce or elastic easing\",\n \"description\": \"Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/styles.css\",\n \"line\": 1,\n \"snippet\": \"cubic-bezier(0.68, -0.55, 0.265, 1.55)\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/styles.css\",\n \"line\": 2,\n \"snippet\": \"Undocumented color rgba(99, 102, 241, 0.6) is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgba(99, 102, 241, 0.6)\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-dir-text.json b/tests/oracle/golden/detect-config-dir-text.json new file mode 100644 index 000000000..5eb6dde6a --- /dev/null +++ b/tests/oracle/golden/detect-config-dir-text.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "\n/src/inline.html\n line 5: [design-system-color] text color rgb(255, 0, 170) on p \"faint text\" is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n [design-system-color] text color rgb(0, 0, 0) on p \"x\" is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n\n/src/page.html\n [low-contrast] 3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n line 3: [design-system-font] div \"Hi\" uses verdana; not declared in DESIGN.md typography\n → A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\n [design-system-color] text color rgb(0, 0, 0) on div \"Hi\" is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n line 4: [design-system-font] p \"Brand\" uses inter; not declared in DESIGN.md typography\n → A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\n line 6: [design-system-color] text color rgb(255, 0, 170) on p \"Off palette\" is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n\n/src/styles.css\n line 1: [bounce-easing] cubic-bezier(0.68, -0.55, 0.265, 1.55)\n → Bounce and elastic easing feel dated and tacky. Real objects decelerate smoothly — use exponential easing (ease-out-quart/quint/expo) instead.\n line 2: [design-system-color] Undocumented color rgba(99, 102, 241, 0.6) is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n\n9 anti-patterns found.\n", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-from-subdir.json b/tests/oracle/golden/detect-config-from-subdir.json new file mode 100644 index 000000000..8e02d754e --- /dev/null +++ b/tests/oracle/golden/detect-config-from-subdir.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 3,\n \"snippet\": \"div \\\"Hi\\\" uses verdana; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"verdana\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on div \\\"Hi\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 4,\n \"snippet\": \"p \\\"Brand\\\" uses inter; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"inter\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 6,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"Off palette\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"overused-font\",\n \"name\": \"Overused font\",\n \"description\": \"Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"Primary font: inter\"\n },\n {\n \"antipattern\": \"pulsing-dot\",\n \"name\": \"Pulsing status dot\",\n \"description\": \"Small pulsing status dots simulate liveness decoratively. Reserve pulse animation for indicators tied to genuinely live, changing data; a static indicator with clear labeling is honest and calmer.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \".dot — 8x8px dot with infinite \\\"blink\\\" animation\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-inline-disabled.json b/tests/oracle/golden/detect-config-inline-disabled.json new file mode 100644 index 000000000..56bd1cf1b --- /dev/null +++ b/tests/oracle/golden/detect-config-inline-disabled.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 5,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"faint text\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 6,\n \"snippet\": \"p \\\"x\\\" uses verdana; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"verdana\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on p \\\"x\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-inline-json.json b/tests/oracle/golden/detect-config-inline-json.json new file mode 100644 index 000000000..690328b61 --- /dev/null +++ b/tests/oracle/golden/detect-config-inline-json.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 5,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"faint text\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/inline.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on p \\\"x\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-page-json.json b/tests/oracle/golden/detect-config-page-json.json new file mode 100644 index 000000000..266fa5bea --- /dev/null +++ b/tests/oracle/golden/detect-config-page-json.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 3,\n \"snippet\": \"div \\\"Hi\\\" uses verdana; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"verdana\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"text color rgb(0, 0, 0) on div \\\"Hi\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(0, 0, 0)\"\n },\n {\n \"antipattern\": \"design-system-font\",\n \"name\": \"Font outside DESIGN.md\",\n \"description\": \"A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 4,\n \"snippet\": \"p \\\"Brand\\\" uses inter; not declared in DESIGN.md typography\",\n \"ignoreValue\": \"inter\"\n },\n {\n \"antipattern\": \"design-system-color\",\n \"name\": \"Color outside DESIGN.md\",\n \"description\": \"A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\",\n \"severity\": \"advisory\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 6,\n \"snippet\": \"text color rgb(255, 0, 170) on p \\\"Off palette\\\" is outside DESIGN.md colors\",\n \"ignoreValue\": \"rgb(255, 0, 170)\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-page-no-config.json b/tests/oracle/golden/detect-config-page-no-config.json new file mode 100644 index 000000000..aff6b56b4 --- /dev/null +++ b/tests/oracle/golden/detect-config-page-no-config.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n },\n {\n \"antipattern\": \"overused-font\",\n \"name\": \"Overused font\",\n \"description\": \"Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"Primary font: inter\"\n },\n {\n \"antipattern\": \"pulsing-dot\",\n \"name\": \"Pulsing status dot\",\n \"description\": \"Small pulsing status dots simulate liveness decoratively. Reserve pulse animation for indicators tied to genuinely live, changing data; a static indicator with clear labeling is honest and calmer.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \".dot — 8x8px dot with infinite \\\"blink\\\" animation\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-page-no-design-system.json b/tests/oracle/golden/detect-config-page-no-design-system.json new file mode 100644 index 000000000..995238d74 --- /dev/null +++ b/tests/oracle/golden/detect-config-page-no-design-system.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/src/page.html\",\n \"line\": 0,\n \"snippet\": \"3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\"\n }\n]\n", + "stderr": "", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-page-text.json b/tests/oracle/golden/detect-config-page-text.json new file mode 100644 index 000000000..0812f9374 --- /dev/null +++ b/tests/oracle/golden/detect-config-page-text.json @@ -0,0 +1,7 @@ +{ + "stdout": "", + "stderr": "\n/src/page.html\n [low-contrast] 3.6:1 (need 4.5:1) — text #ff00aa on #ffffff\n → Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\n line 3: [design-system-font] div \"Hi\" uses verdana; not declared in DESIGN.md typography\n → A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\n [design-system-color] text color rgb(0, 0, 0) on div \"Hi\" is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n line 4: [design-system-font] p \"Brand\" uses inter; not declared in DESIGN.md typography\n → A font is used that is not declared in DESIGN.md typography. Use the documented type system or update DESIGN.md if this is an intentional brand addition.\n line 6: [design-system-color] text color rgb(255, 0, 170) on p \"Off palette\" is outside DESIGN.md colors\n → A literal color is outside the DESIGN.md palette and sidecar tonal ramps. This may be legitimate, but it should be an intentional design-system addition rather than drift.\n\n5 anti-patterns found.\n", + "exit": 2, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-config-vendor-ignored.json b/tests/oracle/golden/detect-config-vendor-ignored.json new file mode 100644 index 000000000..afe20a428 --- /dev/null +++ b/tests/oracle/golden/detect-config-vendor-ignored.json @@ -0,0 +1,7 @@ +{ + "stdout": "[]\n", + "stderr": "", + "exit": 0, + "signal": null, + "files": {} +} diff --git a/tests/oracle/golden/detect-dir-json-all-fixtures.json b/tests/oracle/golden/detect-dir-json-all-fixtures.json new file mode 100644 index 000000000..cd7bc8198 --- /dev/null +++ b/tests/oracle/golden/detect-dir-json-all-fixtures.json @@ -0,0 +1,7 @@ +{ + "stdout": "[\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 52,\n \"snippet\": \"[data-case=\\\"Kinpaku Edge\\\"] — inset box-shadow 3px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 53,\n \"snippet\": \"[data-case=\\\"Patina Edge\\\"] — inset box-shadow 3px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 54,\n \"snippet\": \"[data-case=\\\"Accent Edge\\\"] — inset box-shadow 4px stripe (right)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 55,\n \"snippet\": \"[data-case=\\\"Signal Blue Edge\\\"] — inset box-shadow 5px stripe (top)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 66,\n \"snippet\": \"[data-case=\\\"Chromatic Hex Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 67,\n \"snippet\": \"[data-case=\\\"Named Red Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 68,\n \"snippet\": \"[data-case=\\\"Chromatic Rgb Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 69,\n \"snippet\": \"[data-case=\\\"Chromatic Oklch Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 78,\n \"snippet\": \"[data-case=\\\"Trailing Inset Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 79,\n \"snippet\": \"[data-case=\\\"Trailing Inset Token Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 81,\n \"snippet\": \"[data-case=\\\"Inset Named Token Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 86,\n \"snippet\": \"[data-case=\\\"Two Length Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 87,\n \"snippet\": \"[data-case=\\\"Two Length Trailing Inset Edge\\\"] — inset box-shadow 5px stripe (top)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 88,\n \"snippet\": \"[data-case=\\\"Important Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 89,\n \"snippet\": \"[data-case=\\\"Cascade Override Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 91,\n \"snippet\": \"[data-case=\\\"Color First Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/astro-inset-shadow-stripe.astro\",\n \"line\": 92,\n \"snippet\": \"[data-case=\\\"Color First Var Edge\\\"] — inset box-shadow 4px stripe (left)\"\n },\n {\n \"antipattern\": \"pulsing-dot\",\n \"name\": \"Pulsing status dot\",\n \"description\": \"Small pulsing status dots simulate liveness decoratively. Reserve pulse animation for indicators tied to genuinely live, changing data; a static indicator with clear labeling is honest and calmer.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/blinking-cursor.html\",\n \"line\": 0,\n \"snippet\": \".pass-round-dot — 8x8px dot with infinite \\\"blink-anim\\\" animation\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 10px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-right: 5px + border-radius: 10px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px\"\n },\n {\n \"antipattern\": \"border-accent-on-rounded\",\n \"name\": \"Border accent on rounded element\",\n \"description\": \"Thick accent border on a rounded card — the border clashes with the rounded corners. Remove the border or the border-radius.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-top: 4px + border-radius: 10px\"\n },\n {\n \"antipattern\": \"border-accent-on-rounded\",\n \"name\": \"Border accent on rounded element\",\n \"description\": \"Thick accent border on a rounded card — the border clashes with the rounded corners. Remove the border or the border-radius.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-bottom: 3px + border-radius: 10px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px + border-radius: 10px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-top: 4px\"\n },\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/border-baseline.html\",\n \"line\": 0,\n \"snippet\": \"border-bottom: 3px\"\n },\n {\n \"antipattern\": \"cramped-padding\",\n \"name\": \"Cramped padding\",\n \"description\": \"Text is too close to the edge of its container. Two shapes: (1) an element with its own text where the padding is too low for the font size, and (2) a wrapper with text-bearing children and near-zero padding against a visible boundary (border, outline, or non-transparent background) — children land flush against the boundary line. Add at least 8px (ideally 12–16px) of padding inside bordered, outlined, or colored containers.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"
\\\"pass-split-container\\\": children flush against border on all sides (no inset)\"\n },\n {\n \"antipattern\": \"clipped-overflow-container\",\n \"name\": \"Positioned child clipped by overflow container\",\n \"description\": \"A clipping container (overflow hidden or clip) wrapping an absolutely-positioned child cuts off tooltips, menus, and popovers that need to escape. Let the overflow be visible, or move the positioned layer out of the clip.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"div.box.flag-overflow-hidden clips a positioned child\"\n },\n {\n \"antipattern\": \"clipped-overflow-container\",\n \"name\": \"Positioned child clipped by overflow container\",\n \"description\": \"A clipping container (overflow hidden or clip) wrapping an absolutely-positioned child cuts off tooltips, menus, and popovers that need to escape. Let the overflow be visible, or move the positioned layer out of the clip.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"div.box.flag-overflow-clip clips a positioned child\"\n },\n {\n \"antipattern\": \"clipped-overflow-container\",\n \"name\": \"Positioned child clipped by overflow container\",\n \"description\": \"A clipping container (overflow hidden or clip) wrapping an absolutely-positioned child cuts off tooltips, menus, and popovers that need to escape. Let the overflow be visible, or move the positioned layer out of the clip.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"div.box.flag-overflow-negative clips a positioned child\"\n },\n {\n \"antipattern\": \"clipped-overflow-container\",\n \"name\": \"Positioned child clipped by overflow container\",\n \"description\": \"A clipping container (overflow hidden or clip) wrapping an absolutely-positioned child cuts off tooltips, menus, and popovers that need to escape. Let the overflow be visible, or move the positioned layer out of the clip.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"div.box.flag-overflow-right clips a positioned child\"\n },\n {\n \"antipattern\": \"clipped-overflow-container\",\n \"name\": \"Positioned child clipped by overflow container\",\n \"description\": \"A clipping container (overflow hidden or clip) wrapping an absolutely-positioned child cuts off tooltips, menus, and popovers that need to escape. Let the overflow be visible, or move the positioned layer out of the clip.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"div.box.flag-shadow-utility clips a positioned child\"\n },\n {\n \"antipattern\": \"clipped-overflow-container\",\n \"name\": \"Positioned child clipped by overflow container\",\n \"description\": \"A clipping container (overflow hidden or clip) wrapping an absolutely-positioned child cuts off tooltips, menus, and popovers that need to escape. Let the overflow be visible, or move the positioned layer out of the clip.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/clipped-overflow-container.html\",\n \"line\": 0,\n \"snippet\": \"div.box.flag-overlay-surface clips a positioned child\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text #969696 on bg #3b82f6\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.2:1 (need 4.5:1) — text #969696 on #3b82f6\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text #b4b4b4 on bg #10b981\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.2:1 (need 4.5:1) — text #b4b4b4 on #10b981\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.7:1 (need 4.5:1) — text #c8c8c8 on #ffffff\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"2.1:1 (need 4.5:1) — text #505050 on #1e1e1e\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text #808080 on bg gradient(#3b82f6, #8b5cf6)\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.1:1 (need 3:1) — text #808080 on #8b5cf6\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text #666666 on bg gradient(#3b82f6, #8b5cf6)\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.4:1 (need 4.5:1) — text #666666 on #8b5cf6\"\n },\n {\n \"antipattern\": \"gradient-text\",\n \"name\": \"Gradient text\",\n \"description\": \"Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"background-clip: text + gradient\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"2.2:1 (need 4.5:1) — text #5b4f44 on #1f1a15\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"2.1:1 (need 4.5:1) — text #6c7280 on #374151\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text #5c5449 on bg #b6322d\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.2:1 (need 4.5:1) — text #5c5449 on #b6322d\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text-gray-400 on bg-blue-500\"\n },\n {\n \"antipattern\": \"gray-on-color\",\n \"name\": \"Gray text on colored background\",\n \"description\": \"Gray text looks washed out on colored backgrounds. Use a darker shade of the background color instead, or white/near-white for contrast.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text #9ca3af on bg #3b82f6\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.4:1 (need 4.5:1) — text #9ca3af on #3b82f6\"\n },\n {\n \"antipattern\": \"ai-color-palette\",\n \"name\": \"AI color palette\",\n \"description\": \"Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"Purple/violet text (#a855f7) on heading\"\n },\n {\n \"antipattern\": \"ai-color-palette\",\n \"name\": \"AI color palette\",\n \"description\": \"Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"text-purple-500 on heading\"\n },\n {\n \"antipattern\": \"ai-color-palette\",\n \"name\": \"AI color palette\",\n \"description\": \"Purple/violet gradients and cyan-on-dark are the most recognizable tells of AI-generated UIs. Choose a distinctive, intentional palette.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"Purple/violet gradient (Tailwind)\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"4.0:1 (need 4.5:1) — text #ffffff on #a855f7\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"1.3:1 (need 4.5:1) — text #cfc9bd on #e8e2d6\"\n },\n {\n \"antipattern\": \"low-contrast\",\n \"name\": \"Low contrast text\",\n \"description\": \"Text does not meet WCAG AA contrast requirements (4.5:1 for body, 3:1 for large text). Increase the contrast between text and background.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"4.1:1 (need 4.5:1) — text #ffffff on #7d7d7d\"\n },\n {\n \"antipattern\": \"gradient-text\",\n \"name\": \"Gradient text\",\n \"description\": \"Gradient text is decorative rather than meaningful — a common AI tell, especially on headings and metrics. Use solid colors for text.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"background-clip: text + gradient\"\n },\n {\n \"antipattern\": \"undersized-ui-text\",\n \"name\": \"Undersized functional text\",\n \"description\": \"Interactive and content-bearing UI text (links, buttons, nav items, labels, table cells, meta rows, timecodes) below 11px is a legibility failure, not a style choice. WCAG sets no absolute pixel floor, but functional text under 11px is a defensible quality bar: it fails on high-DPI and small viewports and it degrades tap and read targets. The 11px floor holds even inside a footer; only non-interactive legal smallprint gets the softer 10px floor. Being ON the DESIGN.md size ramp does not exempt a value here: adding 8px to the ramp launders the token but not the legibility problem, and that is exactly the escape hatch this rule closes. Exempts sup/sub, visually-hidden (sr-only) text, and code/terminal contexts. Decorative letterspaced micro-labels are still functional and stay in scope.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"8px functional text \\\"tick\\\" (below 11px floor)\"\n },\n {\n \"antipattern\": \"skipped-heading\",\n \"name\": \"Skipped heading level\",\n \"description\": \"Heading levels should not skip (e.g. h1 then h3 with no h2). Screen readers use heading hierarchy for navigation. Skipping levels breaks the document outline.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"

\\\"Welcome to Our Platform\\\" followed by

\\\"Gradient text\\\" (missing h2)\"\n },\n {\n \"antipattern\": \"skipped-heading\",\n \"name\": \"Skipped heading level\",\n \"description\": \"Heading levels should not skip (e.g. h1 then h3 with no h2). Screen readers use heading hierarchy for navigation. Skipping levels breaks the document outline.\",\n \"severity\": \"warning\",\n \"category\": \"quality\",\n \"file\": \"/tests/fixtures/antipatterns/color.html\",\n \"line\": 0,\n \"snippet\": \"

\\\"Purple heading text\\\" followed by

\\\"Styled and