/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.11) and the raw markdown twin is at /docs.md.

§ 01

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.

AxisWhat it measuresPolarity
design slopThe original CSS fingerprint: 45 weighted tells AI builders converged on — slop fonts, purple CTAs, gradient text, glassmorphism, bento grids.lower is better
copy slop26 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.mdDoes the page honor its own declared design system? Named, contestable drift against a DESIGN.md, never a verdict.higher is better
AEO · agent-readableCan AI engines fetch, read, and cite the page? 16 weighted checks against the live crawler registry.higher is better
Four-axis telemetry architecture schematic showing Design Slop, Copy Slop, System Compliance, and AEO checks converging into a deterministic grade
FIG. 01 · Four-axis telemetry architecture: headless Chromium DOM extraction feeding design, copy, system, and AEO instruments.definitions·2026.11
live rule explorer · 71 rules (45 design + 26 copy) · 71 shown
slop_fontsdesign·fontsAI-default font stack (Inter / Geist / Space Grotesk / Instrument Serif)+8
slop_fonts_2026design·fontsExpanded 2026 default font stack (Syne / Fraunces / Outfit / Sora / Playfair / Lora / JetBrains Mono)+0
purple_accentdesign·colorsVibeCode Purple — filled indigo/violet CTAs+4
gradient_textdesign·colorsHero gradient text (background-clip:text)+6
gradient_backgroundsdesign·colorsGradient-heavy backgrounds (≥15% viewport across ≥2 sections)+4
accent_stripedesign·layoutColored top/left card borders (the AI em-dash)+6
accent_stripe_2026design·layoutBroadened 2px or bottom colored card borders (D-07)+0
glassmorphismdesign·cssGlassmorphism (backdrop-filter blur on translucent layers)+4
colored_glowsdesign·cssBig colored box-shadow glows (purple/blue/pink)+4
centered_herodesign·layoutCentered hero in generic sans (Inter-style)+4
hero_eyebrow_pilldesign·layoutEyebrow pill above hero ("Now in beta" / "New")+5
all_caps_labelsdesign·fontsAll-caps section labels (retired — replaced by eyebrow_label)+0
eyebrow_labeldesign·fontsTracked or uppercase eyebrow labels above ≥60% of section headings+0
perma_dark_modedesign·colorsPerma dark mode + medium-grey body text+4
icon_card_griddesign·layoutIdentical feature cards with icon on top+4
numbered_stepsdesign·layoutNumbered "1 · 2 · 3" step sequences+3
stat_bannerdesign·layoutBig-number stat banner ("10k+", "99.9%", "$2M+")+3
faq_accordiondesign·layoutFAQ accordion in the lower half+2
gradient_letter_avatarsdesign·imagesGradient-letter avatars (testimonial slop)+5
bento_griddesign·layoutBento-grid wall — mixed-span rounded card grid+4
aurora_mesh_gradientdesign·cssAurora / mesh gradient blobs (blurred glowing backdrop)+5
ai_sparkle_badgesdesign·imagesAI-sparkle badges (✨ / Sparkles "magic" tells)+3
cream_default_bgdesign·colorsCream / beige default page background+7
low_contrast_textdesign·colorsWashed-out grey body text (below WCAG AA contrast floor)+7
crushed_trackingdesign·fontsCrushed letter-spacing on display type+5
oversized_hero_h1design·fontsOversized hero headline (long sentence at display size)+4
nested_cardsdesign·layoutCards nested inside cards+4
flat_type_hierarchydesign·fontsFlat type hierarchy (sizes too close together)+3
scaffold_defaults_unediteddesign·layoutUnfinished page — scaffold defaults left unedited+0
hero_accent_worddesign·fontsSingle inline accent span/italic/color word inside H1+0
template_chrome_2026design·layout2026 template micro-chrome bundle (≥3 of 6 sub-signals)+0
uniform_radius_shadowdesign·cssSingle border-radius (≥12px) and identical neutral shadow across ≥3 size classes+0
editorial_cream_kitdesign·colorsWarm-editorial cream + serif + terracotta/brass or broadsheet hairline kit+0
template_page_orderdesign·layoutCanonical 8-section landing-page sequence order+0
pulsing_status_badgedesign·cssInfinite pulsing "Live / Active / Beta" status dot on marketing copy+0
display_mono_headingdesign·fontsMonospace H1/H2 heading at display size (≥24px) outside devtool context+0
chip_rainbowdesign·colorsMulti-hue badge/pill/tag rainbow (≥6 chips across ≥4 hues separated by >30°)+0
fake_macos_terminaldesign·layoutDecorative macOS traffic-light window chrome above a code/text block+0
fake_logo_bardesign·imagesPlaceholder or duplicate-asset logo bar ("Trusted by" generic SVG/text row)+0
infinite_marquee_tickerdesign·cssInfinite linear horizontal marquee ticker with duplicated child sequence+0
fade_up_monoculturedesign·cssAt-rest fade-up entrance monoculture (≥6 below-fold elements at opacity 0 + translateY)+0
cursor_spotlight_gradientdesign·cssCursor-tracking radial-gradient spotlight overlay (--mouse-x / --mouse-y)+0
uniform_card_hoverdesign·cssIdentical transform/box-shadow hover transition tuple across ≥4 cards+0
section_template_repetitiondesign·layoutRepeated top-level section structural template signature (≥3 identical sections)+0
unthemed_browser_surfacesdesign·cssDark page missing color-scheme: dark or dynamic counters missing tabular-nums+0
buzzword_densitycopyAI buzzword density (leverage / seamless / robust / elevate …)+7
em_dash_overloadcopyEm-dash overload (— used as the default connective)+2
antithesis_constructioncopy"It's not just X — it's Y" / "not only … but also" antithesis+6
filler_openerscopyGeneric filler openers ("In today's fast-paced world …")+5
formulaic_closerscopyEssay-style closers ("In conclusion", "In summary", "Ultimately")+4
rule_of_threecopyRule-of-three adjective tricolons ("fast, reliable, and scalable")+4
whether_yourecopy"Whether you're X or Y" audience-spanning construction+3
unicode_artifactscopyInvisible Unicode or mathematical fake-bold characters in text+0
emoji_bullet_headerscopyEmoji-prefixed bullet/headers (🚀 ✅ ✨ as list markers)+3
hidden_tag_characterscopyHidden instruction characters (prompt-injection or smuggling vector)+0
invisible_characterscopyInvisible characters in text (often humanizer or paste residue)+0
fake_bold_math_alnumcopyMathematical Alphanumeric Symbols used as fake bold/italic in prose+4
chatbot_markup_leakcopyLeaked chatbot citation or internal UI markup (oaicite / attributableIndex /grok-card)+0
chatbot_reply_residuecopyChatbot turn framing, refusal, or UI Chrome pasted into page copy+0
ai_referral_utm_linkscopyLinks copied from an AI assistant (utm_source=chatgpt.com / openai / copilot.com)+0
unfilled_placeholderscopyUnfinished copy (unfilled placeholders)+0
leaked_markdowncopyUnrendered raw Markdown syntax in visible non-code text+0
structural_fake_social_proofcopyPlaceholder avatar services, duplicated avatar assets, or unattributed testimonials+0
bare_antithesiscopyBare contrastive frames ("It's not X, it's Y" / "No X, no Y, just Z")+0
emoji_heading_ratiocopyLead emoji ratio ≥30% across ≥6 headings or list items+0
announced_structurecopyAnnounced essay structure in opening prose ("In this post, we'll explore …")+0
restated_thesis_closercopyClosing paragraph restates H1/opening thesis with ≥50% content-word overlap+0
context_leak_copycopyContext-inappropriate dev/privacy boilerplate ("never leaves your browser")+0
trailing_participial_clausescopyTrailing participial clauses (", ensuring / unlocking / enabling …")+0
sentence_length_uniformitycopyParagraph sentence-length dispersion statistics (mean, SD, CV, IQR)+0
excess_vocabulary_log_oddscopyCorpus-derived Fightin' Words excess vocabulary log-odds score+0
§ 02

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.

bash
# 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.com
§ 03

The CLI

Score one or many URLs. The flags compose: axes, presets, the system axis, CI gating.

bash
slop-detect <url> [...urls]        # score one or many pages
  --copy              # also score the copy axis
  --axes all          # design + copy together
  --aeo               # score the 16-check AEO axis
  --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
tip--remote scans through the API and needs no local Playwright install, so it is the right default in CI and on air-gapped runners.
§ 04

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. 16 checks. 6 required checks carry the scored fundamentals. Recommended checks add the rest of the applicable points. Weight 0 checks are reported and do not change the score. Higher is better: Eligible (≥80), Partially Eligible (≥50), Restricted (<50).

AEO agent-readable verification pipeline schematic
FIG. 02 · AEO verification circuit: 16 checks across fetch access, robots and snippet controls, server-rendered prose, markdown twins, and llms.txt.definitions·2026.11
CheckWeightSeverity
html.reachable · Page is reachable over HTTP (HTML 2xx)10required
edge.access · Edge/WAF allows automated retrieval without challenge or block20required
robots.searchAllowed · robots.txt permits AI search & citation crawlers (RFC 9309)20required
html.indexable · Page is indexable (no global noindex directive)12required
ai.previewControls · Snippet & preview controls permit quoting across Google, Bing, and Apple8required
html.serverTextCoverage · Server-rendered HTML contains primary page prose without JS15required
md.twin · Validated Markdown twin served via negotiation or .md route6recommended
html.linkAlternate · HTML advertises a valid Markdown twin (Link rel=alternate)3recommended
site.llmsTxt · Valid /llms.txt published at site root (H1 + links, non-HTML)2recommended
html.vary · Negotiated responses send Vary: Accept4recommended
ai.manipulation · No prompt injection, hidden instructions, or AI manipulation links0info
agent.operability · Interactive controls have accessible names and no blocking overlays0info
agent.discovery · Agent discovery endpoints (ARD, Agentmap, A2A agent-card, WebMCP)0info
ai.usagePolicy · Machine-readable AI usage preferences (AIPREF Content-Usage, Content-Signal, TDMRep)0info
content.dates · Publication and modification dates are valid and consistent across signals0info
jsonld.hygiene · JSON-LD structured data parses cleanly and matches visible text0info
eating our own cookingslop-detect serves a markdown twin for every page (/docs.md), publishes an llms.txt, and lets AI crawlers in. Run the axis on this very page: POST /api/aeo with { "url": "https://slop-detect.com/docs" }.
§ 05

The system axis · DESIGN.md

The absolute slop score measures how many default-template patterns a page matches. 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.

Side-by-side comparison of declared DESIGN.md tokens versus live rendered DOM drift
FIG. 03 · Relative token verification: comparing declared DESIGN.md typography, palette, and corner radii against live rendered DOM styles.definitions·2026.11
bash
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 scale
§ 06

Web & REST API

One curl away. POST a URL, read back the grade, score, verdict, and definitions version.

bash
# 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"]}' | jq

endpoints

Public REST endpoints
EndpointWhat it returns
POST /api/scanScore a URL on the design (and optional copy) axis. Add "designMd": true for the system axis, "preset" for a subset.
POST /api/aeoScore whether AI engines can fetch, read, and cite a page. Higher is better.
GET /api/patternsThe live pattern catalogue: id, label, category, weight, and the definitions version.
POST /api/fix-promptAssemble a paste-ready prompt that de-slops a page, scoped to its heaviest tells.
GET /?mode=agentA machine-readable capability document (capabilities, endpoints, auth, discovery) in one fetch.
GET /api/healthLive readiness probe reporting engine version, definitions version, and KV health.

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.

§ 07

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.

Continuity and directory endpoints
EndpointWhat it returns
POST /api/watchRegister a daily re-scan with regression + DESIGN.md-drift email alerts (double opt-in).
GET /api/watch/confirmConfirm the emailed opt-in link and switch alerts on.
GET /api/watch/unsubscribeOne-click RFC 8058 unsubscribe endpoint for domain watch alerts.
GET /api/sitesThe public, owner-gated directory dataset of listed domains.
GET /api/statsAggregate scan stats: total scans, median score, share carrying slop.
POST /api/dashboard/linkEmail a single-use magic sign-in link for the multi-domain dashboard.
GET /api/me/exportSelf-serve GDPR/DPDP JSON export of all watches owned by the signed-in session.
POST /api/me/eraseSelf-serve GDPR/DPDP erasure of all watches and email records for the signed-in session.
§ 08

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.

javascript
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); // 45 2026.11
§ 09

Presets & 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.

PresetScores
fullAll patterns at their default weights. The canonical score.
strictOnly the highest-signal smoking guns (weight ≥ 5). Fewer false positives — good as a hard CI gate.
marketingBrand-surface tells a marketer can act on: fonts, colours, gradients, hero treatment. Skips structural/code-level signals.
minimalThe three most-cited dead-giveaways only: slop fonts, VibeCode purple, gradient text.
editorialSuppresses warm-cream and serif editorial kit signals on genuine editorial publications (D-06, D-13).
devtoolSuppresses monospace heading and dark-mode signals on developer and CLI documentation sites (D-14).

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.

json
{
  "mcpServers": {
    "slop-detect": { "command": "npx", "args": ["-y", "slop-detect-mcp"] }
  }
}
Closed-loop engineering workflow connecting in-editor MCP self-audit, GitHub Action PR CI gate, and daily continuity sweep
FIG. 04 · Closed-loop verification: in-editor MCP self-audit before commit, pull-request CI gate on preview URLs, and daily continuity monitoring.definitions·2026.11
§ 10

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.

yaml
- 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 B
§ 11

Tiers & 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.

Clean
score 0-9
  • A+0-2
  • A3-5
  • A-6-9

Few default-template patterns matched.

Mild
score 10-27
  • B+10-14
  • B15-19
  • B-20-23
  • C24-27

Matches a few default-template patterns.

Heavy
score 28+
  • D+28-33
  • D34-39
  • F40-100

Matches many default-template patterns.

Empty is better than fake. Show the product, don't decorate around it.

Scan a page and read its fingerprint in about eight seconds.