Ze'ev Waismann · Design Systems

zwaismann.com →

Functional App Design

How I design productive software: one shell, two dialects. A data/ledger dialect and a studio/material dialect, distilled from two products I build and maintain.

A design system for productive software: bookkeeping tools, studios, dashboards, ops apps, internal command centers. Re-derived from the shipped source of Even (accounting OS, the data/ledger reference) and Content Machine (multi-channel campaign studio, the studio/material reference), aligned with the industry shell popularized by Linear, Slack, Plaid, Supabase, Vercel, Claude, and Codex.

This is not for brand, portfolio, or marketing sites. Those are a different discipline (see Section 1). Functional apps may be quietly cinematic; they are never theatrical.

How to use this page: you do not read all of it. Do the 5-second dialect pick, copy the starter block, then dip into the section you need. Everything after the starter is reference.


0. Start here: pick your dialect

One system, two dialects. Ninety percent is shared (the shell, the motion contract, color-as-state, the bans). The last ten percent forks. Pick in five seconds:

Data / Ledger dialect Even-style. Light-default, hairline table, one hero number, F+C motion. Hover a row for intent-reveal actions; collapse the rail (560ms).
Home

Good morning

Cash on hand
$248,610.24
Income (Jul)+$62,140
Expenses−$41,880
Open invoices7
DatePayeeAmountStatus
07/09Baxter Rental−$3,200.00POSTED
07/08Schwartz Prod.+$18,500.00PAID
07/06Ashland Util.−$412.55PENDING
07/03Electra FW+$44,000.00OVERDUE
Studio / Material dialect Content Machine-style. Warm-dark, stage strip + work surface + inspector, color-as-state (amber = selected, sage = locked). Click a frame; hover Advance for the lift.
Source Frames Grid Lock Export Schedule
3/6

If the app has both a data side and a studio side (an ops tool with a media step), the shell and everything in Sections 3-6 stay identical; you switch page-chrome pattern per route (Section 9).

Dial Data default Studio default Why
DESIGN_VARIANCE 4 5 Structure stays stable; sections do not reinvent layout per page
MOTION_INTENSITY 3 4 Motion signals state, never entertains. Studio adds one tactile progression gesture
VISUAL_DENSITY 6 6 Linear-density ground; empty space is a separator, not gallery air

Source: Even DESIGN.md, Content Machine DESIGN.md. These are starting dials, not a cage.


1. What this is (and is not)

Kind Goal References Default tool
Functional app Usability, density, speed, clarity. Get work done. Even, Content Machine, Linear, Slack, Supabase, Vercel, Claude This doc + functional-app skill + per-project DESIGN.md
Brand / portfolio / marketing Emotion, narrative, cinematic presence. Be a window into a brand. Electra HEX, Tokyo Midnight, Allentown taste skill (landing/portfolio) or impeccable for dense marketed product; brand/motion labs

Hard rule: do not pour portfolio motion languages (GSAP hero stages, hex flood grammar, Awwwards scroll theater) into a bookkeeping or studio tool.

This is not: - A replacement for a per-product DESIGN.md. Each product still owns its tokens, voice, and domain rules; it inherits this doc and records only deltas. - A license to clone Even jade or Content Machine amber onto every app. Roles transfer; palettes stay product-specific. - Guidance for Electra HEX / film marketing. - An AI Agent Masterclass chapter. Masterclass is agent harness; this is product UI.


2. Governing principles

Six principles, stated verbatim in both apps' contracts. They apply to every new functional product unless the brief explicitly overrides them.

  1. Simplify presentation, not capability. Depth recedes; it is never removed. "Removing beats adding" means removing from view, not removing ability. (Even decisions.md:9.)
  2. Recede, never remove, never surprise. Irrelevant chrome collapses out of the way but stays one action from reach. Structure stays stable across sessions. Nothing appears or disappears on its own, except a single sanctioned AI presence. Receded routes keep working forever. (Even decisions.md:15.)
  3. Everything minimizable (the Linear feel). Every rail, panel, inspector, and section collapses and expands on demand; state is remembered. This is the shell contract, not optional polish. (Even decisions.md:19.)
  4. Color is only state, never decoration. Before painting any accent, name the state it represents. If there is no state, use ink and line tiers. "A decorative amber element is a bug: it implies active where nothing is." (Content Machine design-system/DESIGN.md:125; Even conventions.md:85.)
  5. Material (or numbers) lead; chrome frames. Data tools: money and tables are the hero, hairlines over card chrome. Studio tools: frames, GIFs, copy, schedules are the hero, chrome stays quiet around them.
  6. Intent before affordance. Secondary controls, sparklines, row actions, FAB labels stay quiet until pointer intent or focus, then arrive on the quick register (150-220ms). No permanent action walls on every row. (Even L3.)

3. The product shell (canonical layout)

The shared skeleton. Brand colors and type faces change per product; the geometry of the shell does not. No top bar: everything the top bar used to own (identity, search, account, entity switch) lives in the rail. Both Even and Content Machine deleted their top bars.

3.1 Desktop anatomy

Wordmark Entity · Search ⌘K
Folds · Collapse Account · Theme Rail · shell-owned
Page head · title + 1 actionCustomize
Content · page-owned
Optional inspector
AI

No top bar. The rail owns identity, nav, and account; each page registers a title + one primary action and ships content only. The AI presence floats (FAB ↔ Medium ↔ Full).

Even (data dialect): left AppRail 232px open / 64px slim. Two-column grid auto 1fr; Ledger AI is a single floating morphing surface (FAB to Medium to Full), not a persistent column. Rail holds wordmark, entity switcher, cmd-K ask-or-create, seven nouns, collapse button, account menu.

Content Machine (studio dialect): left AppSidebar 220px open / 64px collapsed (four workspace nouns). Campaign pipeline steps live in a horizontal CampaignChrome top strip inside the main column, not the rail. Work surface + right inspector + bottom material dock; a full-height Content Library panel sits beside the material stages.

3.2 Shell ownership rules

Owns Shell Page
Persistent left navigation Yes No
Collapse / slim state (remembered) Yes No
Wordmark / product identity Yes No
Account / entity / theme Yes No
Global search / create (cmd-K) Yes when present No
Page title + one primary action Shell renders slot; page registers Registers only
Domain content, tables, editors, stage strip No Yes
Per-page filters, modes, toolbars No Yes (inside content)

Fat shell preferred. Pages ship content only. Cohesion becomes structural, not a review discipline. Even implements this with a PageChrome React context: the page registers its title and controls, the shell renders the header.

3.3 Rail widths (shipped defaults)

Mode Even Content Machine Guidance
Expanded 232px 220px 220-240px
Collapsed / slim 64px 64px Icon strip; labels via title / sr-only
Mobile drawer 280px overlay + backdrop Overlay + backdrop Hamburger opens; route change / backdrop / Esc closes

3.4 Rail collapse state diagram

Expanded220-232px · labels
Slim64px · icons only
Mobile draweroverlay + backdrop

One permanent handle toggles Expanded ⇆ Slim (state remembered). Below ~768px the rail becomes an overlay drawer that closes on navigate, backdrop, or Esc.

3.5 Nav IA rules

  1. Seven nouns or fewer on the primary rail (Even: Home, Transactions, Invoices, Reports, Contacts, Projects, Settings. Content Machine: Campaigns, Calendar, Contacts, Library).
  2. Name the owner's objects, not accounting or engineering concepts.
  3. Configuration, automation, and rare tools live under Settings or a More fold.
  4. Seasonal or rare destinations never permanently rent rail attention (Even's estimated-taxes decision: a permanent home under Reports/Settings, never a shape-shifting rail).
  5. Active route is obvious (fill wash + optional section hue for wayfinding only).
  6. g then key navigation is welcome for power users (Even pattern).

4. Fold / collapse grammar

Adopted product-wide from Even's L4 "recede grammar." Use for rails, panels, sections, More groups, inspector tools, and the studio stage strip.

Rule Detail
Duration House comp register (Even 560ms; studio 150ms for smaller disclosures)
Easing One house curve only
Handle Permanent. Never hide the control that reopens a fold
Honest summary When folded, show a real summary (count, status, one-line lead), never a mystery chevron
Memory Persist open/closed (localStorage or account prefs)
Agency Nothing folds itself. User action only (a sanctioned AI may open its own surface)
Nesting One collapse layer only. Once a section opens, show fields directly; never disclosure-in-disclosure
Implementation Prefer grid-template-rows: 0fr -> 1fr for height animation, not layout-property thrash

One disclosure vocabulary. Both apps standardize a single DisclosureRow primitive: short uppercase title, helper line, chevron chip that rotates and brightens to the accent on open, a soft focus ring. Do not invent a second disclosure look per screen.

Studio stage strip is a two-axis fold (Content Machine CampaignChrome): (1) labels vs icons densify, auto below 1200px, user-overridable; (2) phase groups collapse when inactive to a bordered count chip, click to expand in place without changing stage. Reuse this pattern for any pipeline/stepper that outgrows its width.


5. Motion

One ease, a small set of durations, reduced-motion floor. "One ease" is the stated rule for new work. Both shipped apps still carry legacy back-compat tokens the rule explicitly deprecates; do not introduce new legacy motion, and when the codebase mixes it, the rule is the target, not the old code.

5.1 Data dialect (Even "F+C", the preferred house standard)

Token Value Use
--ease-fc cubic-bezier(0.22, 1, 0.36, 1) The only ease. Soft out, no overshoot
--d-quick 220ms Press, hover, toggle, focus, toast, rollover reveal
--d-comp 560ms Filter expand, dropdown, accordion, tabs, rail width, folds
--d-cinema 900ms Modal, drawer, route-level sheet, hero reveal
Ambient (sanctioned only) --d-breath 4800ms / alert 2600ms A single living AI presence, if the product has one

5.2 Studio dialect (Content Machine)

Token Value Use
--cm-ease cubic-bezier(0.4, 0, 0.2, 1) House calm ease
--cm-ease-out cubic-bezier(0.16, 1, 0.3, 1) Reveals
--cm-dur-fast 120ms Content-editable focus, micro
--cm-dur-ui 150ms Disclosures, hovers, selection rings
--cm-dur-advance 180ms The one progression gesture: primary button translateY(-1px) + brightness

Studio screens hard-swap (no route theater). The studio register is slightly more tactile than the data register: it earns exactly one physical progression gesture (lift + brighten) and one lock ceremony. It does not earn more.

Bans (both dialects): no second curve, no fourth data-register duration, no bounce/elastic, no animating layout properties, no opacity: 0 first-paint fades that hide content, no ambient motion except one explicitly designed presence.

5.3 Reduced motion (required)

@media (prefers-reduced-motion: reduce) {
  /* Zero (or ~0.001s) every duration token globally.
     Nothing may reintroduce motion that bypasses this. */
}

6. Color as state

Each product picks its own ground and accent. The roles stay constant; the palette does not. Before painting an accent, name the state.

Role Even (data) Content Machine (studio) Rule
App ground Tinted near-neutral, hue ~260 Warm near-black #141310 Never pure #000 / #fff
Raised surface surface ladder panel / panel-2 ladder Depth via ladder + hairlines, not shadow
Ink ramp ink -> ink-5 (5 tiers) ink -> ink-3 (3 tiers) Quiet hierarchy
Hairlines line / line-soft line / line-2 Primary separators
Brand accent jade / pos-green on the wordmark amber (also = active state) Wordmark + active + primary; under ~10% of surface
Positive / done pos green (income) sage (locked / approved; no separate green)
Negative / danger neg red clay (destructive / error only, never positive)
Warning / needs-you warn amber clay or amber, named Earned, not decorative
AI presence accent-ai mint (product-specific) Quiet signal, not chatbot chrome

Exactly three semantic state colors is a good ceiling (Content Machine: amber / sage / clay, no fourth accent, amber is also the brand hue). Data apps add money semantics (income vs loss vs pending) on top.

Section identity hues (data apps, optional): each primary nav noun may carry a wayfinding accent (Even: Home emerald, Transactions blue, Invoices violet, Reports cyan, Contacts rose, Projects olive, Settings deliberately neutral). Applied to rail active/hover and a dot beside the page title only. Never on money, never colliding with pos/neg/warn or the AI accent.

6.1 Light vs dark stance (decide this on day one)

This is the one place the two references genuinely diverge, and it is the first scaffolding decision:

Pick your stance before you pick tokens. If you support both themes, stamp data-theme on the root and reach every color through a token so dark mode never breaks (Even rule: no raw color literals in components). If you go single-surface, say so in the project DESIGN.md so no one adds a half-built toggle later.


7. Typography roles

Role Even Content Machine Guidance
UI / body Inter 14px Inter 14px Default product face, base 14px, dense
Numbers / money Inter, tabular-nums JetBrains Mono for machine values Align columns; never money in mono (Even); never mix mono/sans in one numeric column
Taxonomy labels JetBrains Mono uppercase, tracked JetBrains Mono uppercase, tracked wide Eyebrows, th, pills, wordmark. Wide tracking is what marks mono as taxonomy, not prose
Machine metadata JetBrains Mono JetBrains Mono IDs, account masks, timecode, keyboard hints
Display / identity none (Inter only) Fraunces, sparingly Route titles only; never inside compact controls

Weight discipline: 400 row data, 500 stats / totals / stat-card values, 600 reserved for the single hero amount per page (never on repeated stat cards or table cells; Even caught a real drift here). Numerics use font-variant-numeric: tabular-nums, not font-feature-settings: 'tnum'.

Money negatives: Unicode minus U+2212, not a hyphen-minus. GAAP parentheses on totals for accounting products.


8. Interaction states

8.1 Control states

Rest
Hover · lift + tint
Active · pressed
Focus-visible · 2px ring
Disabled · ~0.5, no lift
Busy · aria-busy, no double-submit
Selected · fill wash + accent

Every interactive control ships all seven. Hover and rollover arrive on the quick register (150-220ms); focus-visible mirrors hover for keyboard users.

8.2 Rollover (intent before affordance)

Surface Hidden at rest Revealed on intent
Data rows Extra actions Row action menu (portal chevron whenever 2+ actions)
Figures (Even) Sparkline + Explain popover On hover / focus of the figure
AI FAB (Even) Question label On hover when amber
Asset tiles (Content Machine) "Use" overlay On hover

Reveal on the quick register (150-220ms). Keyboard users get the same affordances on focus.

8.3 Selected vs locked (studio)

8.4 Status as machine language

Status pills are 1-2 words, uppercase mono when the product uses a mono taxonomy: PAID, OVERDUE, LOCKED, SCHEDULED, IN REVIEW. A pill is state, not a sentence. Show a given state once per row or card; do not duplicate the same status across neighboring panels.


9. Page chrome

9.1 Data list / index (Even contract, the default for data apps)

1Page head — H1 + sub left; one quiet action rightCustomize
2Stat strip — optional hairline KPIs$248,610
3CTA row — one primary+ New invoice
4Toolbar — filters / status pillsAll
5Search — row
6Table — fixed layout, colgroup, right-aligned figures, row actions on intent

The default order for a data/list page. Hairlines, not cards. Dense (~7px cell padding). One hero number, weight 600, once per page.

9.2 Studio workspace (Content Machine)

SourceFramesGridLockExportSchedule
Library
Work surface
Inspector
Optional bottom dock · shared selected material

Collapsible stage strip on top, then work-surface-left / inspector-right, material in a bottom dock. Cascade before nesting; browser-width, not centered; no card-in-card.

Cascade before nesting. Avoid nested scrollbars; prefer responsive wrapping, collapsible option cards, and full browser width. Cards are for repeated items and tools; never card-in-card, never wrap a whole page section in a card. Workspaces are browser-width; the one documented exception is a source-intake screen (centered ~840px column).


10. Empty, loading, error, confirm

State Behavior
First run One clear next action; no fake charts full of zeros pretending to be data
Loading Skeletons or calm progress on the real layout chrome; no full-page spinner thrash
Empty list Noun + one primary create / connect action
Partial AI failure Degrade gracefully; never invent numbers or confident prose without evidence (Even Morning Brief rule; Content Machine keeps AI output inspectable, provenance visible)
Destructive confirm In-app confirm dialog only (Even useConfirm()); window.confirm is banned
Busy actions aria-busy, block double-submit, honest label ("Saving...")

11. Responsive behavior

11.1 Breakpoint bands

Band Width Shell behavior
Desktop >= ~1024px Full rail + multi-column workspaces
Tablet ~720-1023px Rail may slim or overlay; secondary panels stack
Mobile < ~768px Rail becomes overlay drawer (280px); stat grids 2-up; tables scroll-x preserving fixed columns; AI becomes a full sheet

Content Machine named breakpoints: drawer 1000 (library becomes overlay), fold 1180 (secondary panels stack), wide 1360 (grid opens three composer zones).

11.2 Mobile non-negotiables

Reality note: both reference apps treat mobile as inherited, not freshly redesigned each pass. Meet the non-negotiables; you do not owe a bespoke mobile redesign on v1.


12. Favicon, icons, and Open Graph

Every new functional app ships these on day one of any public surface. Missing tab icons and link previews are a recurring regression, and the two reference apps each ship a partial version, so treat this as the target, not something to copy verbatim from Even or Content Machine.

12.1 Checklist

Asset Spec Notes
Favicon SVG or 16/32/192/512 PNG Monogram / mark that reads at 16px. In Next.js under a basePath, use the app/icon.svg file convention, not a hardcoded /icon.svg that 404s
Apple touch icon 180x180 PNG apple-icon or explicit metadata
Title template "%s · Product" Default product name when no page title
Description One calm sentence Not marketing spam
Open Graph image 1200x630 Product card. Generate with next/og ImageResponse
Twitter card summary_large_image Match OG
metadataBase Absolute product URL Required for correct OG URLs in Next.js

12.2 OG recipe

Product.
One-line calm product promise.
domain.tld1200 × 630

Product ground, product ink, brand accent on the mark only. No screenshot spam, no six feature bullets, no gradient mesh. Keep the card as quiet as the product.

Keep OG as quiet as the product. No screenshot spam, no six feature bullets, no gradient mesh. Even's card: Even. wordmark with a jade period, "Accounting without theater.", evenlabs.ai. Do not leak entity branding into product link previews (icons and OG should carry the product mark, not a client / entity brand).

13. Copy voice


14. Universal bans (never ship)

Anti-slop (shared with brand systems): pure #000 / #fff as large fields; gradient text; glassmorphism as a default surface treatment; colored left-border accent stripes as decoration; the huge-number + tiny-label hero cliche repeated as a grid; identical icon-card feature walls; em dashes; emoji in product UI.

Functional-specific: color used as decoration; card chrome as the default table treatment (prefer hairlines for ledgers); nested cards / nested disclosures; new motion curves or bounce; ambient animation everywhere (one living presence max, if any); pages re-implementing the whole shell; nav that lists every engine concept; window.confirm; raw color literals in components (tokens only); shipping without favicon + OG on public routes; a half-built theme toggle in a single-surface studio app.


15. Bootstrap + copy-paste starter

Do this before inventing screens.

  1. Pick your dialect (Section 0) and write a one-line north star (Even: "accounting without theater"; Content Machine: "material first, state as color").
  2. Decide light/dark stance (Section 6.1).
  3. Drop in the token starter below, fill the ground / accent / semantic values.
  4. Pick type roles (UI face, mono taxonomy, optional display face); self-host fonts.
  5. Scaffold the shell: root ground wrapper, left rail (expanded / slim / mobile drawer), main content outlet, page-chrome registration.
  6. Define 5-7 primary nav nouns. Everything else goes to Settings or a fold.
  7. Install motion tokens + reduced-motion zero-out.
  8. Ship favicon + apple icon + OG + metadataBase.
  9. Define empty / loading / error + a confirm dialog primitive (never window.confirm).
  10. Write the project DESIGN.md that inherits this doc and records only product-specific deltas.

15.1 Token starter (tokens.css)

/* Fill the four bracketed groups; keep the role names. Reach every color
   through these tokens - no raw literals in components (breaks theming). */
:root {
  /* --- ground + ink (never pure #000/#fff) --- */
  --bg:      /* data: tinted near-white or hue-260 near-black; studio: warm near-black e.g. #141310 */;
  --surface: /* one step raised */;
  --surface-2:/* two steps raised */;
  --ink:     /* primary text */;
  --ink-2:   /* strong secondary */;
  --ink-3:   /* labels / secondary */;
  --line:    /* primary hairline */;
  --line-soft:/* faint hairline */;

  /* --- accent + semantics (each = a state, not decoration) --- */
  --brand:   /* wordmark + active nav + one primary action family; under ~10% of surface */;
  --pos:     /* income / done / approved */;
  --neg:     /* loss / overdue / destructive only */;
  --warn:    /* pending / needs-you (earned) */;
  --accent-ai:/* one quiet AI signal, if the product has AI */;

  /* --- motion (one ease; do not add a second) --- */
  --ease:      cubic-bezier(0.22, 1, 0.36, 1);   /* data F+C; studio: cubic-bezier(0.4,0,0.2,1) */
  --d-quick:   220ms;   /* press, hover, focus, rollover  (studio: 150ms) */
  --d-comp:    560ms;   /* folds, dropdowns, rail width    (studio: 180ms) */
  --d-cinema:  900ms;   /* modal, drawer, full sheet       (studio: hard-swap, omit) */
}
@media (prefers-reduced-motion: reduce) {
  :root { --d-quick: 0.001ms; --d-comp: 0.001ms; --d-cinema: 0.001ms; }
}

15.2 Shell skeleton (React, framework-agnostic intent)

// Rail owns identity + nav + account. Page ships content only.
<div className="app-root">        {/* fixed inset-0, overflow hidden, bg=var(--bg) */}
  <aside className={rail(slim)}>   {/* 220-232px open / 64px slim; sticky, border-right hairline */}
    <Wordmark />                   {/* product mark, quiet */}
    <EntitySwitcher />             {/* if multi-entity */}
    <AskOrCreate />                {/* cmd-K search + quick create, if present */}
    <Nav nouns={FIVE_TO_SEVEN} />  {/* owner's nouns; active = fill wash (+ optional section hue) */}
    <Fold label="More" />          {/* rare destinations; permanent handle */}
    <CollapseButton />             {/* toggles slim; persist to localStorage */}
    <AccountMenu />                {/* pinned to foot: theme toggle, sign out */}
  </aside>

  <main className="main-col">
    <PageChrome />                 {/* shell renders the header slot; each page REGISTERS title + 1 action */}
    <Outlet />                     {/* data: list/table  |  studio: stage strip + work surface + inspector */}
  </main>

  {hasAI && <FloatingAI />}        {/* FAB <-> Medium <-> Full morph; the ONLY ambient motion */}
</div>
  1. Optional: extract a small *-design-system package once patterns stabilize; do not block v1 on it, and verify against the shipped app, not a stale package.

15.3 Per-project DESIGN.md template

Every product gets a DESIGN.md that inherits this doctrine and records only its deltas. Copy this skeleton on day one; fill the brackets. Even and Content Machine both follow this shape.

# <Product> - DESIGN.md
> Inherits the functional-app doctrine (this doctrine).
> Records ONLY product-specific deltas. When this and the doctrine disagree, this wins
> for this product; if a pattern should be house law, promote it to the doctrine instead.

North star:  <one line, e.g. "accounting without theater">
Dialect:     data | studio            (doctrine Section 0)
Theme:       light-default multi-theme | single warm-dark, no toggle   (Section 6.1)
Dials:       VARIANCE / MOTION / DENSITY    e.g. 4 / 3 / 6

## Tokens (deltas from the starter in Section 15.1)
Ground:      <bg + surface ladder>
Ink:         <ramp, how many tiers>
Brand:       <accent - and the ONE state/role it names>
Semantic:    pos / neg / warn  (+ studio: sage / clay)   - the STATE each color means

## Type
UI:          <face + base size>
Taxonomy:    <mono face>
Display:     <optional; route titles only>

## Shell
Rail nouns:  <5-7 owner nouns>
Recede:      <what lives under Settings / More fold, never deleted>
AI:          none | <one floating presence + what it does>

## Motion
<inherit data F+C or studio register; note any product-specific token>

## Domain rules (product-specific, NOT in the doctrine)
- <e.g. money = integer cents, GAAP parens; bank-vs-books; lock ceremony; timecode>

## Bans beyond the universal list
- <product-specific>

15.4 Implementation defaults (how we wire it)

The doctrine is design; this is the plumbing an agent should not have to re-derive. Lean on purpose - it names defaults, it is not a framework manual. Override per project brief.

Concern Default
Framework Next.js (App Router) + React + TypeScript
Styling Tailwind for utility layout; design tokens as CSS custom properties on :root / [data-theme], reached via var(--...). No raw color literals in components (breaks theming)
Package manager pnpm if the repo uses it, else bun. Never npm / yarn
Fonts Self-host via next/font (or @font-face + font-display: swap). Never a Google Fonts <link> in production
State persistence Rail slim/expanded, fold open/closed, Customize prefs -> localStorage, keyed per product (e.g. even-rail); account prefs when signed in. Restore on load
Icons / OG app/icon.* + app/opengraph-image.tsx file conventions (basePath-safe); metadataBase absolute URL; "%s · Product" title template
Confirm An in-app useConfirm() / ConfirmDialog primitive. window.confirm is banned
Deploy Vercel or similar; keep secrets in a manager, never in the repo
Verify Run the feature at the user's layer before calling it done; do not trust a green build alone
DS package Extract *-design-system only once patterns stabilize; the shipped app stays source of truth (packages lag, see Section 16.2)

17. Diagram pack (copy into plans)

Shell layers

Layer 0tokens + root ground wrapper
Layer 1app shell (rail + main + optional floating AI)
Layer 2page chrome registration (title, one primary control)
Layer 3domain content (tables, studio workspace, forms)
Layer 4overlays (dialogs, drawers, command palette, AI window)

Information hierarchy - data page

1One hero number — optional, weight 600, once per page
2Quiet stat strip
3Primary action (“+ New <noun>”)
4Filters + search
5Dense hairline table
6Row actions on intent

Information hierarchy - studio page

1Stage / progress — which step am I on (the collapsible strip)
2Material — frame, post, mailer (the hero)
3Inspector tools (one disclosure layer)
4One primary: advance / lock / schedule

Functional App Design · a working doctrine, kept current. Ze'ev Waismann