--- 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 `{children}`. 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-`