f087551454
Adds 7 reusable primitives in src/lib/components/ui/ and migrates ~70 hand-rolled call sites across forms, admin panels, and routes. Tokens unchanged — same Cozy Home palette, just consistently applied. Primitives - Switch: pill toggle, role=switch, terracotta track, cubic-bezier knob - Button: 5 variants × 4 sizes, press-squash, loading spinner, buttonClass() helper for <a> link-as-CTA cases - Checkbox: rounded square with animated check-draw + indeterminate - Select: native <select> with Cozy chevron + matched radius - Slider: gradient track, terracotta-bordered knob, aria-valuetext - Input + Field: documented in CLAUDE.md for future use - 9 buttonClass unit tests Migrations - 23 <input type=checkbox> → <Switch> (boolean settings) - 5 multi-select checkboxes → <Checkbox> (DiscoveryPanel, sys-stats metrics) - ~28 <select> → <Select> - 17 <input type=range> → <Slider> (ThemeCustomizer's hue picker kept custom) - ~25 hand-rolled buttons → <Button> / buttonClass() Surface polish - Admin section wrappers: rounded-lg → rounded-[1.4rem] + shadow-soft (resolves the Phase-5 tradeoff from the Cozy migration memo) - BoardPropertiesPanel: live theme preview swatch showing computed hsl() on a sample button; hue/sat use Slider; bg/cardSize use Select - AppHealthBadge: role=status + aria-live=polite; .status-degraded (slow amber breathing) and .status-offline (single attention flash) now applied - AppForm collapse triggers: rotating chevron + aria-expanded - Empty states for /boards and /apps: inline SVGs using --room-* tokens (peach/sky/sage/butter) instead of generic Lucide icons - Login Remember Me: showcase Switch (first-impression surface) Motion (src/app.css) - New cozy-rise / cozy-rise-stagger for staggered grid reveals (/boards, /apps) - New cozy-expand for accordion sections (healthcheck, integration, wallpaper) - All motion respects prefers-reduced-motion CLAUDE.md - New project guide with a mandatory Frontend reuse table — every primitive documented with "never use raw <input type=checkbox>/<select>/<range>" and "do not repeat rounded-xl bg-primary px-4 py-2 ..." rules Verification - npm run check: 0 errors, 0 warnings, 5831 files - npm test: 301 passing - npm run lint: 0 errors (19 pre-existing warnings unchanged) - npm run build: ✔ done Branch is feat/cozy-polish, ready to PR against master.
57 lines
4.2 KiB
Markdown
57 lines
4.2 KiB
Markdown
# web-app-launcher — project guide for Claude
|
|
|
|
SvelteKit 2 + Svelte 5 (runes) + Tailwind 4 + Prisma + Vitest. Cozy Home design system (warm cream / dusk, terracotta accent, Fraunces + Figtree, soft shadows). Token contract lives in `src/app.css`.
|
|
|
|
## Frontend
|
|
|
|
### Basic-component reuse — MANDATORY
|
|
|
|
When you need any of the following, **use the existing primitive from `src/lib/components/ui/`. Do not hand-roll a new Tailwind class string for a control that already has a primitive.**
|
|
|
|
| Need | Primitive | Why |
|
|
|---|---|---|
|
|
| Boolean on/off setting | `Switch.svelte` | Pill toggle, `role="switch"`, AA contrast, terracotta track when on. Default for any "enable X" / "show Y" / "is default" field. **Never use `<input type="checkbox">` for booleans.** |
|
|
| Multi-select item in a list | `Checkbox.svelte` | Rounded square with animated check-draw. Only use when the control is truly "pick any number of these," not a single boolean. |
|
|
| Dropdown of fixed options | `Select.svelte` | Styled chevron, matches Cozy input radius. Wraps native `<select>`. **Do not use raw `<select>`.** |
|
|
| Single-line text / number / email / url / password | `Input.svelte` | Standard rounded-xl, focus ring, invalid state. **Do not repeat the `w-full rounded-xl border border-input bg-background px-3 py-2 ...` string anywhere.** |
|
|
| Number in a range (refresh interval, hue, blur, etc.) | `Slider.svelte` | Cozy gradient track, terracotta-bordered knob, value tooltip, `aria-valuetext`. **Do not use raw `<input type="range">`.** |
|
|
| Action button (submit, save, cancel, link-as-CTA) | `Button.svelte` | Variants `primary | secondary | outline | ghost | destructive`, sizes `sm | md | lg | icon`, built-in `loading` spinner, press-squash. **Do not repeat `rounded-xl bg-primary px-4 py-2 ...` strings.** |
|
|
| Label + hint + error wrapper around a control | `Field.svelte` | Consolidates `<label> + control + <p class="text-xs text-destructive">`. |
|
|
| Confirm-before-destructive | `ConfirmDialog.svelte` | Already exists. Use it. |
|
|
| Entity / icon / tag picker | `EntityPicker`, `MultiEntityPicker`, `IconPickerButton`, `TagsInput` | Already exist. Reuse. |
|
|
|
|
### Process
|
|
|
|
1. Before writing any form control in a `.svelte` file, **scan `src/lib/components/ui/` first**. If a matching primitive exists, import and use it.
|
|
2. If you find yourself copying a Tailwind class string verbatim from another file, **stop**: that's the trigger to extract a primitive (or expand an existing one).
|
|
3. If you genuinely need a new primitive, add it to `src/lib/components/ui/`, give it a `class?: string` prop merged via `cn()`, document it in this table, and migrate at least two call sites in the same PR so it's not dead code.
|
|
4. Tokens (`--primary`, `--card`, `--room-*`, `--shadow-soft`, etc.) are defined once in `src/app.css`. Never hardcode hex/HSL — read from the token.
|
|
|
|
### Cozy spec quick reminders
|
|
|
|
- Hero cards: `rounded-[1.4rem]` + `shadow-[var(--shadow-soft)]`. Dense panels: `rounded-xl`. **Never** `rounded-lg` on a section wrapper.
|
|
- Headings (`h1`, `h2`, `h3`) automatically get Fraunces via base layer — no need to add `font-display` unless overriding non-heading text.
|
|
- Focus uses `focus-visible:ring-2 focus-visible:ring-primary/30` — primitives already do this; mirror it on anything hand-rolled.
|
|
- Motion is gentle and present: prefer `cozy-rise` / `cozy-expand` from `app.css` over generic Tailwind animations. All motion classes already respect `prefers-reduced-motion`.
|
|
|
|
## Backend
|
|
|
|
- Auth: session cookie + optional OAuth. Roles: `admin` / user / guest. Always check role at the route load function, not the component.
|
|
- Validation: Zod schemas live in `src/lib/utils/validators.ts`. Reuse the same schema on client (superForms) and server.
|
|
- DB: Prisma. Never query the DB directly from a route — go through `src/lib/server/services/*Service.ts`.
|
|
|
|
## Testing
|
|
|
|
- Vitest, Node environment, no DOM (existing pattern). Component tests use the module-scope helpers (e.g., `buttonClass` in `Button.svelte`) rather than rendering — keep that convention.
|
|
- Run before committing: `npm run check && npm run lint && npm test && npm run build`.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm run dev # vite dev on :5181
|
|
npm run check # svelte-check (TS + Svelte)
|
|
npm run lint # eslint
|
|
npm test # vitest run
|
|
npm run build # production build
|
|
```
|