Rule handbook metadata
- Rule
DET.PAGE.VIEWPORT· lanedeterministic- Page status
- current
- page_version
e7575c6d91dfcdd8b116c46bf9d0e72834a96f8ba28aef18b49964474c7f0736- generated_at
- 2026-05-25T17:30: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-page-viewport. Scroll down for Before / After examples and Evidence and remediation steps.
Purpose
Without a viewport meta tag, mobile browsers assume a ~980px layout width. Kitchen Sink responsive grids (container-fluid, col-lg-*, d-lg-flex, clamp() headings) collapse incorrectly: sidebars stay desktop-sized, text rivers shrink to a narrow column, and users must pinch-zoom to read body copy.
Kitchen Sink layouts emit the standard tag in every full document shell:
<meta name="viewport" content="width=device-width, initial-scale=1" />
handbook_page, chapter_page, product_page, landing_page, and showcase shells in components/layouts.py include this line in <head> alongside charset, title, and theme CSS.
This deterministic rule runs in the metrics phase. The crawler reads document.querySelector('meta[name="viewport"]')?.getAttribute('content') into metrics.metaViewport. A page passes when that value is present and non-empty after trim. A page fails when the tag is missing or content="".
Plan: Treat viewport meta as part of the document shell contract for every consumer HTML page. Do: Keep the KS <head> block intact in generators and static export. Check: metrics.metaViewport is truthy in crawl JSON. Adjust: Add the meta tag to minimal stubs, legacy templates, or SPA shells that ship HTML without a responsive viewport.
Passing signals
<meta name="viewport" content="width=device-width, initial-scale=1">appears in<head>on every full web document.handbook_pageand sibling layouts inlayouts.pyemit the tag immediately after charset / color-scheme init.- Audit metrics report
metaViewport: "width=device-width, initial-scale=1"(or another non-empty value);DET.PAGE.VIEWPORTproduces no findings. - Mobile layout behaves as authored:
forge-sidebarhides atd-lg-flex,doc-contentuses readable width, Bootstrap breakpoints apply. - Viewport meta sits in
<head>, not duplicated or relocated to<body>.
Failing signals
- Missing tag:
<head>has charset and<title>but no<meta name="viewport">— common in minimal HTML stubs or copied desktop-only templates. - Empty content:
<meta name="viewport" content="">— treated as absent;metrics.metaViewportis falsy. - Wrong attribute:
property="viewport"or a typo inname— the check queriesmeta[name="viewport"]only. - Generator drift: Python layout includes the tag but post-processing or a consumer partial strips
<head>during static export. - SPA shell gap: Client-rendered app serves a bare
index.htmlwithout viewport meta before hydration. - Auditor evidence: "Responsive viewport meta tag is missing." with
<meta name="viewport"> not found.
Before example
Before (failing example)
Methodology
Governed human + agent delivery
Responsive CSS is present, but mobile browsers still assume desktop width.
Outcome
Handbook grids and typography do not reflow correctly on phones.
Failing KS markup: handbook-style shell mirrors handbook_page anatomy (forge-aurora, forge-sidebar, main#main, doc-content, responsive grid classes) but omits viewport meta, so mobile browsers default to a desktop layout width.
After example
After (passing example)
Methodology
Governed human + agent delivery
Mobile browsers scale the layout to device width.
Outcome
Bootstrap breakpoints and KS sidebar hiding work on narrow viewports.
Passing KS markup: same handbook shell with the viewport meta emitted by handbook_page in components/layouts.py.
Evidence and remediation
| Evidence | Meaning | Remediation |
|---|---|---|
metrics.metaViewport empty in crawl JSON |
Viewport meta missing or blank | Add <meta name="viewport" content="width=device-width, initial-scale=1"> in layout <head> |
| Finding: Responsive viewport meta tag is missing | viewport.check.js failed |
Restore KS <head> block in generator; fix export step that strips meta tags |
Raw HTML lacks name="viewport" in first <head> |
Post-build regression | Patch template partial or SPA index.html shell |
| Mobile layout looks desktop-zoomed despite responsive CSS | Runtime symptom of missing viewport | Confirm meta tag in served HTML, not only in source templates |
Kitchen Sink: Keep the viewport line in handbook_page, chapter_page, product_page, and landing_page return strings. Confirm generator/build-showcase.py output includes the tag. Do not remove it when trimming <head> for performance.
Consumer sites: After layout fixes, run python3 generator/build-site.py (or handbook build), then re-audit. Spot-check: curl -s … | grep -i 'name="viewport"' should return width=device-width, initial-scale=1.
Harness: generator/build_rule_defect_fixtures.py and auditor-tests/invoke-det-ruleset-harness.sh --only-rule DET.PAGE.VIEWPORT for regression. Implementation: tools/website-ux-auditor/design-rules/deterministic/page/viewport.check.js.