Files
suaveplan/openspec/changes/enterprise-react-ui-theme-dracula/tasks.md
2026-05-27 23:29:53 +01:00

7.7 KiB
Raw Blame History

tasks — enterprise-react-ui-theme-dracula

0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Package scaffolding

  • 1.1 Create packages/react/react-ui-theme-dracula/ directory.
  • 1.2 Author package.json at version 0.1.0 with peer-deps @suaveplan/react-ui and @suaveplan/react-i18n (both workspace:^), tslib from catalog:build as the only runtime dep, devDeps for the testing wrappers + biome + vitest from catalog refs. "sideEffects": ["./src/translations/index.js", "./styles/dracula.css"] so the side-effect import that registers translations and the CSS file are not tree-shaken away.
  • 1.3 Author tsconfig.json extending @suaveplan/typescript-config/library-browser.
  • 1.4 Author vitest.config.ts using createTestConfig() from @suaveplan/testing/config, istanbul provider, thresholds 100/100/100/100.
  • 1.5 Author the subpath exports map: ., ./theme/dracula, ./tailwind-preset, ./styles/dracula.css, ./translations/<locale> for each of the five locales.
  • 1.6 Confirm subpath count matches the §6 / §13 step-8 helper output.

2. Palette + ThemePlugin

  • 2.1 Create src/theme/dracula.ts with the canonical Dracula palette as draculaCssVariables: ThemeTokenSet. Inline-comment every foreground/background pair with its measured contrast ratio.
  • 2.2 Tune any pair below WCAG AA (4.5:1 normal text / 3:1 large + UI) and document the adjustment delta in the same comment.
  • 2.3 Construct draculaTheme: ThemePlugin<'dracula'> with name, labelKey: 'react-ui-theme-dracula.label', cssVariables, tailwindPreset, variantOverrides, motionTokens, colorScheme: 'dark', fontStack (Dracula's recommended JetBrains Mono / Inter pairing).
  • 2.4 Author src/theme/dracula.md with the six required sections (≥200 words excluding code blocks).

3. Variant overrides — both axes

  • 3.1 Create src/theme/variant-overrides/button.ts overriding Button.variants.variant.default with the Dracula purple shadow and adding Button.variants.size.xl (h-14 px-10 text-xl).
  • 3.2 Create src/theme/variant-overrides/card.ts overriding Card.variants.variant.default with Dracula border + foreground tokens (override-only path).
  • 3.3 Author sibling .md files for each.
  • 3.4 Author sibling .test.ts files; the Button test pins both the override merge AND the brand-new size: 'xl' resolution through a narrowed useThemedVariants<'dracula', 'Button'>('Button') call.

4. Motion tokens

  • 4.1 Create src/theme/motion-tokens.ts with respectReducedMotion: 'soft', halved durations under reduced-motion, spring disabled, crossfade easings preserved.
  • 4.2 Author motion-tokens.md.
  • 4.3 Author motion-tokens.test.ts mocking matchMedia('(prefers-reduced-motion: reduce)') and asserting the soft branch resolves the documented values.

5. Tailwind preset + CSS file

  • 5.1 Create src/tailwind-preset/tailwind-preset.ts registering the Dracula colour scale (50–950) plus the brand-new xl size token. Plain object literal — no runtime tailwind import.
  • 5.2 Author tailwind-preset.md.
  • 5.3 Author tailwind-preset.test.ts asserting the preset shape.
  • 5.4 Create src/styles/dracula.css declaring the [data-theme='dracula'] block under @layer base for non-Tailwind / SSR-fallback consumers.

6. Translations × 5 locales

  • 6.1 Create src/translations/en-GB.json with { "react-ui-theme-dracula.label": "Dracula" }. (Source of truth; English keeps the brand name.)
  • 6.2 Create src/translations/zh-CN.json ("德古拉" — confirm with native reviewer in PR).
  • 6.3 Create src/translations/ar-AE.json ("دراكولا" — confirm with native reviewer in PR).
  • 6.4 Create src/translations/cy-GB.json ("Dracula" — proper noun stays untranslated).
  • 6.5 Create src/translations/ru-RU.json ("Дракула" — confirm with native reviewer in PR).
  • 6.6 Create src/translations/augmentation.d.ts augmenting ContributedTranslationKeys with 'react-ui-theme-dracula.label': true.
  • 6.7 Create src/translations/index.ts exporting the runtime register / unregister helpers wired into draculaTheme.onRegister / onUnregister. Side-effect-imports augmentation.d.ts.
  • 6.8 Author src/translations/index.md.
  • 6.9 Author src/translations/index.test.ts covering register / unregister for all five locales + the type-level expectTypeOf assertion that 'react-ui-theme-dracula.label' is assignable to TranslationKey.

7. Index + barrel

  • 7.1 Create src/index.ts with explicit named re-exports — draculaTheme, draculaCssVariables, draculaTailwindPreset — plus the side-effect import of ./translations/index.js. No export * (CLAUDE.md §10).
  • 7.2 Verify zero export * matches inside src/<feature>/index.ts with the §13 step-4a grep.

8. Contract tests

  • 8.1 Create tests/contract.test.tsx: render <ThemeProvider themes={[draculaTheme]} defaultTheme='dracula'><div data-testid='probe' /></ThemeProvider>, assert data-theme='dracula' on the root, assert getComputedStyle resolves --background to the dracula token, assert useThemedVariants<'dracula', 'Button'>('Button')({ size: 'xl' }) returns a non-empty class string.
  • 8.2 Create tests/no-radix-deps.test.ts: parse package.json, assert no @radix-ui/* or react-dom key in any dep block.
  • 8.3 Create tests/wcag-aa.test.ts: iterate the documented foreground/background pairs, run them through a contrast-ratio helper, assert every pair clears AA.

9. Storybook integration in react-ui

  • 9.1 In packages/react/react-ui/src/components/button/button.stories.tsx, add a WithTheme: dracula story that wraps <Button> in <ReactUiProvider themes={[lightTheme, draculaTheme]} defaultTheme='dracula'> (importing draculaTheme as a devDep). Story import is dev-only so it does not introduce a runtime cycle.

10. README + docs sweep

  • 10.1 Author packages/react/react-ui-theme-dracula/README.md with package name, description, install (bun add @suaveplan/react-ui-theme-dracula), basic usage, narrowed-variant usage, custom Tailwind extension example, modules table linking every co-located .md.
  • 10.2 Run bun run docs:check and confirm all sibling .md files exist and meet the ≥200-word floor.

11. Pre-publish gate

  • 11.1 bunx turbo lint typecheck test --filter=@suaveplan/react-ui-theme-dracula green.
  • 11.2 bunx turbo test:coverage --filter=@suaveplan/react-ui-theme-dracula at 100/100/100/100.
  • 11.3 bunx turbo build --filter=@suaveplan/react-ui-theme-dracula green.
  • 11.4 bun scripts/lint/check-test-runner-imports.ts green for this package (zero direct vitest / bun:test imports — CLAUDE.md §11).
  • 11.5 §13 step-4 banned-pattern grep returns zero matches.
  • 11.6 §13 step-9 secret scan returns zero matches.
  • 11.7 Refresh MANIFEST.md via bun scripts/folder-reorg/generate-manifest.ts and refresh MISSING.yml via bun scripts/generate-missing.ts. Update STATUS.md and SUMMARY.md per §13b.
  • 11.8 bunx markdownlint-cli2 --fix then a clean second pass with no --fix.