docs(product): commit Veect product spec bundle under docs/product/

Sandcastle implementers run in git worktrees that contain only committed
files, so every PRD referencing the spec was unreadable to dispatch
agents while the bundle sat untracked in .proto/. The eight spec
documents are copied verbatim (filenames preserved) from
.proto/veect-product-docs/ and become the canonical source.

The stray veect-product-docs.zip at the repo root was already removed
before this change — that half of the task is a verified no-op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
2026-07-12 12:58:47 +02:00
parent 99180887f3
commit 4d1d53432e
8 changed files with 1498 additions and 0 deletions

View File

@@ -0,0 +1,267 @@
# Veect — Product & Design Specification
_v1.0 · July 2026 · covers `Veect.dc.html` (interactive prototype), `Veect Landing.dc.html`, and `veect-codebase/` (React 18 · TypeScript strict · Tailwind · shadcn-style)_
_[Stored verbatim as project ground truth for the current design/prototype — founder-provided 2026-07-10. Companion: `veect-ui-gap-spec.md` for what is NOT yet in this design.]_
---
## 1. Product
**Veect is a design-system-native canvas.** A designer brings their own tokens and
components; Veect composes screens from _only_ that system and exports production React
that imports their real library. The three invariants everything hangs on:
1. **One runtime** — canvas, preview, isolation and export render the same tree through
the same renderer. No "roughly how it'll look."
2. **0 unregistered elements** — AI output is whitelist-sanitized against the component
registry; anything outside it is refused, never faked.
3. **Deterministic code** — same tree → same code, every time. Export imports the
customer's library (`@solstice/ui` in the demo), never inlined clones.
Demo fiction: the customer is **Solstice** (warm team-scheduling brand), with designer
Maya (owner) and design engineer Devon (reviewer, owns the merge veto).
---
## 2. Brand & design language
### 2.1 Logo
Typographic wordmark: lowercase **`veect`**, Instrument Sans 600, letter-spacing 0.035em,
line-height 1, closed by a **square ink terminal** (the "vector point") sized ≈ 0.28 × font
size, baseline-aligned, `currentColor`. No pictorial mark. Codebase atom: `Logo size={n}`.
### 2.2 Color — chrome tokens
Color belongs to the customer's canvas; the instrument is monochrome. The accent IS the ink.
| Token | Light (default file value) | Dark |
|---|---|---|
| `--bg` / `--panel` | `#FCFCFB` | `#0E0E0D` |
| `--raised` / `--chip` | `#FFFFFF` / `#F4F3F0` | `#161615` |
| `--line` / `--line2` | `#E7E6E2` / `#CFCEC8` | `#262624` / `#3A3A37` |
| `--t1` / `--t2` / `--t3` | `#161513` / `#63615B` / `#9A978F` | `#F4F4F2` / `#A6A6A1` / `#73736E` |
| `--iris` (accent = ink) | `#161513` | `#F4F4F2` |
| `--iris-dim` | `rgba(22,21,19,.06)` | `rgba(244,244,242,.09)` |
| `--btn-ink` (text on accent) | `#FFFFFF` | `#0E0E0D` |
| `--warn` / `--err` / `--live` | `#9A7B3F` / `#B05E5E` / `#5B7F5E` | `#C2A36B` / `#C97070` / `#8BA88E` |
Amber (`--warn`) is reserved for AI-scope affordances (pick outline, scope chips, draw
strokes) and the unmapped-component warning. The prototype ships **dark by default**
(user preference); the codebase mirrors this (`index.html data-theme="dark"`).
### 2.3 Surfaces & elevation
Flat. Radius **2px everywhere** in chrome (customer canvas keeps its own `radius-md`
token). No floating shadows — separation is a hairline: popovers/menus use
`box-shadow: 0 0 0 1px var(--line2)`. Canvas frames sit on the board with a 1px
`rgba(0,0,0,.14)` ring.
### 2.4 Typography
- **UI**: Instrument Sans 400/500/600 — 13px base, 1112.5px controls.
- **Labels**: Fragment Mono, 911px, uppercase, letter-spacing .06.14em — all section
headers, metadata, and "engraved" captions.
- **Customer content**: the system's own font (demo: Bricolage Grotesque) — never used
for chrome.
- Scale discipline: chrome text never below 8px (activity-bar labels), canvas display
type responsive per view (§6).
### 2.5 Iconography
Hand-drawn 16-grid SVG, 1.5px stroke, round caps, `currentColor`. Set: chat (spark),
layers, library, tokens, history, inspect (sliders), polish (spark-4), code (chevrons),
pick (crosshair), draw (pen), eye / eye-off, mic. No icon fonts, no emoji.
### 2.6 Motion
120450ms, `cubic-bezier(.2,.7,.2,1)`. Keyframes: `riseIn`, `popIn`, `fadeIn`,
`lineFlash` (code sync), `nodePulse` (token ripple). Everything respects
`prefers-reduced-motion`.
### 2.7 Voice
Plainspoken, no hype, no emoji. Refusals teach ("You don't have a component for this
yet"), fixes explain the _why_ in the user's own tokens.
---
## 3. Screens
| Screen | Purpose | Key elements |
| ----------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Sign in** | design-partner entry | email → straight through; SSO stubs; provisioning note |
| **Home** | projects + systems | project grid (1 live + placeholders), search, Solstice system card (ramp, open tokens, re-import), "bring a design system" |
| **Onboarding** | the re-theme reveal | two paths: paste tokens (parse → contrast checks) or 5-token quick path (brand, neutrals, radius, type, spacing) with live mini-kit; ends "Bring it to life →" |
| **Editor** | the canvas (§45) | |
| **Settings** | system + governance | token source card, component mappings ("merge bar"), craft standard picker, export defaults, plan, delete |
| **Profile** | account | identity card, theme preference, workspace members (Maya/Devon), keyboard map, sign out |
| **System update** | US-6 re-import | paste → diff (CHANGED/ADDED/REMOVED + swatches) → impact line → apply-with-undo → ripple |
| **Landing** | marketing (§10) | |
Navigation: project dropdown (top-left) is the hub — all projects, new project, export,
copy TSX, import tokens, upload component, settings, profile, onboarding demo. Escape and
outside-click dismiss. Avatar → Profile everywhere.
---
## 4. Editor anatomy
### 4.1 Top bar (36px)
Wordmark · project dropdown · `✓ saved` · **view switcher** (centered: Desktop / Tablet /
Mobile / ⧉ All) · Preview · **Export** (primary) · theme toggle ◐ · avatar.
### 4.2 Activity bar (44px, fixed left)
Toggleable icon+label buttons, grouped by dividers:
`✦ Chat``Layers · Library · Tokens · History``Inspect · Polish``Code`.
Click = open that panel/tab; click again = collapse. Active = `--iris-dim` chip. At
overlay widths (<1000px) panels are mutually exclusive and float over the board.
### 4.3 Panels (resizable via edge-drag; « » collapse chevrons)
- **Chat** (284px) vertical AI thread 7): bubbles, proposal Accept/Discard inline,
refusal actions, model picker (haiku/sonnet), image attachments, scope chip.
- **Board rail** (216px) LAYERS: collapsible tree (caret-expand, double-click reveal),
per-frame **variant chips** 6); LIBRARY: 10-component base kit with mapping badges,
isolation ◉, add +, drag-to-canvas, patterns, custom `.tsx/.jsx` upload; TOKENS: brand
ramp + curated swatches + hex, radius stepper, type picker, neutrals, spacing base
every change ripples live (`nodePulse`); HISTORY: labelled steps, restore cursor 8).
- **Inspector** (244px) selection card, Edit-with-AI (scoped), content/variant/weight/
tone/size controls, gap/pad steppers (system-scale steps), Tailwind utilities
(collapsible; w/h/max-w/shadow/opacity selects + custom string), token bindings,
FRAME settings (name, width ±80, **active view** line). Polish tab: §7.3.
- **Code** (380px, dark `#161311` always) file segs (`{Frame}.tsx` / `tokens.css`),
line-numbered TSX with click-lineselect-node sync and `lineFlash` on change; **edit
mode** (toggle syntax-highlighted textarea apply parses back via the generated
grammar, line-referenced errors); footer `prettier ✓ tsc ✓`.
- **Isolation** (over board) variant × state grid per component on a paper stage, real
runtime, Esc exits.
### 4.4 Board
Infinite surface: scroll pans, ⌘/ctrl-scroll zooms at cursor, two-finger pinch, drag
empty board pans, background click deselects. Zoom cluster ( % + · fit · + frame ·
board backdrop toggle). Frames render as **view clusters** 6) with a draggable name
tab (`Home · 34 nodes · one codebase`). Selection = ink ring; hover = neutral ring;
AI-target = amber ring. Tool pill (bottom-center): **Pick (V) · Draw (D) · Overlays (O)
· Voice (stub)** icon buttons with key hints.
### 4.5 Preview
Full-screen overlay, real hover/focus states, active view's width fills the screen
(mobile/tablet render as a centered device sheet).
### 4.6 Keyboard
`⌘K` commands · `✦` focus composer · `⌘Z/⇧⌘Z` undo/redo · `⌘0` fit · `⌘1` 100% ·
`⌘±` zoom · `V/D/O` tools · `⌫` delete selection · `Esc` dismiss cascade (menu modes
refusal proposal scope selection).
---
## 5. Canvas node model
`board → frame[] → (stack | card | heading | text | button | badge | input | avatar |
image | divider | custom)*`. Layout via `dir/gap/pad/padX/padY/align/justify/maxW/bg`
(4px scale). Content props per type; `tw` bag for curated utility classes. Frames carry
`x/y/w/views/view`. IDs are stable; comment anchors and code-line mapping key off them.
---
## 6. Responsive variants — one tree, three widths
- Views: **desktop** (frame's own `w`, default 1280) · **tablet 768** · **mobile 390**.
- A frame renders every enabled view side by side (120px gap) same children, so an
edit anywhere updates every view. Captions (`tablet · 768`) label each block; the
**active view** (drives preview, code, export width) is ink-highlighted.
- Renderer responds to view width: row-stacks wrap 900, cards get flex-basis 240,
display type steps down (xl: 54 44 34px) 900/≤480.
- Switcher semantics: Desktop/Tablet/Mobile = show only that view + make it active;
** All** = all three. Layer chips: label click = select frame + make active (opens
frame settings); eye icon = show/hide that view (min 1). Cluster growth shifts
right-neighbors by the width delta clusters never collide.
---
## 7. AI, constrained
### 7.1 Compose
Prompt system prompt embeds the registry JSON (types + allowed props) + brand voice
rules (one primary CTA per group, 4px spacing, no lorem). Response is sanitized: unknown
type **refusal**, numeric props clamped to scale, strings capped. Result stages as a
**dashed ghost** on the active frame Accept commits (history label `AI compose —
accepted`), Discard drops.
### 7.2 Scoped edits
Pick tool (V) or Inspector "✦ Edit with AI": amber target ring + composer scope chip
(`h1 · Scheduling…`), inline bubble at cursor, type-aware quick actions (punchier /
shorter / demote / colorize ×n…). Edit proposals ghost beside the original and swap in
on Accept.
### 7.3 Polish (the Impeccable pass)
Ranked issues (HIGH/MED/LOW): weak hierarchy (display heading at 400), competing primary
CTAs, off-scale gaps (`gap-[18px]` drift), uneven sibling padding, AA contrast failures
(with measured ratios). Each: title _why it matters_ one-click fix **in the user's
tokens**. Fix-all; empty state "Impeccable. Nothing to fix." Craft standard selectable
in Settings (Veect default / Impeccable / custom DESIGN.md).
### 7.4 Refusal protocol
Off-registry ask (3D globe, video, charts…) "You don't have a component for this yet"
- reason + **Compose from primitives** / Not now. Never generic markup. Models: Haiku 4.5
default, Sonnet 4.5 option; offline heuristics keep demos deterministic.
---
## 8. History
Every mutation is a labelled step (`Add Button`, `Polish — Two primary actions`,
`View → tablet`, `AI edit — accepted`…). History tab lists steps newest-first with age.
Clicking restores that state and sets a **cursor**: newer steps dim (35%); the next
meaningful edit truncates the dimmed trail. `⌘Z` walks steps; redo restores.
---
## 9. Code generation & round-trip
| Canvas | Export | Notes |
| ------------- | ------------------------------------- | --------------------------------------- |
| heading/text | `<h1h4>/<p>` + Tailwind | size/weight/tone classes, neutral ramp |
| button | `<Button kind size>` | `variant→kind` rename shown in mappings |
| card | `<Card>` | pad/gap classes |
| input | `<TextField label placeholder />` | |
| avatar | `<Avatar name />` | |
| badge | `@veect/base-kit` fallback + TODO | until mapped "map now" chip |
| stack | `<div>/<nav>` flex classes | |
| image/divider | semantic HTML | |
| custom upload | real `import X from "./components/…"` | props sniffed from source |
`tokens.css` generates from the live system. The **edit-mode parser** accepts exactly
this grammar back (line-referenced errors otherwise) the grammar runs both ways.
Export dialog: honest file tree, `npm i && npm run dev`, copy `{Frame}.tsx`, fidelity
badge. Guarantee line: _deterministic — same tree, same code_.
---
## 10. Landing page
Carbon-mono flat (same language). Sections: fixed nav (scroll-spy underline, blur on
scroll) · Hero ("Your system in. Real React out.", masked line reveal, magnetic CTAs,
live 5-token kit demo with 3D tilt) · proof strip (0 unregistered / <10% LOC / <20 min)
· three-pillar wedge · graceful-refusal chat (typewriter) · code panel (staggered lines)
· Polish demo (run/undo on a Solstice card) · CTA. Left ruler tracks scroll. GSAP +
ScrollTrigger, all reduced-motion-safe.
---
## 11. Codebase map & seams
See `veect-codebase/README.md` for the atomic-design map (config / engine / store /
hooks / ui / atoms / molecules / organisms / templates / pages), product invariants,
and **Handoff notes** the deliberate seams: model bridge (`engine/ai.ts#complete`),
export zip, placeholder projects, auth, persistence. Board substrate: React Flow
(`@xyflow/react`, MIT) frames as custom nodes, zustand as single source of truth.
Pre-flight: `npm run typecheck && npm run lint`.