Infrastructure for how the built world gets made.
The Atelier Design System governs every pixel of app.atelier.inc — the platform connecting clients, project teams, and factories across custom fabrication. This book documents what ships in production, and sets the standard for everything that ships next.
Why this book exists
Atelier sits between design intent and fabrication reality. Our interface has to carry contract-grade information — budgets, shop drawings, production stages, freight — with the calm confidence of a luxury product and the precision of an engineering document. That tension defines everything in this system.
One system, three portals. Admin, client, and factory experiences share a single design language. Firewalls strip data, never design — a factory sees fewer numbers than an admin, but the register, the status spine, and the type scale are identical. The admin production register is the standard every portal inherits.
Documented, not imagined. Every value in this book is cross-checked against production code — client-shell.css, button.css, ItemStatusBar.module.css, DropZone.tsx, globals.css. Where the platform had drift, this book codifies the approved standard and marks the source.
Category-defining, quietly. We don't decorate. Warm near-white surfaces, a single disciplined accent, mono microtype for machine-truth (IDs, stages, dates), and generous whitespace. The result should read like infrastructure: inevitable, exact, expensive to imitate.
Logo & Wordmark
One mark: the ATELIER wordmark in accent #F1613A. It is artwork, not text — always placed from the master file, never retyped, never restyled. The same file anchors the marketing site nav, the platform, and this book's rail.
Primary wordmark
On White / Paper
On Dark #14120EClearspace
Clearspace is X = the wordmark's cap height on all four sides. Nothing enters the zone — no type, no chips, no hairlines. In dense chrome (rail, mobile top bar) the inset never drops below 0.75X.
Minimum size & placements
| Rule | Spec |
|---|---|
| Minimum width | 96px digital · 25mm print. Below this the counters close — use the A-monogram (favicon crop) instead. |
| Clearspace | 1× cap height (X) all sides · ≥0.75X in dense chrome |
| Color | Accent #F1613A on Paper / White / Dark. One-color fallback: Ink #171512 (print only). Never white, never tinted. |
| Production placements | Marketing nav 118×14 · footer 104×12 · book rail 104×13 · cover 136×17 |
| Favicon / monogram | The "A" cropped from the master artwork, centered on transparent — assets/favicon.png 128×128 |
| Master file | atelier-logo.png · 946×118 RGBA · atelier-platform/public/atelier-logo.png |
Do & Don't


Typography
One family, two voices. Roobert carries everything human — headings, body, data. Roobert Mono carries everything machine — eyebrows, labels, IDs, meta. Mono is never body copy. Since 2026-08-04, all mono text ships with zero letter-spacing and small mono is always UPPERCASE.
Type ramp
Display sizes use weight 300, line-height 1.0, tracking −0.015em. Body is 16px / 300 / 1.6. Bold within body renders Medium (500), never 700.
lh 1.0 · ls −0.015em
lh 1.05 · ls −0.015em
lh 1.15 · ls −0.01em
lh 1.2 · ls −0.005em
strong = 500
var(--at-card-text)
UPPERCASE · ls 0
UPPERCASE · ls 0
UPPERCASE · ls 0
Weights & files
| File | Maps to weights | Role |
|---|---|---|
Roobert-Regular.woff2 | 300–400 | Body, display headings, data — the default voice |
Roobert-Medium.woff2 | 500–700 | Emphasis (b/strong), row titles, current-stage labels |
RoobertMono-Regular.woff2 | 300–400 | All mono labels — Regular is the working weight |
RoobertMono-Medium.woff2 | 500–700 | Available; unused by default |
Self-hosted woff2, font-display: swap, loaded in globals.css so every route renders Roobert. ⚠️ Roobert is a paid face (Displaay); webfont license must be secured for long-term production.
Tracking & casing rules
| Context | Tracking | Case | Rule |
|---|---|---|---|
| Display / headings | −0.015em | Sentence case | Tight, editorial. Never uppercase display type. |
| Body | 0 | Sentence case | 16px floor for reading text. |
| All mono (labels, chips, meta, buttons) | 0 | UPPERCASE | 🔴 Kerning removed platform-wide 2026-08-04 — supersedes the old 0.1–0.12em convention. Do not reintroduce tracking on mono. |
| Names, emails, filenames, user data | 0 | As entered | Never transformed. IDs/codes (SKU, RFI-###) stay as-is. |
| Marketing wordmark / homepage buttons | 0.14–0.28em | UPPERCASE | Homepage (atelier.inc) only — not platform UI. |
Do & Don't
Mono for the eyebrow, Roobert for the title and copy.
Shop drawings for the reception desk are ready for review and approval before production begins.
Color
Warm near-white paper, deep warm ink, one orange accent. Color is rationed: the accent marks the primary action and live production state; status hues are muted, tinted, and always paired with text. Bone backgrounds are banned from the platform.
Core palette
Accent
Rationing rule: one accent-filled primary action per view. Everything else is secondary, quiet, or ghost. The accent also owns "in production" state and the current-stage marker — never decorative.
Status palette
Each status hue has a tint + line pair for chips: green #EAF3EC / #CFE4D4, amber #FAF3E3 / #EEE0BD, red rgba(192,57,43,.07) / .25, neutral #F1F0EE / #E3E1DD. Status text is never bare — always chip, tag-with-dot, or spine bar.
Banned: the bone family
⛔ Bone backgrounds belong to the marketing homepage era. Platform UI surfaces are #FAFAF8 and white — nothing else. Legacy --cream/--bone variables still exist in client-shell.css for old sub-pages; do not use them in new work.
Contrast (WCAG 2.1)
| Pair | Ratio | AA Normal (4.5:1) | AA Large / UI (3:1) |
|---|---|---|---|
| Ink #171512 on Paper #FAFAF8 | ~16.9:1 | Pass (AAA) | Pass |
| Ink-2 #3A362F on White | ~11.9:1 | Pass (AAA) | Pass |
| Muted #757575 on White | ~4.6:1 | Pass | Pass |
| Muted #757575 on Paper | ~4.4:1 | Borderline — use ≥12px or Muted-2 | Pass |
| Muted-2 #6D675F on Paper | ~5.3:1 | Pass | Pass |
| White on Accent #F1613A | ~3.2:1 | Fail | Pass |
| White on Accent Dark #D94E2A | ~4.1:1 | Large text only | Pass |
| Accent Dark #D94E2A on White | ~4.1:1 | Large text / links with context | Pass |
| Green #3D7A4E on tint #EAF3EC | ~4.9:1 | Pass | Pass |
| Red #C0392B on White | ~5.9:1 | Pass | Pass |
| White on Dark #14120E | ~18.4:1 | Pass (AAA) | Pass |
Known exception: accent-filled button labels are uppercase mono ~11px on #F1613A (3.2:1). They pass the 3:1 UI-component bar; treat the label as part of a 44px component, and never set paragraphs in white-on-accent. Prefer accent-dark when accent is used as text.
Spacing & Layout
A 4/8px rhythm, fluid gutters, and a small closed radius scale. Density is earned: registers run tight (10–18px paddings), reading surfaces breathe (24–64px). Nothing is ever fully round.
Spacing scale
| Token | Value | Typical use |
|---|---|---|
--at-s-1 … --at-s-2 | 4 / 8px | Chip gaps, icon-to-label, label-to-value |
--at-s-3 … --at-s-4 | 12 / 16px | Row padding, form gaps, card grids on mobile |
--at-s-5 … --at-s-6 | 20 / 24px | Card padding (18–26px band), register cells |
--at-s-8 … --at-s-16 | 32–64px | Section spacing, hero padding, page rhythm |
--at-gutter | clamp(22px, 4vw, 64px) | Page-level horizontal gutters (journey rows) |
Radius scale — rectangular, never pill
bars · strips
chips · tracks
buttons · inputs
menus
cards · modals
never
| Step | Value | Applies to |
|---|---|---|
| Micro | 2px | Dense-row status tracks, progress bars, spine strips |
| Chip | 4px | Status chips (.achip), detail bar track, count badges (3–4px band) |
| Control | 6px | Buttons, inputs, selects, toggles, dropzone, thumbnails, kebab |
| Menu | 8px | Dropdown menus, popovers, toasts |
| Card | 10px | Cards, panels, modals, demo surfaces (legacy 14px --r-card exists on older client cards; new work standardizes on 10px) |
⛔ No pills, capsules, or stadium shapes — ever. Not on buttons, not on chips, not on bars. Segmented bars use 0 radius inside, small radius on outer ends only.
Source: button.css (6px) · achip (4px) · pis-menu (8px) · --r: 10px · statusbar densities (2–4px)Grid & breakpoints
| Breakpoint | Value | What changes |
|---|---|---|
| Wide | ≤1360px | Register/spine labels tighten (admin 9-phase bar) |
| Tablet | ≤900px | Register drops qty/cost columns; spine labels shrink; rail may collapse |
| Mobile nav | ≤767px | Desktop nav hides; fixed bottom MobileNav appears; body gains clearance padding |
| Narrow | ≤640px | Status bars keep first/last/current labels only; single-pane messaging |
| Design floor | 390px | Everything must work at 390×844 (iPhone 12–14) with safe-area insets |
Safe-area rule (2026-08-05): on mobile the body carries padding-bottom: calc(3.5rem + 12px + env(safe-area-inset-bottom)) so content never sits under the fixed bottom nav or the iOS home indicator.
Elevation
| Level | Shadow | Use |
|---|---|---|
| Flat | none — 1px hairline border | Default. Cards, registers, inputs sit on borders, not shadows. |
| Menu | 0 14px 34px -18px rgba(23,21,18,.35) | Dropdowns, popovers |
| Icon chip | 0 2px 7px -2px rgba(217,78,42,.55) | Orange gradient card-header icon tiles |
| Modal | 0 30px 80px -30px rgba(20,18,14,.5) | Dialogs over glass overlay |
Buttons
One closed set — six variants, three sizes — defined in src/components/ui/Button.tsx + button.css (standard, 2026-08-04). Rectangular 6px radius, Roobert Mono UPPERCASE labels, zero tracking. The accent fill is reserved for the primary action of a view. No hand-rolled buttons, ever.
Variants
| Variant | Recipe | When |
|---|---|---|
| Primary | #F1613A fill · white label · hover #D94E2A | THE action of the view (Save, Send, Invite). Max one per view. |
| Secondary | White · 1px ink border · hover #F4F3F1 | Edit, Export, Cancel — real actions that aren't the headline |
| Quiet | Borderless accent link · optional trailing → | Navigational "View all" conventions |
| Ghost | Transparent · ink-muted · hover ink tint | Inline row actions (Resend, View Portal) |
| Destructive | Red outline rgba(192,57,43,.4) · red text | Row-level danger (Remove, Revoke) |
| Danger | #C0392B fill · white · hover #A93226 | Confirm-modal commits only (Delete Project) |
Sizes
States
Do & Don't
rounded-full, no stadium shapes.Forms
Quiet fields on white: 1px warm-gray borders, 6px radius, mono uppercase labels above. Focus is an accent border plus soft tint ring. Errors speak twice — red border on the field, inline red banner for the message (invite-flow pattern).
Text inputs
| Property | Value |
|---|---|
| Field | White bg · 1px #DCD9D3 · radius 6px · padding 10×12 · Roobert 300 0.9rem |
| Label | Roobert Mono 10px UPPERCASE · #6D675F · 7px below-gap |
| Placeholder | #B4AEA2 |
| Focus | Border #F1613A + ring 0 0 0 3px rgba(241,97,58,.12) |
| Error | Border #C0392B + mono helper or inline banner (tint rgba(192,57,43,.07), 6px radius) |
| Helper | Roobert Mono 9px UPPERCASE #B4AEA2 · success #3D7A4E |
Search · Select · Textarea
Toggles · Radios · Checkboxes · Sliders
File upload — DropZone canon
The ONE shared upload shell (src/components/ui/DropZone.tsx, used on ~17 surfaces). Never the native "Choose File" chrome. Full-width dashed 1px panel, centered outline glyph, helper + accent mono BROWSE FILES, gray mono hint. Accent border + 4% tint on dragover only.
Cards & Content
White surfaces on paper, hairline borders, 10px radius, no default shadows. Hero areas are editorial: full-bleed imagery with glass overlays — never white boxes with text inside. Registers are the platform's signature content pattern.
Project card
Production register — the standard
The admin production register is the canonical list pattern for every portal. Item photo thumbnails ALWAYS (via image-URL endpoint, never base64), type chips with status dots, and the 5-stage spine (Draft → Shop Dwg → Approved → Production → Shipped) with 10px labels. Firewalls strip columns (client hides cost; factory hides client identity) — never the design.
Row anatomy: 3px left status spine bar (draft #CFC9BC / production #F1613A / shipped #6E9B6F) · 56px thumb, 6px radius · grid 56px / 1.6fr / 1fr / 2.6fr / 40px, 20px gaps · row hover rgba(23,21,18,.02) · after ~5 rows the list scrolls.
Empty states · Skeletons · Timeline
Empty states: icon tile + one-line title + ≤34ch subcopy + a single primary action. Skeletons shimmer at 1.4s on #EFEDE9→#F7F5F1. Timeline dots: hollow past, accent current.
Icons & Visual Language
Outline icons, 1.4–1.6px stroke, round caps, 24px grid. Card-title icons render in accent orange; overview card headers use the approved orange-gradient "cube" chip. Chips and tags are rectangular; avatars are the circle exception.
Stroke & sizes
Avatars
Badges · Chips · Tags — always rectangular
Motion
Motion is quiet and purposeful: fast color transitions, one slow confident fill, a single persistent pulse marking "now". Nothing bounces except the celebration toast. Respect prefers-reduced-motion.
Timing tokens
| Token | Value | Used for |
|---|---|---|
--at-t-fast | 0.15s ease | Button bg/color/border, row hover, tab color |
--at-t-ui | 0.18s ease | Kebab borders, toggle knob, quiet hovers |
--at-t-med | 0.25s ease | Arrow nudges (→ +3px), legacy button fills |
--at-t-slow | 0.3s ease | Card hover lift, overlay fades |
--at-t-fill | 0.7s ease | Status-bar fill width — the signature move |
--at-pulse | 1.8s infinite | Current-stage dot ring (scale 1→1.9, fade out) |
--at-toast-in | 200ms | Toast slide-in from right + fade |
Live examples
Motion rules
| Rule | Detail |
|---|---|
| Color moves fast | Hover/active feedback ≤180ms. Users should never wait for a hover. |
| Progress moves slow | The 0.7s fill communicates weight — production state is earned, not instant. |
| One pulse per view | The current-stage ring is the only looping animation on a surface. |
| Transform discipline | Lifts ≤2px, arrow nudges ≤3px, active press = translateY(1px). No large parallax in-app. |
| Dragover is stateful, not animated | DropZone switches border/tint instantly via transition-colors. |
| Reduced motion | Under prefers-reduced-motion: kill pulse + shimmer, keep instant state changes. |
Dashboard Components
Dashboards state facts: mono eyebrow, big light number, quiet delta. The Project Status spine is the platform's signature instrument — dissected below. Dark heroes carry borderless glass stat strips.
KPI cards
Value: Roobert 300, 2rem, tracking −0.015em. Units and denominators drop to 1rem muted. Deltas are mono 8.5px — green up, red down, gray neutral.
Status spine — anatomy
| Part | Spec |
|---|---|
| Track | 26px tall (detail) · #ECECEC · 1px #EEEEEE border · 4px radius |
| Fill | Gradient #D94E2A → #F1613A (90deg) · width animates 0.7s ease · SAME gradient family as the project bar |
| Phase dividers | 1px rgba(0,0,0,.15) at each stage boundary |
| Stage dots | 8px, half-hanging off the bottom edge · done = #C74A2E fill · upcoming = white + 25% ink border |
| Current dot | 11px #C74A2E + 1.5px pulsing ring (1.8s, scale 1→1.9) |
| Pairing rule | 🔴 Fill edge ends EXACTLY at the current dot — both derive from one current-phase source |
| Densities | detail 26px / card 18px / row 12px · admin register spine labels = 10px (never 6px) |
Glass stats strip (dark hero)
Glass standing rule (2026-08-02): rgba(10,10,8,.30) + backdrop-filter: blur(16px), no border. Cells split by 7% white hairlines inside the strip.
Activity feed
Modals & Overlays
Overlays are borderless glass: rgba(10,10,8,.30) + 16px backdrop blur — no strokes, ever. Dialogs are white 10px-radius panels with a mono eyebrow, hairline-separated footer, and right-aligned actions. The drawings viewer modal is the exemplar.
Dialog on glass
| Part | Spec |
|---|---|
| Overlay | rgba(10,10,8,.30) + backdrop-filter: blur(16px) · borderless (glass rule) · click closes non-destructive dialogs |
| Panel | White · 10px radius · modal shadow token · width min(480–720px, 100% − 2×gutter) |
| Header | Mono eyebrow (context) + Roobert 400 title · ✕ ghost close, ESC closes |
| Footer | Paper bg + hairline top · actions right, primary LAST · danger fill only for destructive commits |
| Motion | Overlay fades 0.3s · panel fades + rises ~8px · no bounce |
| Focus | Trap inside dialog · return focus to trigger on close |
Drawers · Tooltips · Popovers
Do & Don't
Feedback
Toasts confirm, banners warn, skeletons wait, empty states invite. Feedback is brief, placed where the action happened, and auto-dismisses when nothing is required of the user.
Toasts
Banners
Loading & progress
Accessibility
WCAG 2.1 AA is the floor. The warm palette passes comfortably for text; the accent is managed as a large-text/UI color. Every interactive element has a visible focus ring, a 44px touch target on mobile, and a real name for assistive tech.
Contrast summary
| Usage | Pair | Ratio | Verdict |
|---|---|---|---|
| Body text | Ink on Paper / White | 16.9 / 17.6 | AAA |
| Secondary text | Ink-2 on White | 11.9 | AAA |
| Labels ≥12px | Muted #757575 on White | 4.6 | AA |
| Small labels on paper | Muted-2 #6D675F on Paper | 5.3 | AA — prefer over #757575 on paper |
| Primary button label | White on Accent | 3.2 | UI-component pass (3:1) — labels only, never paragraphs |
| Accent as text | Accent-dark on White | 4.1 | Large text / paired with icon+underline |
| Destructive text | Red #C0392B on White | 5.9 | AA |
| Rail text | 72% White on Dark | ≥9 | AA |
Focus & keyboard
| Rule | Requirement |
|---|---|
| Keyboard order | DOM order = visual order. Rail → header → content. Skip-link to main on every portal. |
| Modals | Focus trapped; ESC closes; focus returns to trigger. role="dialog" + aria-modal + labelled title. |
| Touch targets | ≥44×44px on mobile. Small visual controls (kebab 32px) carry padding to reach 44px hit area. |
| Icon-only controls | Always aria-label (kebab = "Item actions", ✕ = "Close"). |
| Status ≠ color alone | Chips pair color with text; spine pairs fill with labels + dates; deltas pair color with ▲▼. |
| Images | Item thumbs get item-name alt; decorative heroes alt="". |
| Motion | prefers-reduced-motion: disable pulse + shimmer, keep state changes instant. |
| Uppercase labels | Applied via CSS text-transform, never typed — screen readers get natural casing. |
Responsive
Desktop-first density, mobile-first respect. Every core component has a defined path from 1440px down to the 390px floor. On mobile the rail becomes a fixed bottom nav and the body reserves clearance for it — content never hides under the home indicator.
Adaptation map
| Component | Desktop ≥1360 | Laptop/Tablet ≤1360/900 | Mobile ≤767 → 390 |
|---|---|---|---|
| Nav rail | 248px dark rail, collapsible | Collapsed icon rail (state persists) | Hidden → fixed bottom MobileNav (3.5rem) + safe-area clearance |
| Production register | 5-col grid, spine visible | ≤900: drops qty/cost column, tighter gaps | Thumb + name + kebab; spine collapses to stage chip + % |
| Status spine (detail) | All labels + dates | ≤900: labels tighten (7.5px/64px) | ≤640: only first / last / CURRENT labels; dots remain for every stage |
| Tabs | Inline row | Inline, may wrap groups | Horizontal scroll + edge fade + active tab auto-centers |
| KPI grid | 4-up | 2-up | 1–2-up, value scales to 1.6rem |
| Modals | Centered 480–720px | Centered, gutter margins | Full-width sheet, footer actions stack full-width |
| Messages | Two-pane (list + thread) | Two-pane narrow | Single pane — list OR thread, never squeezed panes |
| Forms | Two-column where related | Single column | Single column, 16px field font (blocks iOS zoom) |
| Glass stats strip | One row of cells | Wraps 2–3 per row | 2-up grid, same glass |
| Dark hero | Full editorial hero | Reduced height | Compact hero, glass info card goes full-width |
Mobile hard rules
| Rule | Spec |
|---|---|
| Design floor | 390×844 (iPhone 12–14). Everything must be usable and unclipped here. |
| Bottom-nav clearance | body { padding-bottom: calc(3.5rem + 12px + env(safe-area-inset-bottom)) } ≤767px (audit fix 2026-08-05) — the last content block never sits under the fixed nav |
| Viewport | viewport-fit=cover + safe-area insets respected on fixed elements |
| Type floor | Body never below 16px in inputs; labels never below 10px |
| Touch | 44px targets; row tap areas span the full row |
Design Principles
Ten commitments that make the platform feel like category-defining infrastructure — kept short enough to be enforceable in review.
One headline per surface
Every view has one dominant element — a number, a title, a status. Size contrast is steep (24px values over 10px labels); everything else recedes into mono microtype.
Registers tight, reading loose
Operational lists earn 12px meta and 18px rows. Narrative surfaces (approvals, foreword-class pages) breathe at 24–64px. Never mix the two registers on one card.
One pattern, every portal
Admin, client, and factory share components verbatim. Firewalls strip data, not design. If a portal needs a "special" version, the component is wrong.
Machine text is mono, human text is not
IDs, stages, dates, sizes → Roobert Mono uppercase. Sentences, names, titles → Roobert. The split is what makes dense screens scannable.
Calm, then one heartbeat
Fast color feedback, slow progress fills, and exactly one pulse per view marking "now". Motion never entertains; it locates.
Rectangular conviction
2–10px radii, hairline borders, no pills, no stadium shapes. Circles are reserved for avatars, dots, and radios — signals, not chrome.
Accent is a scalpel
Orange marks the primary action and live production state. If two things glow, neither does. Status hues stay tinted and quiet.
Photography carries heroes
Hero areas are full-bleed imagery with borderless glass overlays — luxury-magazine, not dashboard boxes. White cards are for work, not for welcome.
Design intent ≠ fabrication intent
Reference drawings (what the designer meant) and shop drawings (what the factory will build) are separate objects with separate ceremonies — reference informs, shop drawings get approved. The UI must never blur which one the user is looking at.
Legible to humans and machines
Structured registers, tokenized styles, semantic status objects — surfaces are built so agents can read, summarize, and act on them. AI output enters through the same components humans use; provenance stays visible.
Component Library
The closed inventory. Each component lists its variants, sizes, states, and canonical source. If it isn't here, propose it — don't hand-roll it.
| Component | Variants | Sizes | States | Source of truth |
|---|---|---|---|---|
| Button | primary · secondary · quiet(+arrow) · ghost · destructive · danger | sm / md / lg | default, hover, focus, disabled, loading | ui/Button.tsx + button.css |
| StatusChip | green · neutral · accent · red · amber | one | static | button.css .achip |
| Tag (dot) | production · transit · review · done · draft · type | one | static | client-shell.css .tag |
| Input / Select / Textarea / Date | text, email, search, select, date, textarea | one | default, focus, error, success, disabled | ui/Input.tsx, ui/Select.tsx |
| Toggle | — | 38×22 | off, on, focus, disabled | ui/Toggle.tsx (6px track) |
| DropZone | full · compact · file-selected | — | idle, dragover, busy, disabled | ui/DropZone.tsx (~17 surfaces) |
| ItemStatusBar | 5-stage spine (+9/10-stage sets) | detail / card / row | animated fill + pulsing current | items/ItemStatusBar.module.css |
| Production register | admin (full) · client (no cost) · factory (no client identity) | row 96px | hover, scrolling after 5 rows | admin-v3.css .pis-* |
| KPI card | plain · delta up/down · glass (dark hero) | — | static | overview cards + .frost |
| Modal | dialog · confirm(danger) · viewer(drawings) | 480–720px | open/close, focus-trapped | glass rule + drawings viewer |
| Toast | success · error · warning · info | max-w-sm | enter 200ms, auto-dismiss 4s | ui/Toast.tsx |
| Menu / Popover | kebab menu · popover | min 156px | open, hover, danger row | .pis-menu |
| Tabs | underline (+count chip) | one | active, hover, scroll+fade (mobile) | item drawings tabs |
| Breadcrumbs / Pagination | — | one | hover, current, disabled | ui/Breadcrumbs.tsx |
| Avatar | accent · ink · outline · stack | 26 / 34 / 46 | image or initials | .pm-av |
| Empty state / Skeleton | icon+title+action · shimmer blocks | — | — | §08 patterns |
Canonical Don'ts (real, from production review)
Long-form copy set in mono reads like a terminal, not a product.
src="data:image/png;base64,iVBORw0KG…"Getting Started for Engineers
The system lives in the platform repo. Use shared components; never re-implement styles per page. This book ships machine-usable tokens for anything new.
Tokens
| Artifact | Path | Use |
|---|---|---|
| CSS custom properties | assets/design-tokens.css | Import at :root — all --at-* variables used throughout this book |
| JSON tokens | assets/tokens.json | Design-token pipeline / Figma sync / codegen |
Canonical sources in atelier-platform
| Concern | File |
|---|---|
| Global fonts + safe-area + mono rules | src/app/globals.css |
| Portal shell vars + shared kits | src/app/client-shell.css (scoped .atelier-client) |
| Buttons + chips | src/components/ui/Button.tsx · button.css — use <Button variant> / buttonClasses() / StatusChip |
| Uploads | src/components/ui/DropZone.tsx — visual shell only; callers own upload flow |
| Status spine | src/components/items/ItemStatusBar*.{tsx,module.css} — detail/card/row densities |
| Admin register + overview kit | src/app/admin/projects/[id]/admin-v3.css (.pis-*, .cticon) |
| Toasts / inputs / toggles / breadcrumbs | src/components/ui/* |
| Decision history | ATELIER-ARCHITECTURE.md + memory/design-decisions.md |
Rules of engagement
| # | Rule |
|---|---|
| 1 | New UI uses shared components. A new variant is a PR to the component, not a page-level override. |
| 2 | No new hex values — pull from tokens. If a color isn't in §03, it doesn't exist. |
| 3 | No pills, no bone backgrounds, no mono body text, no bordered glass, no sub-10px mono labels. |
| 4 | One accent-filled primary per view; destructive fills live in confirm modals only. |
| 5 | Item imagery via the image-URL endpoint. Thumbnails always present in registers. |
| 6 | Ship at 390px. Verify bottom-nav clearance and safe-area on any fixed element. |
| 7 | Julien-approved designs are pixel-exact contracts. Codify new approvals in design-decisions.md the same day. |