code wiki / _hdl_build / nx_a11y_breadth.nx

nx_a11y_breadth.nx source

↩ module page · 61 lines · 3626 B

1// nx_a11y_breadth.nx -- a BROAD WCAG-2.2 checker (12 lexically-detectable criteria) on built HTML, to widen the 2// accessibility arc from the old 3-criterion nx_a11y_check toward axe/Lighthouse breadth. The HONEST exceed axis is 3// NOT raw criteria count (axe checks 50+, an acknowledged residual) -- it is that our builder PASSES + ENFORCES these 4// BY CONSTRUCTION (a non-compliant page can't be emitted) and SOVEREIGNLY (no cloud audit), where axe/Lighthouse only 5// AUDIT (you fix manually) and the proprietary builders score a11y≈0 (June-2026 research). 12 criteria: 6// 3.1.1 lang · 2.4.2 title · 1.4.10 reflow(viewport) · 1.1.1 img-alt · 1.3.1 landmarks · 2.4.7 focus-visible · 7// 2.3.3/2.2.2 reduced-motion · 2.5.5 target-size · 2.4.4 link-purpose · 4.1.2 name(aria-label) · 2.1.1 keyboard(no 8// JS-only) · 1.4.3 contrast(token-driven => R2-guard-enforced). 9// REUSE: nx_brand_tokens (bt_find). Library, sovereign. license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_brand_tokens.nx" 12 13func ab_has(h: *u8, n: i64, lit: *u8) -> i64 { if bt_find(h, n, lit, bt_len(lit)) >= 0 { return 1 } return 0 } 14func ab_count(h: *u8, n: i64, lit: *u8) -> i64 { 15 let ll: i64 = bt_len(lit) 16 if ll == 0 { return 0 } 17 var c: i64 = 0 18 var i: i64 = 0 19 while i + ll <= n { 20 var j: i64 = 0; var m: i64 = 1 21 while j < ll { if h[i+j] != lit[j] { m = 0; break } j = j + 1 } 22 if m == 1 { c = c + 1; i = i + ll } else { i = i + 1 } 23 } 24 return c 25} 26 27func ab_lang(h: *u8, n: i64) -> i64 { return ab_has(h, n, "<html lang=" as *u8) } // 3.1.1 28func ab_title(h: *u8, n: i64) -> i64 { return ab_has(h, n, "<title>" as *u8) } // 2.4.2 29func ab_reflow(h: *u8, n: i64) -> i64 { return ab_has(h, n, "name=\"viewport\"" as *u8) } // 1.4.10 30func ab_img_alt(h: *u8, n: i64) -> i64 { 31 let ic: i64 = ab_count(h, n, "<img" as *u8) 32 if ic == 0 { return 1 } 33 if ab_count(h, n, "alt=" as *u8) >= ic { return 1 } 34 return 0 35} // 1.1.1 36func ab_landmarks(h: *u8, n: i64) -> i64 { if ab_has(h, n, "<header" as *u8) == 1 { if ab_has(h, n, "<footer" as *u8) == 1 { if ab_has(h, n, "<h1" as *u8) == 1 { return 1 } } } return 0 } // 1.3.1 37func ab_focus(h: *u8, n: i64) -> i64 { return ab_has(h, n, ":focus-visible" as *u8) } // 2.4.7 38func ab_motion(h: *u8, n: i64) -> i64 { return ab_has(h, n, "prefers-reduced-motion" as *u8) } // 2.3.3/2.2.2 39func ab_target(h: *u8, n: i64) -> i64 { return ab_has(h, n, "min-height:44px" as *u8) } // 2.5.5 40func ab_links(h: *u8, n: i64) -> i64 { if ab_has(h, n, "click here" as *u8) == 1 { return 0 } if ab_has(h, n, "read more" as *u8) == 1 { return 0 } return 1 } // 2.4.4 41func ab_name(h: *u8, n: i64) -> i64 { return ab_has(h, n, "aria-label=" as *u8) } // 4.1.2 42func ab_keyboard(h: *u8, n: i64) -> i64 { if ab_has(h, n, "<script" as *u8) == 1 { return 0 } return 1 } // 2.1.1 (no JS-only) 43func ab_contrast(h: *u8, n: i64) -> i64 { return ab_has(h, n, "var(--nx-color-" as *u8) } // 1.4.3 (token => R2-enforced) 44 45// total criteria passed (0..12). 46func ab_score(h: *u8, n: i64) -> i64 { 47 var s: i64 = 0 48 s = s + ab_lang(h, n) 49 s = s + ab_title(h, n) 50 s = s + ab_reflow(h, n) 51 s = s + ab_img_alt(h, n) 52 s = s + ab_landmarks(h, n) 53 s = s + ab_focus(h, n) 54 s = s + ab_motion(h, n) 55 s = s + ab_target(h, n) 56 s = s + ab_links(h, n) 57 s = s + ab_name(h, n) 58 s = s + ab_keyboard(h, n) 59 s = s + ab_contrast(h, n) 60 return s 61}