The T9.9 token sweep pointed seven files (help.js, auth-guard.js, wp-format.js, project-data.js, index.html, field.html, wp-creation-app.js) at Carbon names the theme never defined: --cds-layer-01/-02, --cds-border-subtle-01/-strong-01, --cds-layer-hover-01. theme-light.css carries no -01 suffixes. An undefined var() invalidates the whole declaration, so the help-centre modal, the change-password and language dialogs, the print popup's inlined values, the creator nav drawer and the sync badge all rendered TRANSPARENT backgrounds - reported by Nick against the help menu, 2026-08-20. Renamed every consumer to the canonical tokens (--cds-layer, --cds-layer-accent, --cds-layer-hover, --cds-border-subtle, --cds-border-strong), matched to the hex each replacement originally stood in for. color_check gains check 3: every var() consumed anywhere must resolve to a definition somewhere - the class of this bug, pinned. Verified live: the modal computes rgb(255,255,255) over an opaque gray nav, and the language dialog is opaque too. BL-025 logged for the one wrong-base-colour rgba tint noticed in passing. Item: C4 (regression in its own enforcement). Probe: color_check 5/5. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
107 lines
4.8 KiB
Python
107 lines
4.8 KiB
Python
#!/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: <meta name="theme-color"> (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"(?<![:'\"])//.*$", "", ln) for ln in src.split("\n"))
|
|
src = 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'<meta name="theme-color" content="#[0-9a-fA-F]{6}"\s*/?>', "", 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)
|
|
chk("the second brand blue (#2563d6) is gone from the theme itself",
|
|
"2563d6" not in code and "37, 99, 214" not in code)
|
|
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:
|
|
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())
|