I suspect that any reuser will want to change the look-and-feel of the platform, at least via CSS.
I had Claude prepare this prompt, but I don't think I'll have time to review and iterate on it, so sharing here.
Details
Refactor: replace Style Dictionary with shadcn-canonical CSS-var theming
Why
The current design-tokens pipeline (Style Dictionary → generated _variables.css + Tailwind color.js) is over-provisioned and harder to override per deployment than it needs to be. Refactoring to a CSS-variable-first setup makes per-deployment color overrides trivial AND aligns the codebase with the dominant Next.js + Tailwind v3 pattern.
This plan also folds in a configurable-font change (branding ships a next/font instance), since both touch layout.tsx and the DeploymentConfig contract.
Current state (audit summary)
config/tokens/tokens.json declares ~274 color tokens. Only ~13 unique base-color Tailwind classes are referenced anywhere in the app (bg-baseIndigoSolid1, border-baseGraySlateSolid4, text-baseGraySlateSolid11, etc.). ≥95% of generated tokens are dead weight.
- Zero usages of Tailwind opacity utilities with design tokens (no
bg-base…/50 etc.). So the rgb-channel form for CSS vars is not required.
tailwind.config.js has zero plugins and no theme.extend — it replaces Tailwind's defaults wholesale with Style Dictionary outputs.
- opub-ui consumes ~13 semantic CSS vars from
styles/tokens/_variables.css (--text-default, --text-interactive, --border-highlight-default, --icon-highlight, --background-solid-dark, --text-onbg-default, plus space/radius/font-size/z-index/outline). No collisions; opub-ui is a pure consumer.
- Direct
var(--…) consumption in app code is essentially zero (2 sites in globals.css).
- Fonts:
next/font/google Inter loaded statically in app/[locale]/layout.tsx:18.
Target state
Theming source of truth is CSS variables in a single hand-authored file. Tailwind references those CSS vars. Branding overrides = inject CSS vars + provide a next/font instance.
styles/theme.css ← :root CSS vars (the only source of truth)
tailwind.config.js ← references the CSS vars in theme.extend
app/[locale]/layout.tsx ← injects branding cssVars; uses branding font
No Style Dictionary, no generated JS color file, no build:tokens script.
Steps
1. Inventory actually-used Tailwind token classes
Grep the app for all token-derived class usage and build the canonical list (~13 names per audit). Capture both the class name AND the hex value it currently resolves to (from styles/tokens/tailwind/color.js).
grep -roE '(bg|text|border|fill|stroke|outline|ring)-base[A-Z][A-Za-z0-9]+' \
app components | sort -u
Add any semantic-token class usage (e.g. bg-backgroundSolidDark, text-textDefault) found in the same grep.
2. Author styles/theme.css
New file. Lift only the CSS vars actually referenced (by Tailwind classes, by opub-ui, and by globals.css). Use the same semantic names already in _variables.css so opub-ui keeps working. Approx size: ~50 vars (down from 112).
Keep the existing format (hex values, plain var(--name)); no need to refactor to rgb channels since no opacity utilities use these tokens.
3. Rewrite tailwind.config.js
Replace the imports from styles/tokens/tailwind/* with hand-written theme.extend.colors (and any other categories) referencing CSS vars:
theme: {
extend: {
colors: {
textDefault: 'var(--text-default)',
backgroundSolidDark: 'var(--background-solid-dark)',
iconHighlight: 'var(--icon-highlight)',
// ... only the names actually used
},
fontFamily: {
sans: 'var(--font-family-primary)',
},
},
},
Drop the space, borderRadius, borderWidth, fontSize, lineHeight, boxShadow, zIndex, transitionTimingFunction, transitionDuration, fontWeight overrides unless they're actually customised vs. Tailwind defaults — for any that are, keep them as var(--…) references and add the corresponding entries to theme.css.
Keep corePlugins.preflight = false (still required to avoid clashing with opub-ui's reset).
4. Update styles/globals.css
Replace @import './tokens/_variables.css'; with @import './theme.css';. Leave the opub-ui and nprogress imports as-is.
5. Delete obsolete machinery
config/tokens/ (entire directory: tokens.json, sd-config.js, generate.mjs, copy.js, anything else)
styles/tokens/ (entire directory: _variables.css, tailwind/*)
package.json: drop the build:tokens and postbuild:tokens scripts; drop the style-dictionary and fs-extra devDependencies if they're only used by the tokens pipeline.
6. Extend the branding contract
In ids-drr-branding-types/src/index.ts, add to DeploymentConfig:
import type { NextFont } from 'next/font';
export type DeploymentConfig = {
// ...existing fields
// Per-deployment font (e.g. a `next/font/google` or `/local` instance).
// Falls back to the frontend's default Inter when undefined.
font?: NextFont;
// CSS variable overrides applied at runtime to <html style>. Keys match
// the names in styles/theme.css (e.g. "--text-default").
cssVars?: Record<string, string>;
};
7. Update branding implementations
branding-stub/src/index.ts — no changes needed (defaults to undefined, which is fine).
ids-drr-india-branding/src/index.ts and config.ts — declare the deployment's font (pick a next/font/google family) and any CSS-var overrides.
8. Wire through config/site.ts
Add re-exports:
export const font = config.font;
export const cssVars: Record<string, string> = config.cssVars ?? {};
9. Apply in app/[locale]/layout.tsx
import { cssVars, font } from '@/config/site';
const fallbackFont = FontSans({ subsets: ['latin'], display: 'swap' });
const activeFont = font ?? fallbackFont;
<html lang={locale} style={cssVars as CSSProperties}>
...
<body className={activeFont.className}>
10. Verify
npx tsc --noEmit → 0 errors.
npm test → 143/143 pass.
npm run dev against the stub: the home, analytics, and datasets pages look identical to before the refactor (regression check).
npm run dev against the India branding (with a font override and at least one cssVars override) → font and the chosen accent colour change as expected.
npm run build succeeds without build:tokens (confirm script deletion didn't break the build chain).
Files touched
- New:
styles/theme.css
- Modified:
tailwind.config.js, styles/globals.css, app/[locale]/layout.tsx, config/site.ts, branding-stub/src/index.ts, package.json
- Modified (separate repos):
ids-drr-branding-types/src/index.ts, ids-drr-india-branding/src/{index.ts,config.ts}
- Deleted:
config/tokens/**, styles/tokens/**
Effort & risks
Effort: ~1 day. Dominated by inventorying actually-used tokens, hand-authoring theme.css and tailwind.config.js, and visual smoke-testing.
Risks:
- A token in use via runtime-computed classnames may slip past the static grep. Mitigation: keep every var that opub-ui's CSS references (per audit, ~13 semantic vars), then expand if regressions appear.
- 84
baseIndigoAlpha1..12-style alpha tokens exist as separate hex values. None used per the audit, but spot-check before deleting.
next/font requires statically-known font choices, which is fine because branding is a static TS module — but be aware that the font instance must be resolvable at module load time, not lazily.
Trade-off note
If the refactor scope feels too large for the current cycle, the cheap version is:
- Add
font?: NextFont and cssVars?: Record<string, string> to DeploymentConfig.
- Inject
cssVars and the font in layout.tsx.
- Leave Style Dictionary in place.
That gets per-deployment fonts and a few-color override in ~half a day, at the cost of keeping the over-provisioned tokens pipeline.
I suspect that any reuser will want to change the look-and-feel of the platform, at least via CSS.
I had Claude prepare this prompt, but I don't think I'll have time to review and iterate on it, so sharing here.
Details
Refactor: replace Style Dictionary with shadcn-canonical CSS-var theming
Why
The current design-tokens pipeline (Style Dictionary → generated
_variables.css+ Tailwindcolor.js) is over-provisioned and harder to override per deployment than it needs to be. Refactoring to a CSS-variable-first setup makes per-deployment color overrides trivial AND aligns the codebase with the dominant Next.js + Tailwind v3 pattern.This plan also folds in a configurable-font change (branding ships a
next/fontinstance), since both touchlayout.tsxand theDeploymentConfigcontract.Current state (audit summary)
config/tokens/tokens.jsondeclares ~274 color tokens. Only ~13 unique base-color Tailwind classes are referenced anywhere in the app (bg-baseIndigoSolid1,border-baseGraySlateSolid4,text-baseGraySlateSolid11, etc.). ≥95% of generated tokens are dead weight.bg-base…/50etc.). So the rgb-channel form for CSS vars is not required.tailwind.config.jshas zero plugins and notheme.extend— it replaces Tailwind's defaults wholesale with Style Dictionary outputs.styles/tokens/_variables.css(--text-default,--text-interactive,--border-highlight-default,--icon-highlight,--background-solid-dark,--text-onbg-default, plus space/radius/font-size/z-index/outline). No collisions; opub-ui is a pure consumer.var(--…)consumption in app code is essentially zero (2 sites inglobals.css).next/font/googleInter loaded statically inapp/[locale]/layout.tsx:18.Target state
Theming source of truth is CSS variables in a single hand-authored file. Tailwind references those CSS vars. Branding overrides = inject CSS vars + provide a
next/fontinstance.No Style Dictionary, no generated JS color file, no
build:tokensscript.Steps
1. Inventory actually-used Tailwind token classes
Grep the app for all token-derived class usage and build the canonical list (~13 names per audit). Capture both the class name AND the hex value it currently resolves to (from
styles/tokens/tailwind/color.js).Add any semantic-token class usage (e.g.
bg-backgroundSolidDark,text-textDefault) found in the same grep.2. Author
styles/theme.cssNew file. Lift only the CSS vars actually referenced (by Tailwind classes, by opub-ui, and by
globals.css). Use the same semantic names already in_variables.cssso opub-ui keeps working. Approx size: ~50 vars (down from 112).Keep the existing format (hex values, plain
var(--name)); no need to refactor to rgb channels since no opacity utilities use these tokens.3. Rewrite
tailwind.config.jsReplace the imports from
styles/tokens/tailwind/*with hand-writtentheme.extend.colors(and any other categories) referencing CSS vars:Drop the
space,borderRadius,borderWidth,fontSize,lineHeight,boxShadow,zIndex,transitionTimingFunction,transitionDuration,fontWeightoverrides unless they're actually customised vs. Tailwind defaults — for any that are, keep them asvar(--…)references and add the corresponding entries totheme.css.Keep
corePlugins.preflight = false(still required to avoid clashing with opub-ui's reset).4. Update
styles/globals.cssReplace
@import './tokens/_variables.css';with@import './theme.css';. Leave the opub-ui and nprogress imports as-is.5. Delete obsolete machinery
config/tokens/(entire directory: tokens.json, sd-config.js, generate.mjs, copy.js, anything else)styles/tokens/(entire directory: _variables.css, tailwind/*)package.json: drop thebuild:tokensandpostbuild:tokensscripts; drop thestyle-dictionaryandfs-extradevDependencies if they're only used by the tokens pipeline.6. Extend the branding contract
In
ids-drr-branding-types/src/index.ts, add toDeploymentConfig:7. Update branding implementations
branding-stub/src/index.ts— no changes needed (defaults to undefined, which is fine).ids-drr-india-branding/src/index.tsandconfig.ts— declare the deployment's font (pick anext/font/googlefamily) and any CSS-var overrides.8. Wire through
config/site.tsAdd re-exports:
9. Apply in
app/[locale]/layout.tsx10. Verify
npx tsc --noEmit→ 0 errors.npm test→ 143/143 pass.npm run devagainst the stub: the home, analytics, and datasets pages look identical to before the refactor (regression check).npm run devagainst the India branding (with a font override and at least one cssVars override) → font and the chosen accent colour change as expected.npm run buildsucceeds withoutbuild:tokens(confirm script deletion didn't break the build chain).Files touched
styles/theme.csstailwind.config.js,styles/globals.css,app/[locale]/layout.tsx,config/site.ts,branding-stub/src/index.ts,package.jsonids-drr-branding-types/src/index.ts,ids-drr-india-branding/src/{index.ts,config.ts}config/tokens/**,styles/tokens/**Effort & risks
Effort: ~1 day. Dominated by inventorying actually-used tokens, hand-authoring
theme.cssandtailwind.config.js, and visual smoke-testing.Risks:
baseIndigoAlpha1..12-style alpha tokens exist as separate hex values. None used per the audit, but spot-check before deleting.next/fontrequires statically-known font choices, which is fine because branding is a static TS module — but be aware that the font instance must be resolvable at module load time, not lazily.Trade-off note
If the refactor scope feels too large for the current cycle, the cheap version is:
font?: NextFontandcssVars?: Record<string, string>toDeploymentConfig.cssVarsand the font inlayout.tsx.That gets per-deployment fonts and a few-color override in ~half a day, at the cost of keeping the over-provisioned tokens pipeline.