# 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. - `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. ### 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`, `products-mobile-card`, `health-entry-row` - Group headers: `products-section-title` - Table shell: `products-table-shell` - Tabs shell: `products-tabs`, `editorial-tabs` - 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. ### 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 `