Rule handbook metadata
- Rule
DET.LANDMARKS.REQUIRED· lanedeterministic- Page status
- current
- page_version
881b1032b396bfc9b7f6683f951150c8268bc36f73a54f7d4ed8523f847bd708- generated_at
- 2026-05-25T12:00:00.000Z
- registry_fingerprint
aef1de6082cf0f50d463783c843dee0ffb9132fbd5ed4ea6e5bb3f031f359c72
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-landmarks-required. Scroll down for Before / After examples and Evidence and remediation steps.
Purpose
Screen readers, keyboard users, and skip-link targets rely on document landmarks to jump directly to the primary content column, global navigation, and site chrome. Kitchen Sink handbook shells (showcase_page, handbook_page), marketing layouts (landing_page, marketing_page), and app shells emit predictable anatomy: header.site-header or header.landing-header, aside.forge-sidebar with section rails, main#main, and closing footer / .ks-site-footer-region (Ksf).
This deterministic rule samples the live DOM during the metrics phase. It counts visible, non-aria-hidden landmarks at document level (not nested inside main, article, aside, or section). A page passes when:
- Exactly one
mainor[role="main"]wraps the primary reading column. - When ≥ 2 non-auxiliary navigational links sit outside
main(in header, nav, aside, or sidebar chrome), at least onenavor[role="navigation"]landmark exists outsidemain. - When KS header chrome is detected (
.site-header,.landing-header,header.site-header,[data-shell-region="header|banner"]), a single document-levelheaderor[role="banner"]is present. - When KS footer chrome is detected (
footer,.ks-site-footer-region,.fs-footer,[data-shell-region="footer"]) outsidemain, a document-levelfooteror[role="contentinfo"]is present. - No duplicate document-level banner or contentinfo landmarks.
Plan: Map layout contracts to landmark roles before shipping HTML. Do: Use semantic elements KS layouts already name in Python (layouts.py, components.py). Check: landmarksReport.violations is empty. Adjust: Replace styled div shells with header / main / nav / footer; demote extra main or nested section headers that duplicate banner/contentinfo.
Passing signals
main id="main"with classdoc-main,fs-landing-main, or app workspace class wraps the primary content column — one visible root only.header.site-header(handbook/showcase masthead) orheader.landing-headerwithnav.landing-header-navbar/nav.landing-navprovides the document banner and, when global links exist, the navigation landmark outsidemain.aside.forge-sidebarcontainsnav.nav-scrollwitharia-label="Sections"or"Primary navigation"— sidebar wayfinding counts as a nav landmark outsidemain.footer.ks-site-footer-region(Ksf) orfooter.fs-footeroutsidemainexposes contentinfo when footer chrome is contracted at page scope.- In-page
nav.forge-toc(on-this-page TOC) and sectionheaderelements insidemaindo not inflate top-level banner counts — only document-level chrome is measured. - Skip links,
#mainhash targets, and auxiliary anchors (Skip to content,#top) are excluded from the nav-link threshold.
Failing signals
missing-main: Primary content sits indiv.doc-main,article, or a Bootstrap column with nomain/[role="main"]landmark (main_landmark_count=0).duplicate-main: Two or more document-levelmainlandmarks — for example a workspacemainplus a marketing band also taggedmain.missing-nav:header.site-headeror header chrome exposes ≥ 2 global links (Overview,Docs,Trust, …) but links are bare anchors with no wrappingnavoutsidemain(nav_applicable=true,nav_landmark_count=0).missing-banner: Visible.site-headeror.landing-headerexists outsidemainbut the root is adivinstead ofheader/[role="banner"].missing-contentinfo:.ks-site-footer-regionor footer chrome sits outsidemainas adivwith no nested or siblingfooter/[role="contentinfo"]at document level.duplicate-banner/duplicate-contentinfo: Multiple top-levelheaderorfooterelements outside nested sections — common when a page-level masthead and a heroheaderboth register as banners.
Before example
Before (failing example)
Failing KS markup: handbook-style shell uses styled div chrome and a content column without main; global header links lack a nav landmark; footer band is a div outside the content column.
After example
After (passing example)
F Forge
Handbook
Chapter
Body
One main landmark; sidebar nav and header/footer chrome are exposed to assistive tech.
Passing KS markup: same handbook anatomy with semantic landmarks — header.site-header, nav in the sidebar rail, main#main.doc-main, and footer.ks-site-footer-region (Ksf) outside the content column.
Evidence and remediation
Evidence: Metrics phase emits landmarksReport with counts (mainCount, navApplicable, navLandmarkCount, topBannerCount, topContentinfoCount, outsideMainNavLinkCount) and violations[] (kind: missing-main, duplicate-main, missing-nav, missing-banner, duplicate-banner, missing-contentinfo, duplicate-contentinfo). Findings use area accessibility, severity minor, and evidence such as main_landmark_count=0 or chrome_header_without_banner=true. Capture the accessibility tree or a landmark overlay screenshot for the audited URL.
Remediate (in order):
- Establish one
main— wrap the primary reading/workspace column in<main id="main">(or a single[role="main"]); remove duplicate mains from hero bands or nested layouts. - Landmark the global nav — when ≥ 2 wayfinding links live outside
main, wrap them in<nav aria-label="…">inheader.landing-header,aside.forge-sidebar, or a dedicated shell rail; do not rely on bare anchor lists in header chrome. - Use semantic header/footer shells — change
.site-header/.landing-headerroots fromdivto<header>; expose Ksf footer chrome as<footer class="ks-site-footer-region">or nest<footer>inside the contracted footer region when the outer wrapper must stay adivfor hash attrs. - Demote nested headers — section titles inside
mainmay use<header>only when they will not register as a second document-level banner; prefer heading elements insidesectionfor in-page bands. - Re-audit — run
analyze-website-ux.mjson handbook, marketing, and app URLs; confirmlandmarksReport.violationsis empty and skip-link#mainresolves to the sole main landmark.
Related rules
DET.CHROME.BOUNDARY— once landmarks exist, chrome regions must visually separate from the reading canvas.DET.APP.PERSISTENT_CHROME— app shells keep the same landmarked regions across crawled routes.DET.NAV.FOCUS_ORDER— keyboard tab order must follow the landmark structure screen readers announce.DET.NAV.BREADCRUMB— breadcrumbnavinside header content complements sidebarnavlandmarks.DET.SECTION.HEADING— heading hierarchy insidemainmust remain coherent after landmark fixes.DET.PAGE.MODE— layout mode (handbook, marketing, app) predicts which landmark pattern applies.