---
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-`