# Frontend Design Cookbook
This cookbook defines the visual system for the frontend so every new change extends the existing style instead of inventing a new one.
## Agent workflow
- For any frontend edit, consult this cookbook before implementing changes.
- If a change introduces or alters reusable UI patterns, wrappers, component variants, tokens, motion rules, or shared classes, update this cookbook in the same change.
- Keep updates concise and actionable so future edits remain consistent.
## Design intent
- Core tone: light editorial, calm and information-first.
- Product feel: premium personal logbook, not generic SaaS dashboard.
- Contrast model: neutral paper and ink do most of the work; accents are restrained.
## Non-negotiables
- Keep layouts readable first. Aesthetic details support hierarchy, not the other way around.
- Use one visual language across the app shell, cards, forms, tables, and actions.
- Prefer subtle depth (borders, layered paper, soft shadows) over loud gradients.
- Keep motion purposeful and short; always preserve `prefers-reduced-motion` behavior.
## Typography
- Display/headings: `Cormorant Infant`.
- Body/UI text: `Manrope`.
- Use display typography for page titles and section heads only.
- Keep paragraph text in body font for legibility.
- Keep Google font loading aligned with current usage:
- `Cormorant Infant`: `600`, `700` (no italic)
- `Manrope`: `400`, `500`, `600`, `700`
## Color system
Global neutrals are defined in `frontend/src/app.css` using CSS variables.
- `--background`, `--card`, `--foreground`, `--muted-foreground`, `--border`
- `--page-accent` drives route-level emphasis.
- `--page-accent-soft` is the low-contrast companion tint.
### Domain accents (muted and professional)
- Dashboard: `--accent-dashboard`
- Products: `--accent-products`
- Routines: `--accent-routines`
- Skin: `--accent-skin`
- Profile: `--accent-profile`
- Health labs: `--accent-health-labs`
- Health medications: `--accent-health-meds`
The app shell assigns a domain class per route and maps it to `--page-accent`.
## Where to use accent color
Use accent for:
- active navigation state
- important buttons and key badges
- focus ring tint and hover border tint
- section separators and small status markers
Do not use accent for:
- full-page backgrounds
- body text
- large surfaces that reduce readability
Guideline: accent should occupy roughly 10-15% of visual area per screen.
## Layout rules
- Use the app shell spacing rhythm from `app.css` (`.app-main`, editorial cards).
- Keep max content width constrained for readability.
- Prefer asymmetry in hero/summary areas, but keep forms and dense data grids regular.
### Shared page wrappers
Use these wrappers before introducing route-specific structure:
- `editorial-page`: standard constrained content width for route pages.
- `editorial-hero`: top summary strip for title, subtitle, and primary actions.
- `PageHeader.svelte`: preferred reusable wrapper for page-level hero sections; use it to keep title hierarchy, backlinks, meta rows, and action placement consistent.
- `editorial-panel`: primary surface for forms, tables, and ledgers.
- `editorial-toolbar`: compact action row under hero copy.
- `editorial-backlink`: standard top-left back navigation style.
- `editorial-alert`, `editorial-alert--error`, `editorial-alert--success`, `editorial-alert--warning`, `editorial-alert--info`: feedback banners.
- `page-header-meta`, `page-header-foot`, `hero-strip`: shared secondary rows inside page headers for compact metadata and summary stats.
### Collapsible panels
For secondary information (debug data, reasoning chains, metadata), use this pattern:
```svelte
{#if expanded}
{/if}
```
This matches the warm editorial aesthetic and maintains visual consistency with Card components.
## Component rules
- Reuse UI primitives under `frontend/src/lib/components/ui/*`.
- Keep primitive APIs stable when changing visual treatment.
- Style via tokens and shared classes, not one-off hardcoded colors.
- New variants must be documented in this file.
### Existing shared utility patterns
These classes are already in use and should be reused:
- Lists and ledgers: `routine-ledger-row`, `editorial-mobile-card`, `health-entry-row`
- Group headers: `editorial-section-title`
- Table shell: `editorial-table-shell`
- Compact metadata rows: `editorial-meta-strip`
- Tabs shell: `products-tabs`, `editorial-tabs`
- App shell/navigation: `app-mobile-header`, `app-drawer`, `app-nav-list`, `app-nav-link`, `app-sidebar-footer`
- Reusable locale control: `LanguageSwitcher.svelte` with `language-switcher*` classes
- Dashboard summary patterns: `dashboard-stat-strip`, `dashboard-stat-card`, `dashboard-attention-list`, `dashboard-attention-item`
- Health semantic pills: `health-kind-pill*`, `health-flag-pill*`
- Lab results utilities:
- metadata chips: `lab-results-meta-strip`, `lab-results-meta-pill`
- filter/paging surfaces: `editorial-filter-row`, `lab-results-filter-banner`, `lab-results-pager`
- row/link rhythm: `lab-results-row`, `lab-results-code-link`, `lab-results-value-cell`
- mobile density: `lab-results-mobile-grid`, `lab-results-mobile-card`, `lab-results-mobile-value`
## Forms and data views
- Inputs should remain high-contrast and calm.
- Validation/error states should be explicit and never color-only.
- Tables and dense lists should prioritize scanning: spacing, row separators, concise metadata.
- Filter toolbars for data-heavy routes should use `GET` forms with URL params so state is shareable and pagination links preserve active filters.
- Use the products filter pattern as the shared baseline: compact search input, chip-style toggle rows (`editorial-filter-row` + small `Button` variants), and apply/reset actions aligned at the end of the toolbar.
- For high-volume medical data lists, default the primary view to condensed/latest mode and offer full-history as an explicit secondary option.
- For profile/settings forms, reuse shared primitives (`FormSectionCard`, `LabeledInputField`, `SimpleSelect`) before creating route-specific field wrappers.
- In condensed/latest mode, group rows by collection date using lightweight section headers (`products-section-title`) to preserve report context without introducing heavy card nesting.
- Change/highlight pills in dense tables should stay compact (`text-[10px]`), semantic (new/flag change/abnormal), and avoid overwhelming color blocks.
- For lab results, keep ordering fixed to newest collection date (`collected_at DESC`) and remove non-essential controls (no lab filter and no manual sort selector).
- For lab results, keep code links visibly interactive (`lab-results-code-link`) because they are a primary in-context drill-down interaction.
- For lab results, use compact metadata chips in hero sections (`lab-results-meta-pill`) for active view/filter context instead of introducing a second heavy summary card; keep this strip terse (one context chip + one stats chip, with optional alert chip).
- In dense row-based lists, prefer `ghost` action controls; use icon-only buttons on desktop tables and short text+icon `ghost` actions on mobile cards to keep row actions subordinate to data.
- For editable data tables, open a dedicated inline edit panel above the list (instead of per-row expanded forms) and prefill it from row actions; keep users on the same filtered/paginated context after save.
- When a list is narrowed to a single entity key (for example `test_code`), display an explicit "filtered by" banner with a one-click clear action and avoid extra grouping wrappers that add no context.
- For dashboard-style summaries, prefer compact stat strips and attention rows over large decorative cards; each item should pair one strong value with one short explanatory line.
### DRY form primitives
- Use shared form components for repeated native select markup:
- `frontend/src/lib/components/forms/SimpleSelect.svelte`
- `frontend/src/lib/components/forms/GroupedSelect.svelte`
- Use shared checkbox helper for repeated label+hint toggles:
- `frontend/src/lib/components/forms/HintCheckbox.svelte`
- Use shared input field helper for repeated label+input rows:
- `frontend/src/lib/components/forms/LabeledInputField.svelte`
- Use shared section card helper for repeated titled form panels:
- `frontend/src/lib/components/forms/FormSectionCard.svelte`
- Use shared class tokens from:
- `frontend/src/lib/components/forms/form-classes.ts`
- Prefer passing option labels from route files via `m.*` to keep i18n explicit.
### Select policy (performance + maintainability)
- Default to native `