Rule handbook metadata
- Rule
DET.CARD.TITLE· lanedeterministic- Page status
- current
- page_version
f4704f39e3fb8cee13da9d206f39568a52943390bd91ee4b895c74bd9a4a583f- generated_at
- 2026-05-19T19:12:00.000Z
- registry_fingerprint
2ce40848effce579d3e4879f6ca85535183a14db201870a6c007da424624550c
How this rule is fixed
Pilot deterministic fixer: handbook_after · harness mode standalone · verify expect_rule_clean.
- Harness:
apply-harness-fixture-remediation.pycopies the After example HTML from this handbook page onto the defect fixture, theninvoke-det-ruleset-remediation-verify.shre-audits and expects zero findings for this rule. - Production sites:
run-website-ux-remediation-loop.shrunshandbook_html_patch(HTML patches inlib/ux-deterministic-fixers/fixers/patches/) before invoking the Cursor agent when the quality gate still fails.
Detection module: docs/design/ux-audit/deterministic-design-rules.md#det-card-title. Scroll down for Before / After examples and Evidence and remediation steps.
Purpose
Outcome and feature tiles in Kitchen Sink use .forge-card (often with .breathe-link or .breathe-static, sometimes .card-amber) and Bootstrap .card shells in handbook layouts. Screen-reader users and sighted scanners both need a named card: a short label that states what the tile is about before body copy or actions.
This deterministic rule scans card roots (.forge-card, .card, [data-card], *-card preview shells) and requires at least two characters of visible title text from:
- Headings
h1–h6inside the card (not inside a nested card), .card-title,.card-label, or[class*="card-title"],[role="heading"]with visible text,aria-labelon the card root, oraria-labelledbypointing at visible title text elsewhere.
Excluded: cards inside global nav, pagination, breadcrumbs, menus, cookie banners, and toolbar cards (data-card-kind="toolbar", .toolbar-card, or [role="toolbar"] descendants).
Plan: Inventory card grids on landing and handbook pages. Do: Add a heading or .card-label + title pair in every .forge-card cell. Check: Run the auditor metrics phase (cardTitleReport) or spot-check with the accessibility tree. Adjust: When icons carry meaning, wire aria-labelledby instead of leaving only decorative body copy.
Passing signals
.forge-cardtiles includeh3/h4/h5(Forge display scale) orp.card-labelplus a heading, matching showcase patterns ingenerator/pages/_for_agents_content.py.- Link cards use
<a class="forge-card breathe-link" href="…">with a visible title inside the anchor; the link name matches the card title. - Static tiles use
<div class="forge-card breathe-static">withcard-labeleyebrow andh5title abovep.forge-supportbody copy. - Bootstrap
.cardblocks expose.card-titleor a section heading in.card-headerwith non-empty text (≥ 2 characters after trim). - Icon-forward tiles set
aria-label="…"on the card root oraria-labelledbyreferencing a visibleidon title text (pattern used when the visible label sits outside the card border). - Nested cards: title elements inside an inner card do not satisfy the outer card unless the outer card has its own title or
aria-labelledby. metrics.cardTitleReport.violationsis empty after crawl.
Failing signals
.forge-card(or.card) contains onlyp.forge-supportor bullet lists with no heading,.card-title,.card-label, oraria-*name.- Body copy starts with a long paragraph while the “title” is implied by bold styling on a
<span>(not a heading or sanctioned title class). aria-labelledbyreferences a missingidor an element with fewer than two visible characters.- Title text lives only in an
alton a decorative image with no visible heading (image alt does not count for this DOM scan). - Duplicate unnamed cards in a grid (auditor caps at eight findings per page; evidence
missing_card_title card="div.forge-card.breathe-static"). - Severity: major for each card root missing an accessible name (
defaultSeverity: major,MIN_CARD_TITLE_CHARS = 2).
Before example
Before (failing example)
Govern intent, delegate safely, and ship with evidence—without a visible card title.
Learn moreFailing KS markup: static outcome tile with support copy only—no heading, .card-label, or accessible name on the card root.
After example
After (passing example)
Outcome
Governed delivery
Shape intent, delegate safely, and release with reviewable evidence.
Learn morePassing KS markup: canonical Forge card anatomy—eyebrow label, display heading, then support copy (same grid cell as showcase “Static” card).
Passing link-card variant (amber accent) for comparison:
<div class="col-md-4">
<a class="forge-card card-amber breathe-link" href="#methodology">
<p class="card-label">Methodology</p>
<h5 class="mt-2 mb-1">Forge SDLC</h5>
<p class="forge-support mb-0">Human-owned, agent-executed delivery spine.</p>
</a>
</div>
Evidence and remediation
Evidence: Metrics field cardTitleReport.violations[] with kind: "missing-card-title", selectorHint (tag + classes + optional [@hash]), and className. Findings surface as major accessibility issues with evidence missing_card_title card="…" and page URL when available. Message: A card is missing a visible title; add a heading, .card-title, or wire aria-labelledby / aria-label.
Remediate (in order):
- Add
p.card-label(eyebrow) andh5(orh3/h4in denser grids) as the first substantive content inside.forge-card. - For Bootstrap
.card, use.card-titlein.card-bodyor a heading in.card-header. - When the visible title must sit outside the card chrome, set
aria-labelledbyon the card root to the title element’sid(keep referenced text visible and ≥ 2 characters). - For compact icon tiles with no visible heading, set a concise
aria-labelon the card root (do not rely on iconaltalone). - Do not count nested
.forge-cardtitles toward an outer wrapper—give each card root its own name. - Re-run
analyze-website-ux.mjson pages with card grids; pair withDET.CARD.ACTION_LIMITwhen fixing CTA-heavy tiles.