Skip to content

Spun out of ai-config-design-ssot on 2026-08-29, where a routine regeneration was run, diffed, and discarded before committing. pnpm colors is currently unsafe to run, for two independent reasons.

1. Generated theme files were hand-edited, and regenerating reverts the edits. sites/template/src/styles/themes/light.css and dark.css carry “DO NOT EDIT MANUALLY — auto-generated” headers, yet contain hand-applied WCAG AA contrast fixes with explanatory comments. Regenerating silently reverts every one:

VariableHand-edited valueReverts to
--muted-foreground (light)28 8% 32% “Darkened from 44% for WCAG AA 4.5:1”28 8% 44%
--muted-foreground (dark)28 7% 63% “Lightened from 36% for WCAG AA 4.5:1”28 7% 36%
--destructive (light)0 84.2% 40% “Darkened from 60.2% for WCAG AA”0 84.2% 60.2%
--destructive-foreground0 0% 100% “Pure white for maximum contrast”210 40% 98%
--primary (light)215 69% 32% “Darkened from 45% for WCAG AA”215 69% 45%

Anyone running pnpm colors breaks accessibility compliance with no error and no obvious diff signal. This is the AGENTS.md “deploy-from-source configs — diff before you deploy” pattern: the generated copy drifted ahead of its generator.

2. sites/template was last generated with the ts brand. Its themes carry Professional Blue (215 69% …). Running pnpm colors:sd — the obvious command when working on SmartDebt — silently rebrands the entire template site to green. Nothing records which brand a site’s themes were last built from.

3. Minor, but it makes every diff noisy: the generator emits single-quoted attribute selectors ([data-theme='dark']) while prettier rewrites the committed files to double quotes, so every regeneration produces churn unrelated to any colour change.

  • Make the WCAG fixes survive regeneration. Preferred: move them into the generator so they are derived, not patched — the accessibility intent (contrast floor against the theme background) is expressible as a rule. Fallback: a documented post-generation overrides layer the generator emits but never owns. Do not simply re-apply the patches by hand.
  • Record the source brand per site so a regeneration cannot silently rebrand. E.g. a brand field in each site’s site.config.json, with pnpm colors defaulting to it and refusing a mismatch unless --force.
  • Fix the quote-style churn — emit double quotes, or run the generator’s output through prettier.
  • Consider letting the generator target apps/*. Its SITE_ROOT resolves to sites/<name>, so apps cannot receive generated theme layers at all (documented in docs/design/apps.md). This is why apps/sd-app hand-declares its neutral/surface variables.
  • Once safe: regenerate to apply the "3": ["dark:primary"] mapping already committed in src/brand/sd/colors.oklch.json, and verify no WCAG value moved.
  • pnpm colors (and colors:sd / colors:ts) can be run on a clean tree and the resulting diff contains only intended colour changes — no reverted WCAG values, no brand switch, no quote churn.
  • Every contrast pair that was hand-fixed still meets WCAG AA after a regeneration, checked by computed ratio rather than by eye.
  • A site’s brand is recorded in the repo, and a mismatched regeneration is refused rather than silently applied.
  • WCAG AA — Web Content Accessibility Guidelines, level AA: 4.5:1 contrast for normal text.
  • The generator is sites/template/scripts/generate-color-system.mjs; the palette algorithm is packages/design-tokens/src/oklch-palette.mjs.
  • Design authority context: Design (boundary rule), monorepo/docs/design/ (implementation SSOT).