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

87 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.