/methodology
How the scan works
slop-detect scores a page against a fixed, weighted catalogue of the CSS and copy tells that Cursor, v0, Lovable, and Bolt converged on. It runs in real headless Chromium against the page's own computed styles. Same page in, same score out: no model, no randomness, auditable, reproducible, safe to gate CI on.
A fingerprint, not a verdict. Everyone uses AI now; the score measures how generic the result reads, nothing about the team behind it. The full positioning against ML detectors and Lighthouse lives at /compare; the live catalogue is at /api/patterns (definitions·2026.09).
The four axes
A page can be slop in more than one way. slop-detect scores four independent axes; the two slop axes want a low score, the system and AEO axes want a high one.
| Axis | What it measures | Polarity |
|---|---|---|
| design slop | The original CSS fingerprint: 27 weighted tells AI builders converged on — slop fonts, purple CTAs, gradient text, glassmorphism, bento grids. | lower is better |
| copy slop | 9 text tells on the page's own prose: buzzword density, em-dash overload, the "not just X, it's Y" antithesis, filler openers. | lower is better |
| system · DESIGN.md | Does the page honor its own declared design system? Named, contestable drift against a DESIGN.md, never a verdict. | higher is better |
| AEO · agent-readable | Can AI engines fetch, read, and cite the page? 8 weighted checks against the live crawler registry. | higher is better |
Install
Nothing to set up for a one-off scan. npx fetches the CLI, opens the page in a real browser, and prints the score.
# one-off scan, zero install
npx slop-detect https://your-site.com
# or install the CLI globally
npm i -g slop-detect
slop-detect https://your-site.comThe CLI
Score one or many URLs. The flags compose: axes, presets, the system axis, CI gating.
slop-detect <url> [...urls] # score one or many pages
--copy # also score the copy axis
--axes all # design + copy together
--design-md auto # check against <origin>/DESIGN.md
--preset strict # score a curated subset
--remote # scan via the API, no local browser
--json # machine-readable output
--fail-on heavy # exit non-zero to gate CI--remote scans through the API and needs no local Playwright install, so it is the right default in CI and on air-gapped runners.AEO · agent-readable
The AEO axis asks a different question: can AI engines actually fetch, read, and cite this page? A page can look perfectly human-made and still be invisible to ChatGPT, Claude, or Perplexity because it blocks their crawlers or serves JS soup. 8 weighted checks, total weight 100. The four required checks are the fundamentals; the rest are recommended bonuses. Higher is better: AI-Ready (≥80), Partial (≥50), Invisible (<50).
| Check | Weight | Severity |
|---|---|---|
| Page is reachable (HTML 2xx) | 10 | required |
| AI crawlers are not blocked (GPTBot gets 2xx) | 25 | required |
| robots.txt does not Disallow AI crawlers | 20 | required |
| Page is indexable (no noindex) | 15 | required |
| Markdown twin served at <url>.md | 10 | recommended |
| HTML advertises the markdown twin (Link rel=alternate) | 8 | recommended |
| /llms.txt published at the site root | 7 | recommended |
| HTML sends Vary: Accept (content-negotiation aware) | 5 | recommended |
POST /api/aeo with { "url": "https://slop-detect.com/docs" }.The system axis · DESIGN.md
The absolute slop score asks "does this look AI-generated?" The system axis asks the durable question: does the page honor its own declared design system? Point it at a DESIGN.md (Google Labs' open spec: colors, typography, radii) and it reports drift: fonts in use that aren't declared, CTA or surface colors off the palette, radii off the scale. A bespoke site checked against its own system scores Aligned, never "slop". Every drift item is a named, contestable signal.
slop-detect https://your-site.com --design-md auto
◳ system Drifting 62/100 (DESIGN.md)
drift Inter is in use but is not declared in the system
drift the CTA fill is off the declared palette
drift a 16px card radius is off the declared 4 / 8 / 12 scaleWeb & REST API
One curl away. POST a URL, read back the grade, score, verdict, and definitions version.
# score a page on the design + copy axes
curl -s -X POST https://slop-detect.com/api/scan \
-H 'Content-Type: application/json' \
-d '{"url":"https://your-site.com","axes":["design","copy"]}' | jqendpoints
| Endpoint | What it returns |
|---|---|
| POST /api/scan | Score a URL on the design (and optional copy) axis. Add "designMd": true for the system axis, "preset" for a subset. |
| POST /api/aeo | Score whether AI engines can fetch, read, and cite a page. Higher is better. |
| GET /api/patterns | The live pattern catalogue: id, label, category, weight, and the definitions version. |
| POST /api/fix-prompt | Assemble a paste-ready prompt that de-slops a page, scoped to its heaviest tells. |
| GET /?mode=agent | A machine-readable capability document (capabilities, endpoints, auth, discovery) in one fetch. |
Auth. No key needed for normal use. GET endpoints are public; POST is per-IP rate-limited (scan 6/min from the web, 3/min server-to-server). Pass X-API-Key or Authorization: Bearer for higher, per-key limits: free 10/min, pro 60/min, unlimited. Foreign browser origins are rejected without a key; the CLI and MCP are not.
Continuity & directory
Detection is free and stateless. The optional continuity layer remembers a domain, re-scans it daily, and emails you when it regresses to slop or drifts off its DESIGN.md between redesigns. list: true also opts the domain into the public, crawlable directory with a real backlink.
| Endpoint | What it returns |
|---|---|
| POST /api/watch | Register a daily re-scan with regression + DESIGN.md-drift email alerts (double opt-in). |
| GET /api/watch/confirm | Confirm the emailed opt-in link and switch alerts on. |
| GET /api/sites | The public, owner-gated directory dataset of listed domains. |
| GET /api/stats | Aggregate scan stats: total scans, median score, share carrying slop. |
| POST /api/dashboard/link | Email a single-use magic sign-in link for the multi-domain dashboard. |
Programmatic · the core engine
@slop-detect/core is the pure, runtime-agnostic engine: it runs in Node, a Cloudflare Worker, or the browser, and knows nothing about fetching a page. You open the page, run each pattern's detect() in-page, then hand the triggered results to the scorer.
import { scorePatterns, PATTERNS, DEFINITIONS_VERSION } from '@slop-detect/core';
// core is pure: YOU open the page (Playwright/Puppeteer) and run each
// pattern's detect() in-page, then hand the triggered results to the scorer.
const summary = scorePatterns(results);
console.log(summary.score, summary.grade, summary.tier);
console.log(PATTERNS.length, DEFINITIONS_VERSION); // 27 2026.09Presets & agents
Not every audience cares about the same tells. A preset scores a curated subset or weighting without forking the ruleset. Pass --preset on the CLI or { "preset": "strict" } to the API.
| Preset | Scores |
|---|---|
| full | All patterns at their default weights. The canonical score. |
| strict | Only the highest-signal smoking guns (weight ≥ 5). Fewer false positives — good as a hard CI gate. |
| marketing | Brand-surface tells a marketer can act on: fonts, colours, gradients, hero treatment. Skips structural/code-level signals. |
| minimal | The three most-cited dead-giveaways only: slop fonts, VibeCode purple, gradient text. |
MCP · self-audit before shipping
slop-detect-mcp is a Model Context Protocol server that lets an agent scan a page before it ships and pull the fix prompt back into the loop. Four tools: scan_page, check_aeo, check_design_system, fix_prompt.
{
"mcpServers": {
"slop-detect": { "command": "npx", "args": ["-y", "slop-detect-mcp"] }
}
}CI gate · GitHub Action
The action scans a deploy-preview URL, posts a sticky PR comment with the grade and triggered patterns, and fails the check when slop creeps above your threshold. fail-under takes a number (fail if the score exceeds it) or a letter grade. Leave it empty for report-only mode.
- uses: ravidsrk/slop-detect/packages/[email protected]
with:
url: ${{ steps.preview.outputs.url }} # your deploy-preview URL
fail-under: 'B' # fail if the grade drops below BTiers & grades
The 0-100 score is the source of truth; the tier and letter grade are derived from it. Lower is cleaner. The grade bands are read from the engine, so they cannot disagree with the scorer.
- A+0-2
- A3-5
- A-6-9
A point of view. The slop fingerprint is absent.
- B+10-14
- B15-19
- B-20-23
- C24-27
Good bones, a few template tells creeping in.
- D+28-33
- D34-39
- F40-100
Wears the AI starter kit head to toe.
Empty is better than fake. Show the product, don't decorate around it.
Scan a page and read its fingerprint in about eight seconds.