Skip to content

Detector: add italic-serif display headline + hero eyebrow chip rules #127

@pbakaus

Description

@pbakaus

Background

The current detector catches the AI-default content of a hero (purple gradient text, three-icon-tile feature row, etc.) but does not catch the structural patterns that have become category-defining tells of late-2025/early-2026 AI-generated marketing pages. Two specific shapes are now widespread and unflagged:

  1. Italic-serif display headline. Oversized italic serif (Fraunces / Recoleta / Newsreader-italic / similar) used as the primary h1. Magazine-cover affectation that reads as taste in isolation, but has become the universal "AI startup landing page" hero.
  2. Hero eyebrow / pill chip. Tiny uppercase letter-spaced label sitting as a sibling immediately above an oversized hero headline — e.g. AI-NATIVE WORKFLOWS above Build faster with intelligent workflows. Now appears in nearly every newly-shipped SaaS landing page.

These two patterns frequently co-occur, but each warrants its own rule because each appears in isolation often enough.

A real-world example of both, side by side, lives at ~/code/landing-demo/index.html (the demo target page used at the Design Futures Assembly demo). That fixture should drive the test cases.

Rule 1: italic-serif-display

Detection (browser path, jsdom path)

Flag any h1 (or any element rendered at hero scale) where:

  • font-style === 'italic', AND
  • font-family resolves to a serif face (heuristic: name contains "serif" via inheritance, OR the resolved primary family matches a known serif list, OR font-feature-settings is serif-typical), AND
  • computed font-size ≥ 48px.

The serif-family check is the tricky bit. Three options, in order of preference:

  • Maintain a KNOWN_SERIF_FONTS set (Fraunces, Recoleta, Newsreader, Garamond family, Cormorant, Playfair, Tiempos, Lora, Vollkorn, Spectral, Source Serif, IBM Plex Serif, etc.) and require the primary computed family to be in it OR generic serif to be the next fallback.
  • Use the font-family stack: if any token is serif (the generic), treat the primary as serif. False positives possible (someone declares font-family: 'Inter', serif) but in practice the generic serif fallback only ships with a serif primary.
  • Probe via canvas-measured glyph metrics. Most accurate, most expensive, probably overkill.

I'd start with the first approach. Fixture should cover both "primary face known to be serif" and "primary face unknown but generic serif fallback" cases.

Fixture cases (tests/fixtures/antipatterns/italic-serif-display.html)

Should-flag:

  • h1 with font-family: 'Fraunces', Georgia, serif; font-style: italic; font-size: 88px (the Lumina hero pattern from ~/code/landing-demo)
  • h1 with font-family: 'Recoleta', serif; font-style: italic; font-size: 64px
  • h1 with mixed inline italic emphasis (<h1>Build faster with <em>intelligent</em> workflows</h1> where the em is the italic-serif marker — this is the actual Lumina pattern, hardest to detect, possibly out of scope for v1)

Should-pass:

  • h1 italic but sans-serif (Karla italic at 64px) — italic display in a sans-serif face is rare and usually intentional
  • h1 serif, but not italic, regardless of size
  • h1 italic serif at < 32px (a genuine pull-quote inside body text, not a hero)
  • An <em> inside an otherwise-roman headline (because the headline as a whole is not italic)

Rule 2: hero-eyebrow-chip

Detection

Flag any element where:

  • text content length ≤ 30 chars
  • text-transform: uppercase (or content is already all-uppercase letters)
  • computed letter-spacing ≥ 0.1em (≈1.6px at 16px)
  • computed font-size ≤ 14px
  • it sits as a sibling immediately preceding an h1 (or hero-class element) with font-size ≥ 48px

The sibling-relationship requirement is what distinguishes this from a legitimate uppercase label elsewhere on the page. Reference the existing icon-tile-stack rule for the sibling-relationship pattern (~line 425 in src/detect-antipatterns.mjs).

Fixture cases (tests/fixtures/antipatterns/hero-eyebrow-chip.html)

Should-flag:

  • <div class=\"eyebrow\">AI-NATIVE WORKFLOWS</div><h1 style=\"font-size: 88px\">…</h1> (the Lumina pattern)
  • <span>NEW</span><h1 style=\"font-size: 64px\">…</h1> where span has uppercase + tracking + small size
  • Pill-shaped chip variant (background-color, border-radius: 999px, padding) immediately above oversized h1

Should-pass:

  • An uppercase label not adjacent to an h1 (e.g. table header, caption)
  • Eyebrow text with normal letter-spacing
  • An h1 ≥ 48px without any preceding uppercase sibling
  • The eyebrow pattern above an h1 that's body-sized (not hero)

Implementation checklist (per CLAUDE.md TDD order)

For each rule:

  1. Fixture at tests/fixtures/antipatterns/{rule-id}.html with both columns, ≥4 flag cases and ≥5 false-positive shapes, explicit pixel dimensions for jsdom.
  2. Failing test in tests/detect-antipatterns-fixtures.test.mjs using the snippet-substring pattern.
  3. Rule entry in ANTIPATTERNS (category: slop, with skillSection and skillGuideline).
  4. Pure check function checkXxx(opts) returning findings.
  5. Both adapters: checkElementXxxDOM(el) (browser, getComputedStyle + getBoundingClientRect) and checkElementXxx(el, tag, window) (jsdom, parseFloat(style.width) instead of layout). Wire both into both element loops in src/detect-antipatterns.mjs — browser loop (~1837) and jsdom loop in detectHtml (~2058).
  6. Verify on ~/code/landing-demo/index.html and on the public homepage — the eyebrow rule especially needs a no-false-positive check on the impeccable site itself.
  7. Run all three builds + tests: bun run build && bun run build:browser && bun run build:extension && bun run test.

Skill-side updates

If either rule lands, the SKILL.md / reference/critique.md and reference/audit.md should pick up the new patterns by name. The SKILL.md "Absolute bans" list is a candidate home for italic-serif-display once we're confident it has no register exceptions (note: editorial / magazine register may legitimately want italic-serif headlines — keep this register-aware).

Out of scope for this issue

  • Mesh / radial gradient hero backgrounds
  • Border-radius monotony
  • Generic SaaS feature copy (content rule, separate work)
  • Detecting an italic <em> inside a roman h1 (would require richer DOM walking; revisit after v1)

Demo-driven motivation

This issue exists because the patterns above co-occur on the Lumina demo page used at the Design Futures Assembly Impeccable demo. The page already gets flagged for overused-font (Fraunces, Inter), icon-tile-stack, and low-contrast, but the most visually obvious tell — the Fraunces-italic-88px hero with a tiny uppercase eyebrow above it — slips past the detector. Closing that gap closes the gap between what the audience visually identifies as slop and what the tool reports.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions