deckhearth/.cursor/rules/ui-and-theming.mdc

70 lines
3.9 KiB
Text
Raw Permalink Normal View History

bootstrap: agent pipeline v0.5.0 + ship-readiness review Installs the three-layer agent-pipeline scaffold (https://github.com/varutasu/agent-pipeline @ v0.5.0): L1 — Context (curated brain) - AGENTS.md: orientation, conventions, 8 explicit gotchas - .cursor/rules/: no-go-zones, api-routes, auth-and-permissions, db-and-schema, ui-and-theming, schema-map - .cursor/skills/: add-api-route, add-page recipes - docs/agent-context/README.md: layer explainer - docs/SCHEMA_MAP.md: hand-curated Neon Postgres reference (replaces Prisma schema map since stack is raw SQL) L2 — Subagent roles (copied verbatim from upstream templates) - 9 .cursor/agents/role-*.md files: Conductor, IA-Architect, UX-Reviewer, Architect, Implementer, Reviewer, Design-System-Auditor, A11y-Auditor, Doc-Writer L3 — Pipeline scaffolding (Vercel variant) - CI: lint + schema-map-drift only (no duplicate build — Vercel handles it). Test job commented out until vitest lands. - preview-smoke + visual-diff via wait-for-vercel-preview - pr-health-rollup sticky comment aggregator - agent-context-drift weekly cron - PULL_REQUEST_TEMPLATE, CODEOWNERS (auth/admin paths tagged) - .convoys/ folder + seed ship-readiness.md review - lib/flags/index.js (JS — converted from TS template) - scripts/wt.sh (Cursor 3.2 deprecation stub), scripts/log-convoy-event.sh - tests/smoke/app.smoke.spec.ts (Playwright skeleton) Manifest - .agent-context-manifest.yml: tracks 31 artifacts by sha256 for future sync-agent-context drift detection Review - .convoys/ship-readiness.md: 16 findings (7 P0 ship-blockers, 5 P1 quality-bar, 4 P2 refactor, P3 UX/IA/a11y/docs) with proposed 13-convoy launch sequence. No production code changed in this commit. All findings in the ship-readiness review will be addressed in follow-up convoys starting with fix-auth-bypass. Structural brain: user-code-review-graph MCP has indexed the codebase (122 files, 628 nodes, 5602 edges, 11 communities, 84 flows). Per-developer; not committed. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
---
description: Tailwind + CSS-variable theming, component patterns, and a11y reminders
globs: components/**/*.js,pages/**/*.js
---
# UI + theming
## Theming model
Two systems coexist:
1. **Tailwind utility classes** (`text-gray-700`, `bg-white`, `dark:bg-gray-800`) — used for layout, spacing, and structural styles.
2. **CSS variables** (`var(--bg-primary)`, `var(--text-primary)`, `var(--accent-ember)`, `var(--accent-flame)`, `var(--border)`) — used for colors that need to switch with theme (light/dark).
**Don't mix and match within a single style declaration.** Pick one source per property. Generally:
- Backgrounds + text colors: CSS variables (via `style={{ backgroundColor: 'var(--bg-primary)' }}`).
- Spacing, sizing, flex, grid: Tailwind classes.
- Focus rings: CSS variables for color, Tailwind for everything else (`focus:outline-none focus:ring-2 focus:ring-offset-2` + `'--tw-ring-color': 'var(--accent-ember)'`).
Theme switching: `useTheme()` from `lib/theme-context.js`. Provider is wired in `pages/_app.js`.
## Component conventions
- Functional components, default-exported by name (`export default function CardItem(...)`).
- Props destructured in the signature with defaults: `function Layout({ children, user = null, showSearch = false })`.
- **Avoid hardcoded default values for `user` props.** `Layout` currently defaults `user` to a real email address — every page passing through Layout should pass `user` explicitly. New components must default to `null` and render a logged-out state.
## Layout
Pages render inside `<Layout user={user} showSearch={...}>{children}</Layout>`. Layout owns:
- Desktop sidebar + mobile bottom-nav (`components/MobileNavigation.js`).
- Theme toggle.
- User profile dropdown.
Don't duplicate navigation in a page — extend `NavigationContent` inside Layout instead.
## Accessibility
- Every interactive element needs a label: `aria-label`, `aria-labelledby`, or visible text.
- Modals need `role="dialog"`, `aria-modal="true"`, and focus management (trap focus + restore on close).
- Color contrast: stick to the documented theme tokens — they're tuned for AA.
- Keyboard: every `onClick` on a non-`<button>` needs `tabIndex={0}` + `onKeyDown` for Enter/Space.
## Common UI patterns to reuse
| Need | Where |
| --- | --- |
| Card grid item | `components/CardItem.js` |
| Bulk-action toolbar | `components/BulkSelectionToolbar.js` |
| Modal | `components/CollectionSelectionModal.js`, `components/ShareModal.js` |
| Image upload | `components/UploadImageModal.js` |
| Camera scanner | `components/CameraScanner.js` |
| Auth-required wrapper | `components/ProtectedRoute.js` |
| Admin-only wrapper | `components/AdminProtected.js` |
| Public-or-auth wrapper | inline in `pages/cards.js` (`PublicCardsView` / `AuthenticatedCards`) — pattern to copy |
## Branding
feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) Resolves the launch-blocking 'TCG Vault vs Deck Hearth' inconsistency called out in AGENTS.md line 5 since project setup. Operator gate-0 decision: Deck Hearth wins. Two briefs applied serially. B1 (mechanical): 7-file display + comment sweep. B2 (infrastructure): Redis prefix rename in lib/rate-limit.js (5 prefixes, accept one-time counter reset), package.json + lockfile regen (STOP-on-churn confirmed only name lines changed), admin/alice/bob email rename in seed scripts + login pre-fill + NEW idempotent migration script scripts/migrations/2026-05-24-rename-admin-email.js. Risk 4 PRESERVE applied: test/lib/permission-middleware.test.js retains admin@tcgvault.com literal with 7-line architect-authored why comment (documents pre-fix-auth-bypass bug shape; preserves historical truth per project's gotcha-documentation convention). All 5 D-decisions ratified at gate-1 (Deck Hearth / deck-hearth / deckhearth / admin@deckhearth.com / full deckhearth Redis prefix). Local: lint 128 baseline (B1 + B2), vitest 21/21 (B1 + B2). CI all green: Playwright smoke 3/3 against rebranded preview in 1m4s, forbidden-cors-headers pass, forbidden-endpoints pass, Screenshot diff pass, Vercel deployment complete. Cross-validation lineage: 4th convoy where the same 3-test smoke spec defends auth surface through sweeping change (after PR #15 Layout default-user, PR #19 CORS, PR #20 rate-limit, now this PR #21 brand rename). OPERATOR POST-MERGE ACTION REQUIRED: run 'node scripts/migrations/2026-05-24-rename-admin-email.js' against prod Neon DB before next admin login (ordering: migration FIRST, then any subsequent setup-db invocation). Migration is ESM, idempotent, UNIQUE-collision-safe. PR #21 architect-commit 50ce9ab, B1 ac8c998, B2 1c18d21.
2026-05-25 03:28:29 -04:00
The canonical product brand is **Deck Hearth** (two words, internal cap), ratified 2026-05-24 in the `pick-a-name` convoy. The repo directory + GitHub project name remain `tcg-vault` until the queued `rename-repo-and-vercel-project` convoy ships. New UI copy MUST use `Deck Hearth` verbatim — do not introduce a third name, do not abbreviate to "DH" outside the logo glyph (currently used in `components/Layout.js` lines 621, 714 as the sidebar logo monogram).
## Product vocabulary (ownership vs lists)
- **My Collection** — global ownership (`user_cards`, `/my-cards`). Never say "Owned Cards" or "Mark Owned" in UI.
- **Lists** — curated binders (`collections` table, `/collections`). Say "List" in buttons/modals; URL stays `/collections` until a future slug convoy.
- **Synced binder** — display name for the system collection (`is_system_collection`); DB literal `'All My Cards'` is internal only.
- Import copy from `lib/collection-vocabulary.js` (`VOCAB`, `collectionDisplayName`, `formatProcessedDestination`).
- CI `forbidden-stale-strings` enforces the three retired phrases in `pages/` + `components/`.