skill
Adom Theme
Public Made by Adomby adom
The canonical Adom application theme spec: five schemes, exact tokens, font pairings, and the implementation contract.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
#!/usr/bin/env bash
#
# Adom Studio Bright Inverse must be EXACTLY Studio Bright with --bg and --surface traded.
#
# The name is the contract. CSS has no "extend", so the shared text ramp is kept by restating it
# in both blocks, and a restated value is a value that can drift -- someone tunes Bright's
# secondary text, Inverse keeps the old number, and the two stop being the same theme while the
# name still says they are. That is the exact failure this scheme was renamed to fix, so it gets
# a guard rather than a comment asking people to be careful.
#
# Fails the build if:
# - the two blocks disagree on any token other than --bg / --surface
# - the swap is not actually a swap (Inverse's --bg is not Bright's --surface, or vice versa)
set -euo pipefail
CSS="${1:-$(dirname "$0")/../src/routes/styles.css}"
[ -f "$CSS" ] || { echo "[scheme-parity] styles.css not found at $CSS" >&2; exit 1; }
python3 - "$CSS" <<'PY'
import re, sys, io
css = io.open(sys.argv[1], encoding='utf-8').read()
css = re.sub(r'/\*.*?\*/', '', css, flags=re.S) # comments hold example values; strip them
def block(selector):
m = re.search(re.escape(selector) + r'\s*\{(.*?)\}', css, flags=re.S)
if not m:
print(f"[scheme-parity] FAIL: no block for {selector}")
sys.exit(1)
return dict((k, v.strip()) for k, v in re.findall(r'(--[\w-]+)\s*:\s*([^;]+);', m.group(1)))
base = block(':root')
bright = block(":root[data-scheme='studio-bright']")
inverse = block(":root[data-scheme='studio']") # the flagship slug since the 2026-07-25 rename
# Resolve each scheme the way the browser does: the base, then the scheme's overrides.
eff_bright = {**base, **bright}
eff_inverse = {**base, **inverse}
fail = False
# 1. The swap must be a real swap.
if eff_inverse.get('--bg') != eff_bright.get('--surface'):
print(f"[scheme-parity] FAIL: Inverse --bg is {eff_inverse.get('--bg')}, "
f"expected Bright's --surface {eff_bright.get('--surface')}")
fail = True
if eff_inverse.get('--surface') != eff_bright.get('--bg'):
print(f"[scheme-parity] FAIL: Inverse --surface is {eff_inverse.get('--surface')}, "
f"expected Bright's --bg {eff_bright.get('--bg')}")
fail = True
# 2. Nothing else may differ.
for token in sorted(set(eff_bright) | set(eff_inverse)):
if token in ('--bg', '--surface'):
continue
b, i = eff_bright.get(token), eff_inverse.get(token)
if b != i:
print(f"[scheme-parity] FAIL: {token} differs -- Bright={b!r} Inverse={i!r}")
print(" Bright Inverse may only trade --bg and --surface. Either revert "
"this token or rename the scheme.")
fail = True
# 3. The inverse block itself should carry only the ramp it restates plus the swap. A block that
# grows redundant copies of base values is how the last one drifted out of meaning.
redundant = [k for k, v in inverse.items() if k not in ('--bg', '--surface') and base.get(k) == v]
if redundant:
print(f"[scheme-parity] FAIL: redundant override(s) restating the base value: "
f"{', '.join(redundant)}")
fail = True
if fail:
sys.exit(1)
print(f"[scheme-parity] OK -- Bright Inverse is Bright with --bg/--surface traded "
f"({eff_bright.get('--bg')} <-> {eff_bright.get('--surface')}), "
f"{len(inverse)} override(s), nothing else differs")
PY