code wiki / _hdl_build / nx_brand_dark_gate.nx
nx_brand_dark_gate.nx source
↩ module page · 85 lines · 5140 B
1// nx_brand_dark_gate.nx -- R8 gate: dark-mode colour system. A brand with `dark|...` tokens emits a
2// @media(prefers-color-scheme:dark){:root{...}} override; a brand WITHOUT them emits nothing (backward-compatible).
3// BOTH light and dark must pass WCAG AA. Sovereignty + determinism hold. 100% sovereign. expect_exit: 0
4import "nx_syscalls.nx"
5import "nx_sitegate_emit_lib.nx"
6import "nx_brand_tokens.nx"
7import "nx_brand_guard.nx"
8import "nx_web_builder.nx"
9import "nx_gate_verdict.nx"
10
11
12func g_has(s: *u8, n: i64, lit: *u8) -> i64 { if bt_find(s, n, lit, bt_len(lit)) >= 0 { return 1 } return 0 }
13func inband8(v: i64, lo: i64, hi: i64) -> i64 { if v < lo { return 0 } if v > hi { return 0 } return 1 }
14
15func main() -> i64 {
16 let tot: *i64 = sys_mmap(32) as *i64
17 tot[0]=0; tot[1]=0
18 let cap: i64 = 65536
19 let a: *u8 = sys_mmap(cap)
20 gw("=== nx_brand_dark_gate -- R8 dark-mode colour system (prefers-color-scheme token layer) ===\n" as *u8)
21
22 // a dark-capable brand: light tokens + dark overrides
23 let dk: *u8 = "token|color|primary|#0b2545\ntoken|color|accent|#e8a534\ntoken|color|accent-ink|#0b2545\ntoken|color|hero|#13315c\ntoken|color|ink|#16202e\ntoken|color|bg|#ffffff\ntoken|color|muted|#eef3f8\ntoken|color|line|#cdd7e3\ntoken|font|sans|system-ui\ntoken|font|size-base|16px\ntoken|font|leading|1.6\ntoken|font|h1|2rem\ntoken|font|h2|1.4rem\ntoken|radius|md|8px\ntoken|radius|sm|6px\ntoken|motion|fast|150ms\ntoken|motion|ease|ease\ndark|color|bg|#0b1220\ndark|color|ink|#e6edf3\ndark|color|primary|#9ec1ff\ndark|color|muted|#161f2e\ndark|color|line|#2a3850\n" as *u8
24 let dn: i64 = bt_len(dk)
25
26 // D1: dark brand emits the @media dark override
27 let w1: i64 = bt_emit_dark_root_buf(dk, dn, a, cap)
28 var t1: i64 = 0
29 if w1 > 0 {
30 if g_has(a, w1, "@media(prefers-color-scheme:dark){:root{" as *u8) == 1 {
31 if g_has(a, w1, "--nx-color-bg: #0b1220" as *u8) == 1 { if g_has(a, w1, "--nx-color-ink: #e6edf3" as *u8) == 1 { t1 = 1 } }
32 }
33 }
34 t_row("D1 dark brand emits @media(prefers-color-scheme:dark) override block" as *u8, t1, tot)
35
36 // D2: default brand (no dark tokens) emits NOTHING (backward-compatible)
37 let w2: i64 = bt_emit_dark_root_buf(bt_default_brand(), bt_len(bt_default_brand()), a, cap)
38 t_row("D2 brand WITHOUT dark tokens emits nothing (w==0, zero regression)" as *u8, inband8(w2, 0, 0), tot)
39
40 // D3: a BUILT page carries BOTH the light :root and the dark @media block
41 let fd: i64 = sys_openat_wr("/tmp/nx_dark.html" as *u8, 0x1a4)
42 wb_doc_open_branded(fd, "Dark" as *u8, dk, dn)
43 wb_w(fd, "<p>x</p>\n" as *u8)
44 wb_doc_close(fd)
45 sys_close(fd)
46 let lp: *i64 = sys_mmap(16) as *i64
47 let pg: *u8 = sys_read_file("/tmp/nx_dark.html" as *u8, lp)
48 let pn: i64 = lp[0]
49 var t3: i64 = 0
50 if g_has(pg, pn, "--nx-color-bg: #ffffff" as *u8) == 1 { if g_has(pg, pn, "@media(prefers-color-scheme:dark)" as *u8) == 1 { if g_has(pg, pn, "--nx-color-bg: #0b1220" as *u8) == 1 { t3 = 1 } } }
51 t_row("D3 built page has BOTH light :root (#ffffff) AND dark override (#0b1220)" as *u8, t3, tot)
52
53 // D4 + D5: BOTH modes meet WCAG AA
54 let aa_light: i64 = bg_contrast_hex_x100("#16202e" as *u8, "#ffffff" as *u8)
55 let aa_dark: i64 = bg_contrast_hex_x100("#e6edf3" as *u8, "#0b1220" as *u8)
56 gw(" light ink/bg=" as *u8); gn(aa_light); gw(" (x100) dark ink/bg=" as *u8); gn(aa_dark); gw(" (x100)\n" as *u8)
57 t_row("D4 dark mode ink/bg meets WCAG AA" as *u8, inband8(aa_dark, 450, 999999), tot)
58 t_row("D5 light mode ink/bg meets WCAG AA" as *u8, inband8(aa_light, 450, 999999), tot)
59
60 // D6: sovereignty -- a hostile dark value is rejected, a good sibling survives
61 let bad: *u8 = "dark|color|bg|#0b1220\ndark|color|x|<script>alert\n" as *u8
62 let w6: i64 = bt_emit_dark_root_buf(bad, bt_len(bad), a, cap)
63 var t6: i64 = 0
64 if w6 > 0 { if g_has(a, w6, "--nx-color-bg: #0b1220" as *u8) == 1 { if g_has(a, w6, "script" as *u8) == 0 { t6 = 1 } } }
65 t_row("D6 sovereignty: hostile dark value rejected, good sibling survives" as *u8, t6, tot)
66
67 // D7: determinism
68 let b: *u8 = sys_mmap(cap)
69 let e1: i64 = bt_emit_dark_root_buf(dk, dn, a, cap)
70 let e2: i64 = bt_emit_dark_root_buf(dk, dn, b, cap)
71 var t7: i64 = 0
72 if e1 == e2 { if e1 > 0 { var eq: i64 = 1; var i: i64 = 0; while i < e1 { if a[i] != b[i] { eq = 0 } i = i + 1 } t7 = eq } }
73 t_row("D7 determinism: dark block emits byte-identical" as *u8, t7, tot)
74
75 gw("\nrows pass=" as *u8); gn(tot[0]); gw(" fail=" as *u8); gn(tot[1]); gw("\n" as *u8)
76 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
77 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
78 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
79 let ctr__dry: *i64 = gv_ctr()
80 ctr__dry[0] = tot[0]
81 ctr__dry[1] = tot[0] + tot[1]
82 let rc__dry: i64 = gv_verdict("BRAND-DARK-GATE" as *u8, ctr__dry, "dark-mode colour system: AA in both modes, additive, sovereign" as *u8)
83 sys_exit(rc__dry)
84 return rc__dry
85}