--- name: frontend-design description: Capsem frontend design system. Use when building UI components, styling views, working with the design system, choosing colors, or understanding the component library. Covers the stack (Astro 5 + Svelte 5 + Tailwind v4 + Preline), color scheme, Svelte 5 rune patterns, data fetching, and code reuse policy. --- # Frontend Design ## Stack - **Astro 5** -- static site generator, renders `index.astro` as a thin shell - **Svelte 5** -- reactive UI framework, loaded via `client:only="svelte"` - **Tailwind v4** -- utility-first CSS (via Vite plugin, `@source` directives in `global.css`) - **Preline** -- CSS-only: semantic design tokens and component CSS patterns. **Do NOT use Preline JS plugins.** All interactivity is implemented in pure Svelte 5 runes + TypeScript. Use Preline only for its token system (`bg-primary`, `text-foreground`, etc.) and CSS component patterns (class strings from the docs). Never import `preline` JS, never call `HSStaticMethods`, never use `data-hs-*` attributes or `hs-*-active:` variants. ## Loading into capsem-app (Tauri) `tauri::generate_context!()` bakes `frontend/dist/**` into the `capsem-app` binary at cargo compile time (via the `custom-protocol` feature). This means: - `pnpm run build` alone has **no effect** on a running `./target/**/capsem-app` -- the bundle is embedded in the binary. - After any `frontend/` change you intend to test in the desktop app, run `just build-ui` (chains frontend build + `cargo build -p capsem-app`). - `just ui` (`cargo tauri dev`) bypasses this by loading `http://localhost:5173` -- good for iteration, but the production code path goes through the embedded bundle. - The Toolbar shows `build YYYY-MM-DD HH:MM:SS` as a quick visual sanity check -- if it's stale after you rebuilt, you forgot `cargo build -p capsem-app`. Also: iframe `src` for bundled pages **must end in `index.html`** (e.g. `/vm/terminal/index.html`). Tauri's custom protocol on macOS does not auto-append `index.html` for trailing-slash paths the way Vite/Astro dev server does. A `/vm/terminal/` src loads fine in Chrome dev mode and silently 404s in the Tauri app. ## Design principles **Simplicity and correctness above all else.** Every line of frontend code must earn its place. - Preline CSS tokens for theming + Tailwind utilities for layout -- nothing else - All interactivity via Svelte 5 runes + TypeScript -- no JS plugins, no jQuery, no framework plugins - Custom `@theme` tokens in `global.css` for domain-specific colors (status, providers, charts) - **Visual verification required** -- every UI change must be verified via Chrome DevTools MCP (see `/dev-testing-frontend`) - **No DaisyUI** -- Preline is the only component library. DaisyUI remnants in the code are being replaced. ## Framework references - Read `references/preline.md` for Preline UI overview and quick reference. Detailed docs in `references/preline-docs/` covering JS plugins, CSS components, variants, tokens, and framework integration. - Read `references/tailwind.md` for Tailwind v4 utility patterns, responsive design, and CSS-first config. - Read `references/svelte5.md` for Svelte 5 patterns and `@sveltejs/mcp` CLI doc lookups. - Read `references/astro.md` for Astro framework patterns (components, content collections, SSR). ## Surface hierarchy (global.css overrides) The UI uses a two-tone surface system. Semantic token names map to specific roles: | Token | Light | Dark | Role | |-------|-------|------|------| | `--background` | `#ffffff` (white) | `#282828` (rgb 40,40,40) | Main canvas (content area) | | `--background-1` | `#f4f3f2` (rgb 244,243,242) | `#282828` | Recessed (address bar, inset panels) | | `--background-2` | `#f4f3f2` | `#282828` | Most recessed (inactive tabs) | | `--layer` | `#ffffff` (white) | `#3c3c3c` (rgb 60,60,60) | Elevated/selected (active tab, toolbar, cards) | The pattern: **selected = white/lighter, inactive = slightly gray/darker**. In dark mode, the base is very dark (#282828) and elevated surfaces pop with #3c3c3c. In light mode, the canvas is white and recessed areas use a warm off-white. These are set in `:root` and `.dark` blocks in `global.css`. All accent themes share the same surfaces -- only `--primary-*` changes per accent. ## Color scheme (firm -- do not deviate) - **Blue** = main/positive color (allowed, running, ok states). Use Preline `primary` tokens (`bg-primary`, `text-primary-foreground`, etc.) - **Purple** = negative color (denied, stopped, error states). Override Preline `destructive` tokens with purple, not red. - **No green or red anywhere in the UI** -- use blue for positive, purple for negative - Chart colors: blue `oklch(0.7 0.15 250)` for allowed, purple `oklch(0.65 0.15 300)` for denied - Terminal emulation colors (xterm #4ade80 green) are fine -- that's xterm, not UI chrome - **Do NOT hardcode colors or override Preline token CSS variables** (except the surface overrides above). Theme customization happens by selecting a Preline theme (`data-theme` on ``), not by overriding `--destructive` or other vars in `global.css`. ## Terminal theme contrast All 24 terminal themes (12 families x dark/light) must pass WCAG AA 4.5:1 contrast ratio for foreground text and all 6 ANSI colors (red, green, yellow, blue, magenta, cyan) against their background. This is enforced by `theme-contrast.test.ts`. Contrast utilities (`parseHex`, `relativeLuminance`, `contrastRatio`) are exported from `themes.ts` and used in tests. When adding or modifying terminal themes, run `pnpm test` to catch any violations. ## Component patterns Use Preline's semantic token classes for all UI components. Read `references/preline.md` for the overview and load the relevant `preline-docs/` reference for details. - **Buttons**: `bg-primary text-primary-foreground hover:bg-primary-hover` (solid), `bg-layer border border-layer-line text-layer-foreground` (white), etc. - **Cards**: `bg-card border border-card-line rounded-xl`, headers `bg-surface border-b border-card-divider` - **Forms**: `border-line-2 rounded-lg bg-layer text-foreground focus:border-primary focus:ring-primary` - **Navigation**: `bg-navbar border-navbar-border text-navbar-nav-foreground hover:bg-navbar-nav-hover` - **Overlays**: `bg-overlay border-overlay-border`, `bg-dropdown text-dropdown-item-foreground` - **Text hierarchy**: `text-foreground` (primary), `text-muted-foreground-1` (secondary), `text-muted-foreground` (tertiary) Do NOT use raw Tailwind colors (`bg-gray-200`, `text-blue-600`) for UI chrome. Always use semantic tokens so themes work. ### Settings section layout (SettingsSection.svelte) The Appearance section in `SettingsPage.svelte` is the reference pattern. All dynamic settings sections must match it: - **Section title**: `