Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 14 additions & 26 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,30 @@
# sv
# Titen Web Dashboard

Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
SvelteKit 5 + shadcn-svelte + Tailwind v4 frontend for the Titen Threads API manager (proxied to the Rust API by `src/hooks.server.ts`).

## Creating a project
## Design System

If you're seeing this, you've probably already done this step. Congrats!
- **[design/TOKENS.md](./design/TOKENS.md)** — design-token spec (Hallmark/Cobalt, OKLCH, hue 260). The single source of truth for color, spacing, type, radii, motion, and z-index.
- **[design/COMPONENT-STANDARDS.md](./design/COMPONENT-STANDARDS.md)** — canonical components (shadcn primitives + shared components) and the contracts every page must follow (toast, loading, empty, destructive actions, icons, focus/motion).

```sh
# create a new project
npx sv create my-app
```

To recreate this project with the same configuration:

```sh
# recreate this project
npx sv@0.16.4 create --template minimal --types ts --no-install .
```
Rules in short: consume semantic token aliases (no hex, no palette classes, no fallbacks), use shadcn `Button`/`Field`/`Dialog` (the legacy `.btn-*`/`.form-*` layer is removed), render feedback via the live-region toast, skeletons for any wait >150ms, shared `EmptyState` for zero states, two-stage confirmation for publish-triggering (HITL) actions.

## Developing

Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:

```sh
npm run dev

# or start the server and open the app in a new browser tab
npm run dev -- --open
bun install
bun run dev -- --open
```

## Building

To create a production version of your app:

```sh
npm run build
bun run build
bun run preview
```

You can preview the production build with `npm run preview`.
Type checking:

> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
```sh
npx svelte-check --threshold error
```
129 changes: 8 additions & 121 deletions web/bun.lock

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions web/design/COMPONENT-STANDARDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Titen Web — Component Standards

> What is canonical, what is deprecated, and the contract every page must follow.
> Token vocabulary: see [TOKENS.md](./TOKENS.md).

## Canonical Components

| Concern | Use | Notes |
|---|---|---|
| Buttons | `ui/button` `<Button>` | variants: `default`, `secondary`, `destructive`, `outline`, `ghost`; success = `default` + `class="bg-[var(--color-success)]"` |
| Forms | `ui/field` (`Field`, `FieldLabel`, `FieldDescription`, `FieldError`) + `ui/input`, `ui/select`, `ui/switch`, `ui/textarea` | every input has a label; validation errors link via `aria-describedby` + `aria-invalid` |
| Dialogs | `ui/dialog`, `ui/alert-dialog` | long content scrolls (`.detail-body`); full-bleed ≤30rem; destructive actions state the consequence in copy |
| Status display | `StatusBadge` | the only badge renderer — do not hand-roll status colors |
| Empty states | `EmptyState` | everywhere: tables, cards, calendar, dashboard lists |
| Tables | `DataTable` | provides skeleton loading, sort, expandable rows, keyboard row activation, `hideOnMobile` |
| Layout | `PageHeader` | renders the page `h1` + description + action slot |
| Media preview | `MediaLightbox` | Escape/backdrop/X close + body scroll lock — reuse this pattern for other overlays |
| Confirm flows | `ConfirmDialog` | destructive + irreversible actions (delete, HITL gates) |

**Deprecated:** the legacy `@layer components` classes (`.btn-*`, `.form-*`) were removed. shadcn primitives are the only component system. Raw `<button>`/`<select>`/`<table>` in pages are review blockers.

## Contracts

### Toast (feedback)
- Container: `role="status"` `aria-live="polite"`; error items `role="alert"`.
- Every toast carries a type icon (non-color cue). Success and error must be distinguishable without color.
- Every async action surfaces a toast on success AND failure. Error copy is human; raw `e.message` goes to console.

### Loading
- Any wait >150ms renders a **skeleton** shaped like the real content (`StatSkeleton`, `Skeleton` rows in `DataTable`, calendar cell grid). Bare "Loading…" text is not acceptable.
- Keep previous data visible during refetch where possible.

### Empty
- Zero-state uses `EmptyState` with a concrete next action ("Add your first account…"). Distinguish "no data" from "no results for filters" in copy.

### Destructive / HITL actions
- Irreversible or publish-triggering actions require two-stage confirmation (arm → confirm within 3s, `aria-pressed`) or `ConfirmDialog`.
- Reject-style flows collect a reason; approve-style flows state the publish consequence.
- Optimistic updates must revert on error and surface a failure toast.

### Icons
- Lucide only (`@lucide/svelte/icons/*`). No emoji in chrome. Distinct icons per nav item.

### Focus & Motion
- Global instant `:focus-visible` ring (`--color-focus`) — never remove, never animate.
- Durations from `--dur-*`, easing `--ease-out`; nothing above 400ms; `prefers-reduced-motion` honored globally.
- Interactive targets ≥40px on touch (`app.css` table rules handle this — keep them).

### Dark-mode readiness
- Only token references (`var(--…)`) in component CSS. Any new hardcoded color is a review blocker (the palette is light-theme today; tokens are the migration path).
69 changes: 69 additions & 0 deletions web/design/TOKENS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Titen Web — Design Token Spec

> Single source of truth for colors, spacing, type, radii, shadows, motion, and z-index.
> Implementation: `src/app.css` (`@theme` block). The system is **Hallmark / Cobalt** — OKLCH, anchor hue **260**.

## Layer Map

```
PRIMITIVES (--color-paper, --color-rule, --color-ink, --color-accent, …)
│ raw material — do NOT use directly in components
▼
SEMANTIC ALIASES (--color-bg, --color-border, --color-danger, --color-primary, …)
│ the API pages/components consume
▼
SHADCN MAPPING (--background, --primary, --destructive, --ring, …)
│ feeds ui/* primitives — keep in sync with aliases
▼
COMPONENT TOKENS (--form-*, --table-*, --sidebar-*, --z-*, --dur-*)
tuned per component family
```

## OKLCH Recipe

- **Anchor hue: 260** for all neutral surfaces, ink, and accent.
- **Semantic hues:** success `145`, warning `85`, error `25`, info `260`.
- **Text/ink chroma ≤ 0.008** (near-neutral, tinted by anchor hue). Accent carries the saturation (`0.20`).
- **Dim variants** (`-dim`) are high-lightness low-chroma washes for badge/soft backgrounds; `-ink` variants are their foreground pairs.

## Rules

1. **No hex fallbacks.** `var(--token, #fff)` is banned — if the token is missing, fix the token layer, not the call site. (`grep -rn "var(--[a-zA-Z-]*, *#" src` must return 0.)
2. **No raw hex / Tailwind palette classes in markup** (`text-green-600`, `bg-white`, …). Use semantic aliases.
3. **Pages consume semantic aliases** (or shadcn utilities mapped to them). Primitives are only for composing new aliases/tokens.
4. **New tokens get defined in `@theme` first**, then consumed. No phantom `var()` references.
5. Full pills use **`--radius-pill`** (there is deliberately no `--radius-full`).

## Token Inventory (see `app.css @theme` for values)

| Family | Tokens |
|---|---|
| Surfaces | `--color-paper(-2/-3)`, `--surface-base/raised/sunken/overlay` |
| Text | `--color-ink(-2)`, `--color-muted`, `--color-neutral` |
| Rules/borders | `--color-rule(-2)`, `--rule-default/subtle/strong`, `--color-border(-hover)` |
| Accent | `--color-accent(-dim/-ink)`, `--color-focus`, `--color-accent-subtle` |
| Status | `--color-success/warning/error/info` + `-dim` + `-ink`, `--color-*-bg`, `--color-bg-subtle` |
| Radii | `--radius-2xs → --radius-pill` |
| Spacing | `--space-3xs → --space-3xl` (4pt base) |
| Type | `--font-display/body/mono`, `--text-2xs → --text-3xl` (1.25 ratio) |
| Shadows | `--shadow-whisper/raised/overlay` |
| Motion | `--dur-short/base/long`, `--ease-out/in` |
| Z-index | `--z-base → --z-tooltip` (6 named levels) |
| Components | `--form-*`, `--table-*`, `--sidebar-*` |

## Shadcn ↔ Token Mapping

| shadcn name | Titen token |
|---|---|
| `--background` / `--foreground` | `--color-paper` / `--color-ink` |
| `--card` | `--color-paper-2` |
| `--popover` | `--color-paper` |
| `--primary` / `--primary-foreground` | `--color-accent` / `--color-accent-ink` |
| `--secondary` | `--color-paper-3` |
| `--muted` / `--muted-foreground` | `--color-paper-3` / `--color-muted` |
| `--accent` | `--color-accent-dim` |
| `--destructive(-foreground)` | `--color-error(-ink)` |
| `--input` / `--border` | `--color-rule` |
| `--ring` | `--color-focus` |

Adding a new shadcn primitive requires BOTH the `:root` mapping and a `--color-<name>` entry in `@theme` (Tailwind v4 only emits utilities for `@theme` names).
5 changes: 0 additions & 5 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,13 @@
"@sveltejs/kit": "^2.70.2",
"@sveltejs/vite-plugin-svelte": "^7.3.0",
"@tailwindcss/vite": "^4.3.3",
"embla-carousel-svelte": "^8.6.0",
"formsnap": "^2.0.1",
"layerchart": "^2.1.0",
"mode-watcher": "^1.1.0",
"paneforge": "^1.0.2",
"svelte": "^5.56.8",
"svelte-check": "^4.7.5",
"svelte-sonner": "^1.1.1",
"sveltekit-superforms": "^2.30.2",
"tailwindcss": "^4.3.3",
"typescript": "^6.0.3",
"vaul-svelte": "^1.0.0-next.7",
"vite": "^8.2.1"
},
"dependencies": {
Expand Down
Loading
Loading