@design.estate/dees-catalog
Theme Token Migration (bdTheme → --dees-* tokens)
Status: WS1 in progress — vocabulary approved 2026-06-12. Decisions: (1) the three text
tokens below were added to 00theme.ts; (2) shadow tokens are theme-aware
(themeDefaults.shadowsDark); (3) consolidation mapping approved as listed; (4) the
post-consolidation unique tail uses :host([gobright]) (goBright reflects as an attribute
since @design.estate/dees-element 2.3.0) instead of remaining on bdTheme.
Goal: zero cssManager.bdTheme() call sites in ts_web outside ts_web/elements/00theme.ts.
00theme.ts is the single sanctioned bridge — the --dees-* tokens are themselves implemented
via bdTheme() there, so the runtime theme-switch mechanism does not change. Components only
ever consume var(--dees-*).
Inventory (2026-06-12)
- 1,651
bdTheme()call sites across 129 files (demos included): 1,622 single-line literal (clustered below), 22 multi-line literal (handled like any literal during sweeps), 7 with dynamic arguments - 546 distinct (light, dark) pairs after color normalization
- 91 pairs / 397 sites match an existing token exactly or within Δ≤6 per RGB channel (visual no-op)
- 7 pairs / 50 sites look like flipped arguments (light/dark swapped) — see "Suspected bugs"
- 432 pairs / 1,155 sites need consolidation (mostly zinc/gray/slate/ad-hoc palette generations expressing the same semantics) — see mapping table
- 16 pairs / 20 sites are non-color values (shadows, borders shorthand, gradients, filters)
- Dynamic-argument calls live in only 3 components: dees-progressbar (3), dees-spinner (2), dees-searchbar (2)
Analysis tooling: tsx .nogit/debug/analyze-bdtheme.ts regenerates
.nogit/debug/bdtheme-analysis.json (full cluster → file data) and the ratchet baseline counts.
Approved vocabulary additions (in 00theme.ts since 2026-06-12)
New color tokens in IThemeColors / themeDefaultStyles — values taken from the dominant
existing usage, so adopting them is a visual no-op at those sites:
| Token | Light | Dark | Replaces (sites) |
|---|---|---|---|
--dees-color-text-success |
hsl(142.1 76.2% 36.3%) |
hsl(142.1 70.6% 45.3%) |
green-600/500 pairs (~13) |
--dees-color-text-error |
#dc2626 |
#f87171 |
red-600/400 pairs (~10) |
--dees-color-text-warning |
hsl(38 92% 45%) |
hsl(38 92% 55%) |
amber pairs (~4) |
Shadow tokens become theme-aware (today they are static; found usage consistently wants stronger shadows in dark mode). Light values unchanged:
| Token | Light (unchanged) | Dark (new) |
|---|---|---|
--dees-shadow-xs |
0 1px 2px 0 rgb(0 0 0 / 0.05) |
0 1px 2px 0 rgb(0 0 0 / 0.2) |
--dees-shadow-sm |
0 1px 3px rgba(0, 0, 0, 0.1) |
0 1px 3px rgba(0, 0, 0, 0.3) |
--dees-shadow-md |
0 2px 8px rgba(0, 0, 0, 0.15) |
0 2px 8px rgba(0, 0, 0, 0.35) |
--dees-shadow-lg |
0 4px 12px rgba(0, 0, 0, 0.15) |
0 4px 12px rgba(0, 0, 0, 0.4) |
Mechanism for the post-consolidation tail (genuinely component-unique theme pairs,
e.g. terminal/chart palettes): goBright reflects as the gobright attribute since
@design.estate/dees-element 2.3.0, so such sites use plain :host([gobright]) { ... } CSS
for component-local light/dark styling without bdTheme and without growing the global
vocabulary (rule R6).
Migration rules (precedence order)
- R1 — exact/near token match (Δ≤6/channel on both light and dark): replace with the token. Visual no-op.
- R2 — semantic consolidation: palette-generation duplicates (zinc/gray/slate/ad-hoc) map to the canonical token for their role (see table). Small intentional shifts: blue-tinted grays become neutral grays. Pick the token by role (text vs border vs surface), not by raw distance.
- R3 — alpha tints of a semantic color:
color-mix(in srgb, var(--dees-color-X) N%, transparent)instead of a dedicated pair. - R4 — flipped arguments: confirm against rendered output, then fix to the token (these are
bugs — the light theme currently shows dark-theme values). Never "preserve" a flip by mapping
it to swapped tokens, except intentional inverted-contrast UI (tooltips already have
--dees-color-tooltip-*). - R5 — non-color values: shadows →
--dees-shadow-*; spacing/radius/transition literals → their scales when the value matches; composite values (gradients, checkerboards) → compose from color tokens inside the gradient. - R6 — genuinely unique pairs (after R1–R5, expected rare): component-local
:host([gobright])CSS (dark value as default, bright override).
00colors.ts (legacy constants, 10 files) is retired during sweeps: blue/blueActive →
accent/link tokens per role (note: #0050b9 → #3b82f6 is a visible shift, check each site),
text → text tokens.
Consolidation mapping (clusters ≥7 sites; tail follows the same role rules)
| Sites | Pair (light | dark) | → Token | Shift |
|---|---|---|---|
| 45 | #09090b | #fafafa |
--dees-color-text-primary |
none |
| 27 | hsl(0 0% 89.8%) | hsl(0 0% 14.9%) |
--dees-color-border-default |
none |
| 25 | #71717a | #a1a1aa |
--dees-color-text-muted |
dark dims slightly |
| 25 | #e5e7eb | #27272a |
--dees-color-border-default |
none |
| 24 | hsl(215.3 25% 8.8%) | hsl(210 40% 98%) |
--dees-color-text-primary |
blue-gray → neutral |
| 24 | #e5e7eb | #374151 |
--dees-color-border-default |
dark border darkens (wysiwyg blocks) |
| 23 | #3b82f6 | #60a5fa |
--dees-color-link |
none |
| 22 | hsl(215.4 16.3% 56.9%) | hsl(215 20.2% 55.1%) |
--dees-color-text-muted |
blue-gray → neutral |
| 19 | #3b82f6 | #3b82f6 |
--dees-color-accent-primary |
none |
| 19 | #666 | #999 |
--dees-color-text-muted |
minor |
| 18 | hsl(210 40% 96.1%) | hsl(215 20.2% 16.8%) |
--dees-color-badge-default-bg |
near none (secondary surface) |
| 18 | hsl(215.4 16.3% 46.9%) | hsl(215 20.2% 65.1%) |
--dees-color-text-muted |
dark dims |
| 18 | hsl(210 20% 98%) | hsl(215 27.9% 16.9%) |
--dees-color-badge-default-bg |
slate → neutral (datepicker family) |
| 16 | #71717a | #71717a |
--dees-color-text-muted |
becomes theme-aware (improvement) |
| 16 | #9ca3af | #6b7280 |
--dees-color-text-muted |
light darkens (placeholders) |
| 15 | hsl(222.2 47.4% 51.2%) | hsl(217.2 91.2% 59.8%) |
--dees-color-accent-primary |
light brightens |
| 15 | hsl(0 0% 95.1%) | hsl(0 0% 14.9%) |
--dees-color-badge-default-bg |
near none |
| 14 | hsl(220 8.9% 46.1%) | hsl(215 20.2% 65.1%) |
--dees-color-text-muted |
dark dims |
| 13 | #18181b | #fff |
--dees-color-tooltip-bg |
near none |
| 13 | #000000 | #ffffff |
per-site: usually text-primary |
manual (R4 check: some intentional pure contrast) |
| 13 | hsl(0 0% 45.1%) | hsl(0 0% 63.9%) |
--dees-color-text-muted |
dark dims slightly |
| 13 | #374151 | #e5e7eb |
per-site: text-secondary or text-primary |
manual (emphasized text, dark value sits between tokens) |
| 13 | hsl(215 16% 45%) | hsl(215 16% 75%) |
--dees-color-text-secondary |
dark dims |
| 12 | #6b7280 | #9ca3af |
--dees-color-text-muted |
minor |
| 11 | #71717a | #888 |
--dees-color-text-muted |
minor |
| 11 | #111827 | #f9fafb |
--dees-color-text-primary |
near none |
| 10 | #dc2626 | #f87171 |
--dees-color-text-error (new) |
none |
| 10 | #ffffff | #000000 |
per-site (inverse surfaces) | manual |
| 10 | #a1a1aa | #666 |
per-site (storage-browser family) | manual, flip-suspect |
| 10 | #333 | #fff |
--dees-color-text-primary |
light deepens |
| 10 | hsl(214.3 31.8% 91.4%) | hsl(217.2 32.6% 17.5%) |
--dees-color-border-default |
slate → neutral |
| 10 | hsl(0 0% 50%) | hsl(0 0% 60%) |
--dees-color-text-muted |
minor |
| 9 | #e5e7eb | #3f3f46 |
--dees-color-border-default |
dark darkens |
| 9 | hsl(0 0% 88%) | hsl(0 0% 20%) |
--dees-color-border-default |
minor |
| 9 | #d1d9e0 | #30363d |
--dees-color-border-default |
github palette → neutral (markdownoutlet) |
| 8 | hsl(142.1 76.2% 36.3%) | hsl(142.1 70.6% 45.3%) |
--dees-color-text-success (new) |
none |
| 8 | #e5e7eb | #333 |
--dees-color-border-default |
minor |
| 8 | hsl(0 0% 20%) | hsl(0 0% 90%) |
per-site: text-secondary/text-primary |
manual |
| 8 | hsl(224 71.4% 4.1%) | hsl(210 20% 98%) |
--dees-color-text-primary |
near none |
| 8 | hsl(0 0% 95%) | hsl(0 0% 9%) |
--dees-color-code-block-bg |
near none |
| 7 | #999 | #666 |
--dees-color-text-muted |
faint/disabled text, both shift |
| 7 | hsl(222.2 47.4% 51.2% / 0.1) | hsl(217.2 91.2% 59.8% / 0.1) |
color-mix(accent-primary 10%) (R3) |
near none |
| 7 | hsl(0 0% 100%) | hsl(224 71.4% 4.1%) |
--dees-color-bg-primary |
none |
| 7 | hsl(0 72.2% 50.6%) | hsl(0 62.8% 30.6%) |
--dees-color-accent-error |
dark brightens (destructive buttons) — review |
| 7 | hsl(215.3 25% 26.7%) | hsl(217.9 10.6% 74.9%) |
--dees-color-text-secondary |
dark dims |
| 7 | #d1d5db | #4b5563 |
--dees-color-border-strong |
dark darkens |
Suspected bugs (flipped light/dark arguments — fix via R4 during sweeps)
| Sites | Pair (light | dark) | Looks like | Files |
|---|---|---|---|
| 23 | hsl(0 0% 9%) | hsl(0 0% 95%) |
code-block-bg flipped | chart-log demo, table demo (terminal panels — verify: may be intentional always-dark panels; if so, use literal dark values, not a flip) |
| 12 | hsl(0 0% 15%) | hsl(0 0% 90%) |
border-default flipped | dataview-statusobject, input-checkbox |
| 9 | hsl(0 0% 63.9%) | hsl(0 0% 45.1%) |
text-secondary flipped | dropdown, statusobject |
| 6 | #333 | #ccc |
border-strong flipped | profiledropdown, pagination |
| 3 | #a1a1aa | #404040 |
badge-default-fg flipped | storage-browser |
| 3 | hsl(0 0% 15%) | hsl(0 0% 93.9%) |
badge-default-bg flipped | input-richtext, input-text |
| 3 | hsl(142.1 70.6% 45.3%) | hsl(142.1 76.2% 36.3%) |
text-success flipped | (locate during sweep) |
| 2 | #262626 | #e5e5e5 |
border-default flipped | appui-mainmenu, simple-appdash |
Sweep order (WS1, after vocabulary approval)
- Add approved tokens to
00theme.ts(+IThemeColors), release. - The 49 files currently using both systems (finish what's started; fastest surface reduction).
00group-input+ form infrastructure (incl. datepicker slate re-skin)00group-dataview(table styles.ts, storage-browser family incl. flip-suspects)00group-overlay,00group-layout00group-appui,00group-simple00group-chart,00group-media,00group-workspace, remaining groups + demos- Dynamic-call components (progressbar, spinner, searchbar) — restructure their color props to accept tokens/CSS values rather than light/dark pairs.
Per sweep: pnpm build, pnpm test, wcctools demo screenshots in both themes (before/after),
node scripts/check-bdtheme-ratchet.cjs --update-baseline, focused commit per group.
Ratchet
scripts/check-bdtheme-ratchet.cjs (runs first in pnpm test):
- counts
bdTheme(occurrences per file underts_web/, excludingts_web/elements/00theme.ts - fails if any file exceeds its count in
scripts/bdtheme-baseline.json, or if a file not in the baseline uses bdTheme at all --update-baselinerewrites the baseline after a sweep (counts may only shrink; the script refuses to raise them)