code wiki / _hdl_build / nx_brand_book.nx
nx_brand_book.nx source
↩ module page · 107 lines · 5469 B
1// nx_brand_book.nx -- R5: (a) BRAND-BOOK generator -- emit the brand guidelines doc FROM the token SSOT (the
2// Nike/Coca-Cola "brand book": color swatches with measured WCAG contrast + AA/AAA badges, the type scale, the
3// spacing scale), rendered IN the brand itself; (b) BRAND AUDIT -- scan a built page for OFF-TOKEN colour drift
4// (hardcoded #rrggbb outside the :root token block = a page bypassing the design system). Complements
5// nx_a11y_check (WCAG img/px/link grader) and CLOSES its queued 1.4.3 contrast gap via R2's nx_brand_guard.
6// REUSE: nx_brand_tokens (bt_lookup/bt_find), nx_brand_guard (bg_contrast_hex_x100), nx_web_builder (render).
7// 100% sovereign. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
10import "nx_brand_tokens.nx"
11import "nx_brand_guard.nx"
12import "nx_web_builder.nx"
13
14// ---- brand-book generator ----
15// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
16// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
17// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
18// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
19func bb_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 }
20func bb_label(fd: i64, cx: i64) -> i64 {
21 if cx >= 700 { wb_w(fd, "AAA" as *u8); return 0 }
22 if cx >= 450 { wb_w(fd, "AA" as *u8); return 0 }
23 wb_w(fd, "FAIL" as *u8); return 0
24}
25// one color row: swatch (via var) + name + hex + measured contrast-vs-bg + AA/AAA/FAIL badge.
26func bb_color_row(fd: i64, brand: *u8, bn: i64, name: *u8) -> i64 {
27 let hex: *u8 = sys_mmap(64)
28 if bt_lookup(brand, bn, "color" as *u8, name, hex, 64) < 0 { return 0 }
29 let bg: *u8 = sys_mmap(64)
30 bt_lookup(brand, bn, "color" as *u8, "bg" as *u8, bg, 64)
31 let cx: i64 = bg_contrast_hex_x100(hex, bg)
32 wb_w(fd, "<tr><td><span style=\"background:var(--nx-color-" as *u8); wb_w(fd, name)
33 wb_w(fd, ");display:inline-block;width:28px;height:28px;border:1px solid var(--nx-color-line);border-radius:4px\"></span></td><td>" as *u8)
34 wb_w(fd, name); wb_w(fd, "</td><td><code>" as *u8); wb_w(fd, hex); wb_w(fd, "</code></td><td>" as *u8)
35 bb_wn(fd, cx / 100); wb_w(fd, "." as *u8)
36 let frac: i64 = cx % 100
37 if frac < 10 { wb_w(fd, "0" as *u8) }
38 bb_wn(fd, frac); wb_w(fd, ":1 " as *u8)
39 bb_label(fd, cx)
40 wb_w(fd, "</td></tr>\n" as *u8)
41 return 0
42}
43// one key/value scale row.
44func bb_kv(fd: i64, brand: *u8, bn: i64, group: *u8, name: *u8) -> i64 {
45 let v: *u8 = sys_mmap(64)
46 if bt_lookup(brand, bn, group, name, v, 64) < 0 { return 0 }
47 wb_w(fd, "<tr><td>" as *u8); wb_w(fd, group); wb_w(fd, "." as *u8); wb_w(fd, name)
48 wb_w(fd, "</td><td><code>" as *u8); wb_w(fd, v); wb_w(fd, "</code></td></tr>\n" as *u8)
49 return 0
50}
51// emit the whole brand book FROM the token SSOT, rendered IN the brand.
52func bb_emit(brand: *u8, bn: i64, fd: i64) -> i64 {
53 wb_doc_open_branded(fd, "Brand Guidelines" as *u8, brand, bn)
54 wb_w(fd, "<div class=\"wrap\"><h1>Brand Guidelines</h1>\n" as *u8)
55 wb_w(fd, "<h2>Color & contrast (WCAG, measured)</h2><table>\n" as *u8)
56 wb_w(fd, "<tr><th>Swatch</th><th>Token</th><th>Hex</th><th>Contrast vs bg</th></tr>\n" as *u8)
57 bb_color_row(fd, brand, bn, "primary" as *u8)
58 bb_color_row(fd, brand, bn, "accent" as *u8)
59 bb_color_row(fd, brand, bn, "hero" as *u8)
60 bb_color_row(fd, brand, bn, "ink" as *u8)
61 bb_color_row(fd, brand, bn, "muted" as *u8)
62 bb_color_row(fd, brand, bn, "line" as *u8)
63 wb_w(fd, "</table>\n<h2>Type scale</h2><table>\n" as *u8)
64 bb_kv(fd, brand, bn, "font" as *u8, "h1" as *u8)
65 bb_kv(fd, brand, bn, "font" as *u8, "h2" as *u8)
66 bb_kv(fd, brand, bn, "font" as *u8, "size-base" as *u8)
67 wb_w(fd, "</table>\n<h2>Spacing</h2><table>\n" as *u8)
68 bb_kv(fd, brand, bn, "space" as *u8, "1" as *u8)
69 bb_kv(fd, brand, bn, "space" as *u8, "2" as *u8)
70 bb_kv(fd, brand, bn, "space" as *u8, "3" as *u8)
71 bb_kv(fd, brand, bn, "space" as *u8, "4" as *u8)
72 wb_w(fd, "</table></div>\n" as *u8)
73 wb_doc_close(fd)
74 return 0
75}
76
77// ---- brand audit: off-token colour drift ----
78func ba_hexd(c: i64) -> i64 {
79 if c >= 48 { if c <= 57 { return 1 } }
80 if c >= 97 { if c <= 102 { return 1 } }
81 if c >= 65 { if c <= 70 { return 1 } }
82 return 0
83}
84func ba_is_hex6(s: *u8, i: i64, hi: i64) -> i64 {
85 if i + 6 >= hi { return 0 }
86 if s[i] != (35 as u8) { return 0 } // '#'
87 var j: i64 = 1
88 while j <= 6 { if ba_hexd(s[i+j] as i64) == 0 { return 0 } j = j + 1 }
89 return 1
90}
91func ba_count_hex_in(s: *u8, lo: i64, hi: i64) -> i64 {
92 var c: i64 = 0
93 var i: i64 = lo
94 while i < hi { if ba_is_hex6(s, i, hi) == 1 { c = c + 1; i = i + 7 } else { i = i + 1 } }
95 return c
96}
97// count #rrggbb literals OUTSIDE the :root token block = off-token drift (a page bypassing the design system).
98// A token-driven builder page has hex ONLY inside :root (everything else is var(--nx-*)) -> audit returns 0.
99func ba_audit(html: *u8, n: i64) -> i64 {
100 let rs: i64 = bt_find(html, n, ":root{" as *u8, 6)
101 if rs < 0 { return ba_count_hex_in(html, 0, n) }
102 var re: i64 = rs + 6
103 while re < n { if html[re] == (125 as u8) { break } re = re + 1 }
104 let before: i64 = ba_count_hex_in(html, 0, rs)
105 let after: i64 = ba_count_hex_in(html, re, n)
106 return before + after
107}