Skip to content
Draft
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
73 changes: 73 additions & 0 deletions .design-sync/EXTRACTION_SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Extraction spec for design-sync subagents

You extract a **framework-agnostic** spec of shadcn-vue components for upload to claude.ai/design.
The design tool renders React, so we DO NOT ship runnable component code — only contracts, docs, and a
discoverable preview card. Everything you write must be derived by **reading** the real source under
`/home/corrin/src/docketworks/frontend/src/components/ui/<group>/` (the `.vue` files + `index.ts`).
Do not invent props, variants, or parts — read them from `defineProps`, the cva config in `index.ts`,
and the reka-ui primitive each part wraps.

## For each assigned group, write THREE files

Output dir: `/home/corrin/src/docketworks/.design-sync/ds-bundle/components/<group>/<Name>/`
where `<Name>` is the PascalCase group name (e.g. `dropdown-menu` -> `DropdownMenu`, `button` -> `Button`).

### 1. `<Name>.d.ts` — the API contract
- A TypeScript declaration for the component (and every exported sub-component/part).
- Export a `<Name>Props` interface (and per-part `XProps` interfaces) with the REAL prop names and types
read from each `.vue`'s `defineProps`/`withDefaults`. Resolve cva `VariantProps` into literal unions
(e.g. `variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'`).
- Include `class?: string` where the component forwards a class via `cn()`.
- Note slots in a JSDoc comment per component (`@slot default`, named slots if any).
- Re-export the cva helper type if one exists (e.g. `export type ButtonVariants = ...`).
- Keep it pure declarations — no implementation.

### 2. `<Name>.prompt.md` — usage reference for the design agent
Sections, terse and concrete:
- **`# <Name>`** one-line description + when to use it.
- **Parts** — bullet list of the exported components from `index.ts` and what each is for.
- **Props** — the key props/variants/sizes with their literal values and defaults (from `defaultVariants`).
- **Usage** — ONE minimal composition example using the REAL import (`import { ... } from '@/components/ui/<group>'`)
and the REAL component/part names and slot structure. Vue SFC `<template>` snippet is fine — it documents
composition; the agent translates intent, not literal code.
- **Notes** — gotchas: which reka-ui primitive backs it, controlled-vs-uncontrolled (`v-model`), required wrappers
(e.g. Trigger must be inside Root), accessibility behaviors. Only what you can verify from source.

### 3. `<Name>.html` — discoverable preview card
- **First line MUST be exactly:** `<!-- @dsCard group="<Group>" -->` where `<Group>` is the category you were
assigned (e.g. `Forms`, `Overlays`, `Display`, `Data & Navigation`, `Date & Time`).
- A self-contained HTML fragment that renders a faithful static preview using the **real design tokens**.
Paste the token `:root` block below into a `<style>` so the card renders standalone (do NOT rely on an
external stylesheet being injected). Use the token CSS variables (`var(--primary)`, `var(--radius)`, etc.)
and the component's real geometry (heights/padding read from the cva size classes) so the preview looks right.
- Show the component title, its main variants/sizes, and a representative instance. Keep it compact
(a card ~360px wide). This is a human-facing picker thumbnail, not a functional component.

## Token `:root` block to embed in every card's `<style>`

```css
:root{
--background:oklch(1 0 0);--foreground:oklch(0.129 0.042 264.695);
--card:oklch(1 0 0);--card-foreground:oklch(0.129 0.042 264.695);
--popover:oklch(1 0 0);--popover-foreground:oklch(0.129 0.042 264.695);
--primary:oklch(0.208 0.042 265.755);--primary-foreground:oklch(0.984 0.003 247.858);
--secondary:oklch(0.968 0.007 247.896);--secondary-foreground:oklch(0.208 0.042 265.755);
--muted:oklch(0.968 0.007 247.896);--muted-foreground:oklch(0.554 0.046 257.417);
--accent:oklch(0.968 0.007 247.896);--accent-foreground:oklch(0.208 0.042 265.755);
--destructive:oklch(0.577 0.245 27.325);--destructive-foreground:oklch(0.577 0.245 27.325);
--border:oklch(0.929 0.013 255.508);--input:oklch(0.929 0.013 255.508);--ring:oklch(0.704 0.04 256.788);
--radius:0.625rem;
font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
}
```

Radius scale: `--radius-sm: calc(var(--radius) - 4px)`, `-md: calc(var(--radius) - 2px)`, `-lg: var(--radius)`,
`-xl: calc(var(--radius) + 4px)`. Default control height is `h-9` (2.25rem); sm `h-8`; lg `h-10`.

## Rules
- Read before you write. Never guess a prop or variant — open the file.
- shadcn-vue groups bundle parts (Root/Trigger/Content/etc.). Document the GROUP as one component with parts.
- If a group has no variants (e.g. `input`), say so; don't fabricate a variant axis.
- Do not edit anything under `frontend/`. Write only under `.design-sync/ds-bundle/`.
- Report back: a one-line-per-group summary of parts + variants found, and any anomaly. Keep file CONTENT out
of your reply — you wrote it to disk; just summarize.
28 changes: 28 additions & 0 deletions .design-sync/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# design-sync notes

## This is a manual, off-script extraction
The repo (`frontend/`) is a **private application**, not a publishable component library — there is no
library `dist/`, no Storybook, and the components are **Vue** (reka-ui). Claude Design renders **React**,
so the standard converter (`package-build.mjs`) does not apply. Per user direction, we do NOT build an
isolated Vite lib and do NOT stub-and-render-verify (couplings in `ui/` are minimal: Pinia is cache-only,
all API access via the generated OpenAPI client).

**Deliverable = framework-agnostic only:** `styles.css` + `tokens/`, per-component `.d.ts` + `.prompt.md`
+ a self-contained preview `.html` card, and a `conventions.md` header. **No `_ds_bundle.js`.**

## Source of truth
- Tokens: `frontend/src/assets/main.css` (`@theme inline` + `:root` + `.dark`, oklch).
- Components: `frontend/src/components/ui/<group>/` — 28 groups (one standalone `LoadingState.vue`).
- `cn()` = `frontend/src/lib/utils.ts`; cva only in `button`, `alert`, `badge`.

## Re-sync procedure (manual)
Re-run the extraction by re-reading `frontend/src/components/ui/` and rebuilding `.design-sync/ds-bundle/`.
There is **no `_ds_sync.json` anchor** (the converter's hashing recipe doesn't fit this layout), so a
re-sync re-extracts everything — correct for this flow. The build dir is regenerable; not committed.

## Gotchas found during extraction
- `LoadingState` uses hard-coded grays/blue, not design tokens.
- `Drawer` is vaul-vue, not reka-ui. `Pagination` and `Collapsible` are custom (no upstream parts;
Collapsible has no Trigger part).
- `Sonner` re-exports only `Toaster`; `toast()` comes straight from `vue-sonner`.
- `sonner` group's component dir is named `Sonner` (PascalCase of the group), though the export is `Toaster`.
9 changes: 9 additions & 0 deletions .design-sync/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"projectId": "3511bfb4-deb6-409a-86ad-5f99e5382b32",
"pkg": "frontend",
"shape": "package",
"mode": "tokens-and-specs-only",
"note": "Vue (reka-ui) component kit. Claude Design renders React, so no _ds_bundle.js is produced. We extract framework-agnostic artifacts only: oklch token system, styles.css, per-component prop/variant/slot specs (.d.ts + .prompt.md), and a conventions header. Extraction is read-only from frontend/src/components/ui/ — no isolated lib build, no stub-and-render verification (per user direction: couplings are minimal; Pinia is cache-only, all API via the generated OpenAPI client).",
"source": "frontend/src/components/ui",
"readmeHeader": ".design-sync/conventions.md"
}
72 changes: 72 additions & 0 deletions .design-sync/conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Docketworks Design System

A **shadcn-vue** kit (new-york style, slate base) on **Tailwind v4**. The components are authored in
Vue (reka-ui primitives); this design system ships their **design language and API contracts**, not a
runnable bundle. Build on-brand UI by using the token vocabulary and idiom below, and read each
component's spec for its parts and props.

## Setup

- Load `styles.css` — it defines every token as a CSS custom property on `:root` (light) and `.dark`
(dark), and, for Tailwind v4 pipelines, an `@theme inline` block that generates the matching utilities.
- **Dark mode** is class-based: add `class="dark"` to a root element. There is no media-query auto-switch.
- Font is **Inter** with `font-feature-settings: 'cv11','ss01'`. Default body size 15px.

## The styling idiom — Tailwind utilities bound to semantic tokens

Style with Tailwind utility classes whose color comes from **semantic tokens**, never raw hex. Every
token below exists as `bg-<token>`, `text-<token>`, and `border-<token>`:

| Token | Use for |
|---|---|
| `background` / `foreground` | page surface + default text |
| `card` / `card-foreground` | raised panels |
| `popover` / `popover-foreground` | floating surfaces (menus, popovers, tooltips) |
| `primary` / `primary-foreground` | primary actions, emphasis |
| `secondary` / `secondary-foreground` | secondary solid controls |
| `muted` / `muted-foreground` | subdued backgrounds + secondary text |
| `accent` / `accent-foreground` | hover/active surface |
| `destructive` | danger actions, errors |
| `border` / `input` / `ring` | hairlines, field borders, focus rings |
| `chart-1`..`chart-5`, `sidebar*` | data viz, app sidebar |

Pair a base with its `-foreground` (e.g. `bg-primary text-primary-foreground`). For opacity use the
slash modifier (`bg-primary/90`, `ring-ring/50`). A fixed `gray/green/yellow/red/blue/slate` hex palette
(see `tokens/colors.json`) is available for status accents only.

Radius: `rounded-sm | -md | -lg | -xl`, all derived from one `--radius` base (0.625rem). Buttons/inputs
use `rounded-md`; cards/popovers `rounded-lg`/`xl`.

### Two helpers the kit relies on
- **`cn(...)`** = `twMerge(clsx(...))` — merge/override classes safely. Every component forwards an optional
`class` prop through `cn()`, so consumer classes win over defaults.
- **`cva`** (class-variance-authority) — declares variant→class maps. In this kit it is used sparingly:
`Button` (`buttonVariants`), `Alert`, and `Badge` have real cva variant axes; most components have **no
variant axis** and are styled purely by composition + `class`.

## Where the truth lives

- `styles.css` and `tokens/*.json` — the token system. Read before choosing any color/radius.
- `components/<group>/<Name>/<Name>.prompt.md` — per-component usage, parts, props, gotchas.
- `components/<group>/<Name>/<Name>.d.ts` — the typed API contract (props, variant unions, slots).

## One idiomatic snippet

```html
<!-- A card with a primary action, using only semantic tokens -->
<div class="rounded-lg border bg-card text-card-foreground shadow-sm p-6">
<h3 class="text-sm font-semibold">Job #J-1042</h3>
<p class="text-sm text-muted-foreground mt-1">Awaiting quote approval</p>
<div class="mt-4 flex gap-2">
<button class="inline-flex h-9 items-center rounded-md bg-primary px-4 text-sm
font-medium text-primary-foreground hover:bg-primary/90">Approve</button>
<button class="inline-flex h-9 items-center rounded-md border bg-background px-4 text-sm
font-medium hover:bg-accent hover:text-accent-foreground">Dismiss</button>
</div>
</div>
```

Available components (read the per-component specs for parts/props): Button, Input, Textarea, Label,
Checkbox, Switch, Select, Dialog, Drawer, Popover, Tooltip, DropdownMenu, Alert, Badge, Avatar, Card,
Skeleton, Progress, Sonner (toaster), LoadingState, Table, Tabs, Pagination, Collapsible, Calendar,
RangeCalendar, DatePicker, CustomDatePicker.
120 changes: 120 additions & 0 deletions .design-sync/ds-bundle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Docketworks Design System

A **shadcn-vue** kit (new-york style, slate base) on **Tailwind v4**. The components are authored in
Vue (reka-ui primitives); this design system ships their **design language and API contracts**, not a
runnable bundle. Build on-brand UI by using the token vocabulary and idiom below, and read each
component's spec for its parts and props.

## Setup

- Load `styles.css` — it defines every token as a CSS custom property on `:root` (light) and `.dark`
(dark), and, for Tailwind v4 pipelines, an `@theme inline` block that generates the matching utilities.
- **Dark mode** is class-based: add `class="dark"` to a root element. There is no media-query auto-switch.
- Font is **Inter** with `font-feature-settings: 'cv11','ss01'`. Default body size 15px.

## The styling idiom — Tailwind utilities bound to semantic tokens

Style with Tailwind utility classes whose color comes from **semantic tokens**, never raw hex. Every
token below exists as `bg-<token>`, `text-<token>`, and `border-<token>`:

| Token | Use for |
|---|---|
| `background` / `foreground` | page surface + default text |
| `card` / `card-foreground` | raised panels |
| `popover` / `popover-foreground` | floating surfaces (menus, popovers, tooltips) |
| `primary` / `primary-foreground` | primary actions, emphasis |
| `secondary` / `secondary-foreground` | secondary solid controls |
| `muted` / `muted-foreground` | subdued backgrounds + secondary text |
| `accent` / `accent-foreground` | hover/active surface |
| `destructive` | danger actions, errors |
| `border` / `input` / `ring` | hairlines, field borders, focus rings |
| `chart-1`..`chart-5`, `sidebar*` | data viz, app sidebar |

Pair a base with its `-foreground` (e.g. `bg-primary text-primary-foreground`). For opacity use the
slash modifier (`bg-primary/90`, `ring-ring/50`). A fixed `gray/green/yellow/red/blue/slate` hex palette
(see `tokens/colors.json`) is available for status accents only.

Radius: `rounded-sm | -md | -lg | -xl`, all derived from one `--radius` base (0.625rem). Buttons/inputs
use `rounded-md`; cards/popovers `rounded-lg`/`xl`.

### Two helpers the kit relies on
- **`cn(...)`** = `twMerge(clsx(...))` — merge/override classes safely. Every component forwards an optional
`class` prop through `cn()`, so consumer classes win over defaults.
- **`cva`** (class-variance-authority) — declares variant→class maps. In this kit it is used sparingly:
`Button` (`buttonVariants`), `Alert`, and `Badge` have real cva variant axes; most components have **no
variant axis** and are styled purely by composition + `class`.

## Where the truth lives

- `styles.css` and `tokens/*.json` — the token system. Read before choosing any color/radius.
- `components/<group>/<Name>/<Name>.prompt.md` — per-component usage, parts, props, gotchas.
- `components/<group>/<Name>/<Name>.d.ts` — the typed API contract (props, variant unions, slots).

## One idiomatic snippet

```html
<!-- A card with a primary action, using only semantic tokens -->
<div class="rounded-lg border bg-card text-card-foreground shadow-sm p-6">
<h3 class="text-sm font-semibold">Job #J-1042</h3>
<p class="text-sm text-muted-foreground mt-1">Awaiting quote approval</p>
<div class="mt-4 flex gap-2">
<button class="inline-flex h-9 items-center rounded-md bg-primary px-4 text-sm
font-medium text-primary-foreground hover:bg-primary/90">Approve</button>
<button class="inline-flex h-9 items-center rounded-md border bg-background px-4 text-sm
font-medium hover:bg-accent hover:text-accent-foreground">Dismiss</button>
</div>
</div>
```

Available components (read the per-component specs for parts/props): Button, Input, Textarea, Label,
Checkbox, Switch, Select, Dialog, Drawer, Popover, Tooltip, DropdownMenu, Alert, Badge, Avatar, Card,
Skeleton, Progress, Sonner (toaster), LoadingState, Table, Tabs, Pagination, Collapsible, Calendar,
RangeCalendar, DatePicker, CustomDatePicker.

---

## Component index

Each component ships a typed contract (`.d.ts`), a usage reference (`.prompt.md`), and a preview card
(`.html`) under `components/<group>/<Name>/`. Most are reka-ui primitives; exceptions noted.

### Forms
- **Button** — primary action control. cva variants (default/destructive/outline/secondary/ghost/link) × sizes (default/sm/lg/icon). Polymorphic via `as`/`asChild`.
- **Input** — native text field, fixed `h-9`, `v-model`. No variants.
- **Textarea** — auto-grow native textarea (`field-sizing-content`), `min-h-16`. No variants.
- **Label** — reka-ui label, disabled-aware via peer/group. No variants.
- **Checkbox** — reka-ui, tri-state (`boolean | 'indeterminate'`). No variants.
- **Switch** — reka-ui toggle, optional `thumb` slot. No variants.
- **Select** — 11 parts (Trigger/Value/Content/Group/Label/Item/Separator/scroll buttons). Trigger size axis (`default`/`sm`).

### Overlays
- **Dialog** — modal; parts incl. Content, ScrollContent, Header/Footer/Title/Description, Close. Content auto-portals + ships its X.
- **Drawer** — **vaul-vue** (not reka-ui); placement via root `direction` (default bottom); `shouldScaleBackground` default true.
- **Popover** — Trigger/Anchor/Content; Content `align='center'`, `sideOffset=4`.
- **Tooltip** — Provider/Trigger/Content; `delayDuration` default **0**, built-in arrow.
- **DropdownMenu** — full menu set incl. Checkbox/Radio items, Sub-menus, Shortcut. Item `variant` default/destructive + `inset`.

### Display
- **Alert** — cva variant default/destructive; leading svg auto-positioned.
- **Badge** — cva variant default/secondary/destructive/outline; polymorphic.
- **Avatar** — Avatar/AvatarImage/AvatarFallback; size via `class` (default `size-8`).
- **Card** — Card + Header/Title/Description/Action/Content/Footer. Horizontal padding lives on inner parts.
- **Skeleton** — pulse placeholder, sized via `class`.
- **Progress** — reka-ui, `modelValue` 0–100, determinate.
- **Sonner** — `Toaster` host wrapping vue-sonner; `toast()` imported directly from `vue-sonner` (not re-exported).
- **LoadingState** — bespoke tri-state (loading/empty/content) wrapper; slots default/empty-icon/empty-actions. Uses some hard-coded grays (not tokens).

### Data & Navigation
- **Table** — native table elements; 9 parts incl. TableEmpty (`colspan`). Selected-row styling via `data-[state=selected]`. `valueUpdater` helper for @tanstack/vue-table.
- **Tabs** — reka-ui; List/Trigger/Content; `v-model`, `orientation`, `activationMode`.
- **Pagination** — custom (not reka-ui): ghost Buttons + chevrons, windowed (max 10), `page`/`total`, emits `update:page`.
- **Collapsible** — custom provide/inject; Collapsible + CollapsibleContent; **no Trigger part** (wire your own); `v-model:open`.

### Date & Time
- **Calendar** — reka-ui CalendarRoot; 12 parts (Header/Heading/Prev/Next, Grid*, HeadCell, Cell, CellTrigger). v-model `DateValue` (`@internationalized/date`).
- **RangeCalendar** — reka-ui RangeCalendarRoot; mirrors Calendar; v-model range `{ start, end }`.
- **DatePicker** — composed Button + Popover + Calendar; v-model ISO string `'YYYY-MM-DD' | null` (`en-NZ` formatting).
- **CustomDatePicker** — two native `<input type="date">`; v-model `DateRange { from?, to? }` ISO strings. Not a calendar.

> **Note on rendering:** these are Vue components. Claude Design renders React, so no runnable component
> bundle is shipped — use the tokens + idiom above to build on-brand, and the per-component specs for API shape.
23 changes: 23 additions & 0 deletions .design-sync/ds-bundle/components/alert/Alert/Alert.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { VariantProps } from 'class-variance-authority'

declare const alertVariants: (props?: {
variant?: 'default' | 'destructive'
}) => string

export type AlertVariants = VariantProps<typeof alertVariants>

export interface AlertProps {
/** Visual style. @default 'default' */
variant?: 'default' | 'destructive'
/** Extra classes merged via cn(). */
class?: string
}

/**
* Alert — a static callout box for inline messages.
* Renders a <div> with role-less alert styling; an optional leading <svg>
* is auto-positioned (absolute, top-left) and its sibling text is indented.
*
* @slot default — alert body (title, description, optional leading icon).
*/
export declare const Alert: import('vue').DefineComponent<AlertProps>
Loading
Loading