// Theme 10 — Presentation: the statically-checkable slice of zoom support. // 10.4 (text legible at 200%) generally needs rendering, but a viewport meta that // blocks zooming is a DEFINITE failure detectable from the markup alone. import type { Doc, El } from "../parse/html.js"; import { attr, textContent, ancestors } from "../parse/html.js"; import { isDisplayHidden, visuallyRenderedText } from "../name.js"; import type { Rule, RuleFinding } from "./rule.js"; const metaViewportZoomBlock: Rule = { id: "meta-viewport-zoom-block", criteria: ["1.4.4"], severity: "majeur", run(doc: Doc): RuleFinding[] { const out: RuleFinding[] = []; for (const el of doc.elements) { if (el.tag !== "meta" || (attr(el, "name") ?? "").toLowerCase() !== "viewport") continue; const content = (attr(el, "content") ?? "").toLowerCase(); const pairs = new Map(); for (const part of content.split(/[,;]/)) { const [k, v] = part.split("=").map((s) => s.trim()); if (k) pairs.set(k, v ?? ""); } const userScalable = pairs.get("user-scalable"); const maxScale = pairs.get("maximum-scale"); // Only a real, finite maximum-scale < 2 blocks zoom. An empty (`maximum-scale=`) // or malformed value must NOT be treated as 0 (Number("") === 0) and falsely flagged. const maxScaleNum = maxScale !== undefined && maxScale.trim() !== "" ? Number(maxScale) : Number.NaN; // A maximum-scale of 0 or less is invalid: browsers discard it, so zoom is NOT // blocked (`maximum-scale=-1` is a W3C ACT "passed" example). const blocked = userScalable === "no" || userScalable === "0" || (Number.isFinite(maxScaleNum) && maxScaleNum > 0 && maxScaleNum < 2); if (!blocked) continue; out.push({ criteriaId: "1.4.4", el, msgId: "meta-viewport-zoom-block", params: { blockedBy: userScalable === "no" || userScalable === "0" ? "user-scalable" : "maximum-scale", maxScale: maxScale ?? "" }, }); } return out; }, }; // ADVISORY (10.2-adjacent): CSS generated content that carries informative WORDS // (`content: "…"`) is invisible to assistive technologies. Recommendation: move the text // into the DOM. Conservative — only inline