#!/usr/bin/env python3 """Is theme-light.css the only place a colour exists? — C4, T9.9. The token rule, finally enforceable everywhere: after this sweep no colour literal survives outside theme-light.css - not in page stylesheets, not in the help centre's injected styles (BL-004), not in the JS-built dialogs (BL-005), not in the print popup. One accent blue (BL-008 - the second brand blue is gone, .sop-inherited tints with THE blue) and one warning amber (BL-009 - the alt token is deleted). Comments are stripped first: quoting a hex while explaining it is not declaring one (the BL-017 lesson). The exceptions, in full: (a meta attribute cannot resolve a CSS var), and rgba() shadow/overlay alphas, which are opacity recipes, not palette entries. Static sweep - no browser needed. Exit 0 all passed, 1 a failure. """ import io import os import re import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from browser_check import chk, _PASS, _FAIL # noqa: E402 ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) HTML = os.path.join(ROOT, "html") def strip_comments(src, is_css): src = re.sub(r"/\*.*?\*/", "", src, flags=re.S) if not is_css: src = "\n".join(re.sub(r"(?", "", src, flags=re.S) return src def main(): print("\n1. hex literals outside theme-light.css") offenders = [] for name in sorted(os.listdir(HTML)): if not name.endswith((".js", ".html", ".css")) or name == "theme-light.css": continue src = strip_comments(io.open(os.path.join(HTML, name), encoding="utf-8").read(), name.endswith(".css")) # the one exception: the browser-chrome hint, which cannot use var() src = re.sub(r'', "", src) for m in re.finditer(r"#[0-9a-fA-F]{3}\b|#[0-9a-fA-F]{6}\b", src): offenders.append("%s: %s" % (name, m.group(0))) chk("no hex colour literal outside theme-light.css; grep confirms", not offenders, offenders[:8]) print("\n2. one blue, one amber") theme = io.open(os.path.join(HTML, "theme-light.css"), encoding="utf-8").read() code = strip_comments(theme, True) # BL-025 widened this: the rgb spelling is compared space-free, because # rgba(37,99,214,.15) in help.js slid past the spaced grep for months. chk("the second brand blue (#2563d6) is gone from the theme itself", "2563d6" not in code.lower() and "37,99,214" not in code.replace(" ", "")) chk("the ninth amber (--wp-status-warning-text-alt) is deleted", "--wp-status-warning-text-alt" not in code) others = [] for name in sorted(os.listdir(HTML)): if name == "theme-light.css" or not name.endswith((".js", ".css", ".html")): continue src = strip_comments(io.open(os.path.join(HTML, name), encoding="utf-8").read(), name.endswith(".css")) if ("warning-text-alt" in src or "2563d6" in src.lower() or "37,99,214" in src.replace(" ", "")): others.append(name) chk("...and no consumer still references either", not others, others) print("\n3. every token consumed is a token defined") # The bug this pins: help.js (and six other files) shipped consuming # --cds-layer-01/-02 and --cds-border-subtle-01/-strong-01 - names the theme # never defined (its names carry no -01 suffix). An undefined var() makes # the whole declaration invalid, so the help centre modal, the password and # language dialogs, and the print popup all rendered TRANSPARENT # backgrounds. Found by the user, 2026-08-20. Definitions are collected # from every file (page aliases are legal); consumption of a name nobody # defines is the defect. defined, consumed = set(), {} for name in sorted(os.listdir(HTML)): if not name.endswith((".js", ".html", ".css")): continue src = io.open(os.path.join(HTML, name), encoding="utf-8").read() for m in re.finditer(r"(--[a-zA-Z0-9-]+)\s*:", src): defined.add(m.group(1)) for m in re.finditer(r"setProperty\(\s*['\"](--[a-zA-Z0-9-]+)", src): defined.add(m.group(1)) for m in re.finditer(r"var\(\s*(--[a-zA-Z0-9-]+)", src): consumed.setdefault(m.group(1), set()).add(name) # --wp-chart- is the creator's JS-concatenated fallback ('--wp-chart-'+k); # the numbered names it builds are all defined, the fragment is not a name. unresolved = ["%s (%s)" % (t, ", ".join(sorted(fs))) for t, fs in sorted(consumed.items()) if t not in defined and t != "--wp-chart-"] chk("no var() anywhere names a token that nothing defines", not unresolved, unresolved[:8]) print("\n" + "-" * 54) print("%d/%d checks passed." % (len(_PASS), len(_PASS) + len(_FAIL))) for f in _FAIL: print(" - " + f) return 1 if _FAIL else 0 if __name__ == "__main__": sys.exit(main())