From 85064b523194d5b9f70b1660512c634627863ee7 Mon Sep 17 00:00:00 2001 From: Shaan Shoukath <147940963+Shaan-Shoukath@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:59:33 +0530 Subject: [PATCH] Make the UX spec our own: Space Works UX, inspired by Yuyu Co-Authored-By: Shaan-Shoukath Co-Authored-By: Claude Opus 5.5 --- README.md | 9 +- docs/ui-brand-spec.md | 56 +-- docs/ux-screens-longtail.md | 188 ++++----- docs/{ux-yuyu-spec.md => ux-spec.md} | 377 ++++++++++--------- site/astro.config.mjs | 3 +- site/scripts/sync-kit.mjs | 2 +- site/src/content/docs/about/credits.md | 7 +- site/src/content/docs/brand/introduction.mdx | 3 +- site/src/content/docs/index.mdx | 1 + site/src/content/docs/ux/introduction.mdx | 60 +++ 10 files changed, 388 insertions(+), 318 deletions(-) rename docs/{ux-yuyu-spec.md => ux-spec.md} (91%) create mode 100644 site/src/content/docs/ux/introduction.mdx diff --git a/README.md b/README.md index 9ac155f..28dc451 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ masters, colours, type, voice, every design token and the UI specs, published fr | `icons/` | `favicon.svg`, PNG app icons (16, 32, 180, 192, 512) and the 1200×630 social card | | `fonts/` | Gilroy Regular, Medium, Semibold and Bold (WOFF), plus Unbounded (WOFF2). Legacy faces retained for existing consumers. See `fonts/README.md`. | | `tokens/` | `tokens.css` (`@font-face` + light/dark colour variables as RGB triplets) and `tokens.json` (the same colours as hex, plus the seven brand colours) | -| `docs/` | `ui-brand-spec.md` (token derivations, contrast ratios, states, status pills, type scale, logo use), `ux-yuyu-spec.md` (UX patterns from Yuyu, reusable layouts, navigation, screen specs), `ux-screens-longtail.md` (remaining screens) | +| `docs/` | `ui-brand-spec.md` (token derivations, contrast ratios, states, status pills, type scale, logo use), `ux-spec.md` (**Space Works UX**: our interaction patterns, reusable layouts, navigation and screen specs), `ux-screens-longtail.md` (remaining screens) | | `site/` | The Starlight website published to GitHub Pages; `scripts/sync-kit.mjs` copies the kit in at build time | -| `references/yuyu/` | Reference boards from [Yuyu](https://github.com/twilighty-abhi/yuyu) that the UX patterns and layouts are modelled on | +| `references/yuyu/` | The reference boards from [Yuyu](https://github.com/twilighty-abhi/yuyu) that inspired Space Works UX | ## Brand at a glance @@ -51,8 +51,9 @@ Add the class `dark` to `` for dark mode. The font URLs in `tokens.css` ar ## Credits -The UX patterns, layouts and interaction details are modelled on [Yuyu](https://github.com/twilighty-abhi/yuyu) -(reference boards in `references/yuyu/`). The visual identity — colour, type, logo, icons and voice — comes only from the Space Works +**Space Works UX** (the patterns, layouts and interaction details in `docs/ux-spec.md`) is our own +interaction system. It was inspired by [Yuyu](https://github.com/twilighty-abhi/yuyu), whose screens we +studied as reference boards (kept in `references/yuyu/`). The visual identity — colour, type, logo, icons and voice — comes only from the Space Works brand guidelines in `brand/`. ## Export provenance diff --git a/docs/ui-brand-spec.md b/docs/ui-brand-spec.md index a764048..949c9ad 100644 --- a/docs/ui-brand-spec.md +++ b/docs/ui-brand-spec.md @@ -3,14 +3,14 @@ Design-system specification. The brand export and typography sections were refreshed from Figma on 15 October 2026. App-specific implementation notes below describe the prior integration, not verification of another repository. **Binding source rule (owner).** Every visual decision comes from exactly two sources: the brand PDF -(`brand/space-works-brand-guidelines-v1.pdf`) for visual identity, and the Yuyu boards -(`scratchpad/crops/*.png`, summarised in `ux-yuyu-spec.md`) for layout and UX. All existing app +(`brand/space-works-brand-guidelines-v1.pdf`) for visual identity, and the UX reference boards +(`scratchpad/crops/*.png`, summarised in `ux-spec.md`) for layout and UX. All existing app functionality is kept and re-themed. Every token below names its source: - **brand**: a verbatim brand hex, or white (p.4 and p.16 draw white cards). - **brand-mix**: a per-channel sRGB mix of brand colours (Deep Ink, Warm Cream, white, or a brand accent with one of those). The mix is stated. -- **Yuyu**: a pixel-sampled Yuyu colour. **Yuyu-mix**: a sampled Yuyu colour mixed with a brand neutral. +- **Ref**: a colour pixel-sampled from the UX reference boards. **Ref-mix**: a sampled reference colour mixed with a brand neutral. Nothing else (no OKLCH shifts, no black, no invented hues). @@ -23,7 +23,7 @@ Nothing else (no OKLCH shifts, no black, no invented hues). | `pdf2svg.py`, `logo_list.py`, `logo_build.py` | vector extraction to SVG | | `brand_tokens_v2.py` | the token derivation and every contrast ratio | | `brand_contract_v2.py` | the tenant contract plus a 4,096-colour sweep | -| `png.py`, `sample_yuyu.py` | Yuyu pixel sampling | +| `png.py`, `sample_reference.py` | reference-board pixel sampling | | `otfinfo.py` | font metrics | The maths is WCAG 2.x relative luminance. It reproduces the PDF's own tested pairs exactly: @@ -58,12 +58,12 @@ The maths is WCAG 2.x relative luminance. It reproduces the PDF's own tested pai | D1 | The light canvas is Warm Cream with white cards. Dark mode is built **only from the brand's reversed pairings**: `bg` = Deep Ink, raised surfaces = Ink mixed with 3/6/11 % Cream, text = Warm Cream, accents = brand. **No black.** | brand p.7 (reversed on Ink / Teal); owner rule | | D2 | `accent` = **Maker Teal** in light. In dark it is **Teal + 34 % Cream** (#579C90) with Deep Ink labels. | brand-mix. Brand teal on Deep Ink is 2.36:1, under the 3:1 a selected-state fill needs (SC 1.4.11). | | D3 | `secondary` is renamed **`brand`** (the action colour of a stranger-facing surface; default = accent; tenant-overridable). **`emphasis` = Workshop Orange**: Space Works' own, never tenant-overridable, one bold moment per layout. | brand p.9 "teal = primary · actions; orange = energy · emphasis"; p.13 "use one bold moment" | -| D4 | **Danger comes from Yuyu**, because the brand has no red: `#B91C1C` text/fill, `#991B1B` hover, `#FCA5A5` card border (dark text). | Yuyu org5 danger zone (sampled, C.0) | +| D4 | **Danger is sampled from the UX reference boards**, because the brand has no red: `#B91C1C` text/fill, `#991B1B` hover, `#FCA5A5` card border (dark text). | reference org5 danger zone (sampled, C.0) | | D5 | Solid `*-soft` tokens replace every `bg-{tone}/NN`. **Translucent text is banned.** | Codex (1): `text-on-accent/75` on teal = 3.90:1 | -| D6 | Status = **word** (mandatory) on a soft pill. Light mode uses the brand-tested solid pastel + ink pairs; dark mode uses soft + tone-ink. Icons are optional. | brand p.10 "add a word or icon to every status"; Yuyu P10 tinted pills | +| D6 | Status = **word** (mandatory) on a soft pill. Light mode uses the brand-tested solid pastel + ink pairs; dark mode uses soft + tone-ink. Icons are optional. | brand p.10 "add a word or icon to every status"; UX spec P10 tinted pills | | D7 | **Typography updated by the owner, 15 October 2026:** Gilroy Regular 400 / Medium 500 / Semibold 600 / Bold 700 for all everyday text and headings. Unbounded 500–600 for one short accent phrase. This supersedes the previous Outfit body and free-weight-only restriction. | owner; current Figma p.11–12 | -| D8 | Shape follows the brand's own product mock (p.16, measured): controls r 10, window/cards r 16, tags fully round, **no shadows**, hairlines = Deep Ink @ 18 %. Yuyu asks for pill buttons; one token switches it. | brand p.16 vector; Yuyu P11 | -| D9 | Light-mode sidebar = **Citron field** with Deep Ink labels and a filled teal active item. Dark-mode sidebar = Deep Ink. **The nav is text-only.** Neither the brand mock (p.16) nor Yuyu (org5) puts icons in the sidebar. | brand p.16; Yuyu org5 | +| D8 | Shape follows the brand's own product mock (p.16, measured): controls r 10, window/cards r 16, tags fully round, **no shadows**, hairlines = Deep Ink @ 18 %. The UX reference uses pill buttons; one token switches it. | brand p.16 vector; UX spec P11 | +| D9 | Light-mode sidebar = **Citron field** with Deep Ink labels and a filled teal active item. Dark-mode sidebar = Deep Ink. **The nav is text-only.** Neither the brand mock (p.16) nor the UX reference (org5) puts icons in the sidebar. | brand p.16; reference org5 | | D10 | Attention counters and low-stock tags use `emphasis` (orange + ink; p.16 "2 left" is an orange tag). `danger` is reserved for destructive actions and errors. | brand p.16 | | D11 | **No "System" theme option.** The existing Light/Dark toggle stays as it is. | owner | | D12 | Logo masters are exported directly from the Figma component library into `logo/`. All three horizontal variants are supplied as masters, including reversed and one-colour ink. | current Figma component library | @@ -121,11 +121,11 @@ The maths is WCAG 2.x relative luminance. It reproduces the PDF's own tested pai | `on-info` | `20 44 42` #142C2A | brand (tested pair) | `20 44 42` #142C2A | brand (tested pair) | | `info-soft` | `246 249 255` #F6F9FF | brand-mix: panel + 12% info | `52 74 77` #344A4D | brand-mix: panel + 12% info | | `info-ink` | `81 105 123` #51697B | brand-mix: Sky + 62% Ink | `181 205 255` #B5CDFF | brand | -| `danger` | `185 28 28` #B91C1C | Yuyu (sampled #B91C1C) | `252 165 165` #FCA5A5 | Yuyu (sampled #FCA5A5) | -| `danger-hover` | `153 27 27` #991B1B | Yuyu (sampled #991B1B) | `253 185 182` #FDB9B6 | Yuyu-mix: #FCA5A5 + 25% Cream | -| `danger-active` | `133 30 29` #851E1D | Yuyu-mix: #991B1B + 15% Ink | `253 201 195` #FDC9C3 | Yuyu-mix: #FCA5A5 + 45% Cream | +| `danger` | `185 28 28` #B91C1C | Ref (sampled #B91C1C) | `252 165 165` #FCA5A5 | Ref (sampled #FCA5A5) | +| `danger-hover` | `153 27 27` #991B1B | Ref (sampled #991B1B) | `253 185 182` #FDB9B6 | Ref-mix: #FCA5A5 + 25% Cream | +| `danger-active` | `133 30 29` #851E1D | Ref-mix: #991B1B + 15% Ink | `253 201 195` #FDC9C3 | Ref-mix: #FCA5A5 + 45% Cream | | `on-danger` | `255 244 232` #FFF4E8 | brand | `20 44 42` #142C2A | brand | -| `danger-line` | `252 165 165` #FCA5A5 | Yuyu (sampled, danger-zone card border) | `124 98 97` #7C6261 | Yuyu-mix: Ink + 45% #FCA5A5 | +| `danger-line` | `252 165 165` #FCA5A5 | Ref (sampled, danger-zone card border) | `124 98 97` #7C6261 | Ref-mix: Ink + 45% #FCA5A5 | | `sidebar` | `223 255 143` #DFFF8F | brand (p.16 sidebar) | `20 44 42` #142C2A | brand (Deep Ink) | | `sidebar-hover` | `242 255 210` #F2FFD2 | brand-mix: Citron + 60% white | `46 66 63` #2E423F | = hover | | `sidebar-active` | `0 110 99` #006E63 | brand (p.16 active item) | `87 156 144` #579C90 | = accent | @@ -167,7 +167,7 @@ The worst cell in the whole matrix is **4.65 in light** (`info-ink` on `hover`) | `focus` | 5.68 / 5.28 / 6.16 / 5.52 | 7.83 / 7.21 / 6.61 / 7.83 | | `outline` (form-control boundary) | 3.27 / 3.04 / 3.55 | 3.62 / 3.33 / 3.06 | | `accent` as a selected fill (nav pill, tab underline) | 5.68 / 5.28 / 6.16 / 5.52 | 4.61 / 4.25 / 3.89 / 4.61 | -| `danger-line` (decorative card border, Yuyu) | 1.90 vs panel | 2.24 vs panel | +| `danger-line` (decorative card border) | 1.90 vs panel | 2.24 vs panel | | `line` (decorative hairline) | 1.42 / 1.32 / 1.54 | ink on line 8.67 | Banned translucent cases (measured): `on-accent/75` on teal is 3.90 and `/80` is 4.22. Cream at 80 % on @@ -321,17 +321,17 @@ The button family stays closed (`desk-button*`). Every variant keeps `min-h-11` |---|---|---|---|---|---| | `desk-button-primary` (staff main) | `bg-accent text-on-accent` | `bg-accent-hover` | `bg-accent-active` | 5.68 / 6.24 / 6.78 | 4.61 / 5.22 / 5.89 | | `desk-button-brand` (ex `-secondary`; public/member main) | `bg-brand text-on-brand border-brand-boundary` | `bg-brand-hover` | `bg-brand-active` | default = primary; tenants proven by H.5 | | -| `desk-button` (neutral; the Yuyu outline "secondary") | `bg-panel text-ink border-line` | `bg-hover` | `bg-line` | 14.75 / 11.95 / 9.60 | 11.49 / 9.84 / 8.67 | +| `desk-button` (neutral; the outline "secondary") | `bg-panel text-ink border-line` | `bg-hover` | `bg-line` | 14.75 / 11.95 / 9.60 | 11.49 / 9.84 / 8.67 | | `desk-button-ghost` | `text-accent-ink`, no fill | `bg-accent-soft` | `bg-accent-soft text-ink` | 6.16 / 5.16 / 12.37 | 6.61 / 5.60 / 9.72 | | `desk-button-emphasis` (new; the one bold public CTA, never in staff chrome) | `bg-emphasis text-on-emphasis` | `-hover` | `-active` | 5.51 / 5.99 / 6.55 | same | | `desk-button-danger` (final confirm) | `bg-danger text-on-danger` | `bg-danger-hover` | `bg-danger-active` | 5.96 / 7.66 / 8.80 | 7.77 / 8.99 / 10.06 | -| `desk-button-danger-outline` (new; Yuyu "Delete organisation…") | `bg-panel text-danger border-danger` | `text-danger-hover border-danger-hover` | same + `bg-hover` | 6.47 / 8.31 / 6.73 | 6.57 / 7.79 / 6.68 | +| `desk-button-danger-outline` (new; "Delete organisation…") | `bg-panel text-danger border-danger` | `text-danger-hover border-danger-hover` | same + `bg-hover` | 6.47 / 8.31 / 6.73 | 6.57 / 7.79 / 6.68 | | `desk-button-success` / `-warn` (kept in Phase 1, folded into primary/neutral in Phase 6) | `bg-success\|warn text-on-*` | `-hover` | `-active` | 13.23/10.99/8.99 · 10.37/8.66/7.11 | same | | Element | Rest | Hover | Selected / current | Ratios (light · dark) | |---|---|---|---|---| | Sidebar item (text-only, D9) | `text-ink` on `sidebar` | `bg-sidebar-hover` | `bg-sidebar-active text-on-accent font-semibold`, `aria-current="page"` | 13.23 / 14.06 / 5.68 (pill vs field 5.52) · 13.60 / 9.84 / 4.61 (pill vs field 4.61) | -| Sidebar sub-item (Yuyu org5: indented, smaller) | `text-muted` 14/20 | `text-ink` | `text-ink font-semibold`, no fill | 5.23 · 7.92 | +| Sidebar sub-item (reference org5: indented, smaller) | `text-muted` 14/20 | `text-ink` | `text-ink font-semibold`, no fill | 5.23 · 7.92 | | Underline tab | `text-muted` | `text-ink` | `text-ink font-semibold` + 2 px `accent` underline | underline 5.68 · 4.61 | | Segmented control | track `bg-surface border-line` | `text-ink` | thumb `bg-panel border-outline text-ink` | thumb boundary 3.55 · 3.06 | | Table / list row | `bg-panel` | `bg-hover` | `bg-accent-soft` + checked box | 14.75 / 11.95 / 12.37 · 11.49 / 9.84 / 9.72 | @@ -347,7 +347,7 @@ Counters (unread, cart): `bg-emphasis text-on-emphasis` (5.51), at full opacity. ## C. Status pills -**C.0 Yuyu samples** (from `crops/org5-settings.png` using `png.py`; exact pixel values): +**C.0 Reference samples** (from `crops/org5-settings.png` using `png.py`; exact pixel values): | Colour | Where | Matches | |---|---|---| @@ -358,7 +358,7 @@ Counters (unread, cart): `bg-emphasis text-on-emphasis` (5.51), at full opacity. The greens are **not used**: Citron covers success (owner preference). -**Shape** (Yuyu P10 + brand p.16): fully round, 24 px tall (32 on public), 12/16 semibold, horizontal +**Shape** (UX spec P10 + brand p.16): fully round, 24 px tall (32 on public), 12/16 semibold, horizontal padding 8–10. The **word is mandatory and the icon is optional.** Neither source shows icons in pills. | Tone | Light | Dark | @@ -368,7 +368,7 @@ padding 8–10. The **word is mandatory and the icon is optional.** Neither sour | info | `bg-info text-on-info` (9.23) | `bg-info-soft text-info-ink` (5.89) | | emphasis | `bg-emphasis text-on-emphasis` (5.51) | same | | accent | `bg-accent-soft text-accent-ink` (5.16) | same (5.60) | -| danger (Yuyu danger-zone vocabulary) | `bg-panel border border-danger-line text-danger` (6.47) | same (6.57) | +| danger (danger-zone vocabulary) | `bg-panel border border-danger-line text-danger` (6.47) | same (6.57) | | neutral | `bg-surface border border-line text-ink` (12.64) | same (12.52) | | Domain · status (backend value) → tone · word | @@ -458,11 +458,11 @@ Measured from the p.16 vector mock (the 1312 × 493 product frame): | Sidebar | 245 wide, 20 px inset, labels inset 19 | | Nav | text-only | -Yuyu (org5) shows the same pattern: a text-only nav, an active item filled warm grey, sub-items indented, and danger as a white card with a red-300 border. +The UX reference (org5) shows the same pattern: a text-only nav, an active item filled warm grey, sub-items indented, and danger as a white card with a red-300 border. | Token | Value | Notes | |---|---|---| -| `--radius-control` | **10px** (brand). Owner may switch it to `9999px` for Yuyu pill buttons. | buttons, nav items, inputs, segmented thumb | +| `--radius-control` | **10px** (brand). Owner may switch it to `9999px` for pill buttons. | buttons, nav items, inputs, segmented thumb | | `borderRadius` sm / DEFAULT / md / lg / xl / 2xl / full | 6 / 8 / 10 / 12 / **16** / 20 / 9999 | `xl` = cards and panels (was 18). `md` (273 uses) 12→10. `lg` (93) 14→12. | | Borders | 1px `line` on cards and dividers. 1px `outline` on form controls. 2px only for the tab underline and the focus ring. | | | Shadow | cards none. `shadow-overlay` = `0 2px 6px rgb(var(--shadow-color)/.06), 0 12px 32px rgb(var(--shadow-color)/.10)` for popovers, menus, modals, drawers and toasts only. | `soft`, `soft-lg`, `brutal*` and `elev-*` deleted | @@ -527,8 +527,8 @@ always labelled. **`lucide-react` is acceptable only as the implementation of th a 24 grid with 2 px stroke and round caps and joins. Pin the exact version. Render at 20/16 with default (relative) stroke. Icon-only buttons need `aria-label` plus a tooltip, and the SVG gets `aria-hidden`. -**Source observations.** Neither the p.16 brand mock nor the Yuyu sidebar (org5, org1) uses nav icons; both -nav lists are **text-only**. Yuyu uses icons only for "View public page ↗", the switcher chevrons, +**Source observations.** Neither the p.16 brand mock nor the reference sidebar (org5, org1) uses nav icons; both +nav lists are **text-only**. The reference uses icons only for "View public page ↗", the switcher chevrons, callouts and back arrows. **Default: no icons in the sidebar or member tabs.** Icons appear only where a source shows them: @@ -564,7 +564,7 @@ This replaces `components/icons.tsx` and `staffNavIcons.tsx`. The old nav-icon s | `features/inventory/ProductCard.tsx:71`, `ProductQuickViewModal.tsx:52` | `text-ink/15` decorative initials | `text-line` + `aria-hidden` | | `styles/chrome.css` `.material-chrome` | panel @0.8 + blur | delete | | `components.css` ghost/nav `hover:bg-accent/15`; the `.desk-shell .bg-secondary/10\|15` color-mix block | alpha tints | `hover:bg-accent-soft`; `bg-brand-soft`; delete the color-mix block | -| **119 `bg-{tone}/NN` occurrences in 58 files** (12 of them `hover:`) | alpha tints | `/5`, `/10`, `/15` → `bg-{tone}-soft` (`secondary` → `brand-soft`). **`bg-danger/NN` → `bg-panel border border-danger-line`** (the Yuyu danger card; there is no danger-soft) | +| **119 `bg-{tone}/NN` occurrences in 58 files** (12 of them `hover:`) | alpha tints | `/5`, `/10`, `/15` → `bg-{tone}-soft` (`secondary` → `brand-soft`). **`bg-danger/NN` → `bg-panel border border-danger-line`** (the danger card; there is no danger-soft) | | `border-{tone}/20–40` (≈40) | decorative | allowed; `border-danger/40` → `border-danger-line` | | `disabled:opacity-50` (14) | WCAG-exempt | allowed outside the button family | @@ -675,7 +675,7 @@ PINs (keep `tracking-widest`), versions and public codes. They include 8 inputs: ### H.7 Other corrections - `statusToneMap`: `issued` and `checked_out` change `danger` → `accent`. Add every status in C. -- The dashboard and stats tiles become `StatCard`: a neutral panel with the tone carried by the number (Yuyu P4). +- The dashboard and stats tiles become `StatCard`: a neutral panel with the tone carried by the number (UX spec P4). - `OperationsReportCharts` marks need ≥3:1 on panel. Light: teal 6.16, info-ink 5.74, success-ink 5.75, warn-ink 5.78, danger 6.47. Dark: the brand fills. Orange and the pastels in light mode only appear with a 1 px ink stroke. - The PLAN's "theme (Light/Dark/System)" in the user menu becomes **Light/Dark** (D11). @@ -694,7 +694,7 @@ PINs (keep `tracking-widest`), versions and public codes. They include 8 inputs: | new `test_sidebar` | ink, muted and accent-ink on `sidebar` and `sidebar-hover` ≥4.5; `sidebar-active` vs `sidebar` ≥3; `on-accent` on `sidebar-active` ≥4.5 | | `test_focus…` | ≥3 on bg, surface, panel, hover and sidebar | | new `test_control_boundary` | `outline` ≥3 on bg, surface and panel | -| new `test_token_provenance` | parse the A.1 values. Every neutral must equal an integer-percent mix of `20 44 42` with `255 244 232` or white (re-derive and compare). Every fill must be a brand hex or one of the 3 Yuyu reds. Fails on any off-source colour. | +| new `test_token_provenance` | parse the A.1 values. Every neutral must equal an integer-percent mix of `20 44 42` with `255 244 232` or white (re-derive and compare). Every fill must be a brand hex or one of the 3 sampled danger reds. Fails on any off-source colour. | | new source guards (scan `frontend/src`, excluding `*.test.*`) | `\btext-[\w-]+/\d+`; `\bbg-(accent\|brand\|emphasis\|success\|warn\|info\|danger)/\d+`; `\btext-(accent\|brand\|emphasis\|success\|warn\|info)(?![\w-])`; `\bfont-mono\b`; `warning\|accent-bright\|secondary` colour utilities; `brand-` utilities under `features/staff/` (except `MakerspaceThemeSettings.tsx`); `#[0-9a-fA-F]{6}` hex literals outside `lib/brandColours.ts`, `SpaceWorksLogo` and the filament swatches | | Parser | unchanged (`:root {` / `:root.dark {`, comments stripped) | @@ -770,8 +770,8 @@ these tests live only in the main checkout (they are gitignored), so the updated ## J. Owner decisions still open -1. Control radius: 10 px (brand p.16) or pill (Yuyu). -2. Citron sidebar (brand p.16) or the Yuyu warm-neutral sidebar. +1. Control radius: 10 px (brand p.16) or pill (UX reference). +2. Citron sidebar (brand p.16) or the reference warm-neutral sidebar. 3. Reversed horizontal is now supplied directly from the Figma component library. 4. Tenant mid-tone primaries: accept the 15.8 % rejection (audit stored palettes first), or allow black as a functional-only label. 5. The original Gilroy free-font EULA covers the legacy Light/ExtraBold files. The current 400–700 faces were supplied by the owner; see `fonts/README.md` for provenance and licensing. diff --git a/docs/ux-screens-longtail.md b/docs/ux-screens-longtail.md index 847014f..0efbe06 100644 --- a/docs/ux-screens-longtail.md +++ b/docs/ux-screens-longtail.md @@ -1,6 +1,6 @@ -# UX spec: long-tail screens (Yuyu patterns across all of SpaceWorks) +# UX spec: long-tail screens (Space Works UX across all of SpaceWorks) -Status: research only, no app code. Companion to `ux-yuyu-spec.md`, which covers the core screens and +Status: research only, no app code. Companion to `ux-spec.md`, which covers the core screens and **defines the closed set of page layouts**. This document covers every other screen, so that after the revamp nothing is left in the old `Panel`-and-stacked-forms style. @@ -11,7 +11,7 @@ Branch `feature/design-revamp`. Visual identity (colour, type, logo) comes from ## 0. How to read this doc -### 0.1 Layouts are the closed set from `ux-yuyu-spec.md` +### 0.1 Layouts are the closed set from `ux-spec.md` Every screen below is written as **"Layout X, slots filled by …"**. No screen gets its own page structure. The layouts are: @@ -20,7 +20,7 @@ structure. The layouts are: `SettingsLayout` · `FlowLayout` (wizard) · `AuthLayout` · `CenteredCardLayout` · `PublicLayout` · `PublicDetailLayout` · `KioskLayout`. -The slot names below are what this doc assumes each layout exposes. **Where `ux-yuyu-spec.md` names a slot +The slot names below are what this doc assumes each layout exposes. **Where `ux-spec.md` names a slot differently, its names win.** The mapping is one-to-one, so a rename does not change any screen spec. | Layout | Slots assumed here | @@ -41,10 +41,10 @@ differently, its names win.** The mapping is one-to-one, so a rename does not ch - **One primary action per page.** Actions are ranked as primary (dark/teal pill), secondary (outline pill, at most one or two) and the rest in a **More menu** (`[More v]`, an outline icon button; it opens a menu of labelled items). Exports (CSV/XLSX) always go in More as "Export CSV" / "Export Excel". -- **Row actions:** one inline text action at the right of the row (Yuyu "Send invites", "Undo"). Every +- **Row actions:** one inline text action at the right of the row (reference "Send invites", "Undo"). Every other row action goes in a row overflow `[...]` menu. Destructive items sit last in red and always confirm. -- **Inline Undo** (Yuyu mgr2/att11) is used for reversible one-click state changes that the API already +- **Inline Undo** (reference mgr2/att11) is used for reversible one-click state changes that the API already supports reversing. Where the API has no reverse, use a confirm dialog and do not fake an Undo. - **Contextual callout** (warm yellow, icon, bold lead sentence, action link on the right) replaces today's `bg-warn` boxes, read-only notices and disclosure paragraphs. There is at most **two per page**. @@ -62,7 +62,7 @@ differently, its names win.** The mapping is one-to-one, so a rename does not ch `machines` may carry a path segment, and that stays true. - **States voice** (brand: clear, warm, specific, says the next step): - Empty: "No {things} yet. {Do the next thing}." The primary action is repeated as a button inside the - empty card (dashed border, Yuyu org4/org6). + empty card (dashed border, reference org4/org6). - Filtered empty: "Nothing matches these filters." + "Clear filters" link. - Loading: skeleton rows or skeleton stat cards that match the final layout. No "Loading..." text except in `aria-live` status. @@ -81,9 +81,9 @@ differently, its names win.** The mapping is one-to-one, so a rename does not ch payload" label, "Search and commands", "Sign out", "Station PIN". If a name must change, the matching test changes in the same commit. -### 0.3 Yuyu reference screens cited +### 0.3 Reference screens cited -| ID | Yuyu board / screen | Pattern it contributes | +| ID | Reference board / screen | Pattern it contributes | |---|---|---| | org1 | Organiser workspace · Home | greeting + date, "Your organisations" cards, dashed "+ New" card, "Coming up" date-chip rows, Recent activity rail | | org2 | Organiser workspace · Events | title + 1 primary + 1 outline, yellow callout with link, 4 stat cards, underline tabs with counts, date-chip card rows, inline row action, dashed draft row, activity rail + "See all →" | @@ -133,7 +133,7 @@ Screens are grouped by layout so each batch composes the same primitives. `RefundReceiptAmendDialog`, `payments/PaymentDocuments.tsx`). Gate: `view_finance` or `record_payment` (`forRecording` mode = record-only actor). - **Job:** see what money is owed, record cash/bank receipts, waive or refund, find a receipt. -- **Yuyu:** org2 (stats + tabs + card rows), mgr1 ("At a glance"), mgr2 (Undo). +- **Reference:** org2 (stats + tabs + card rows), mgr1 ("At a glance"), mgr2 (Undo). - **Layout:** `ListLayout`. - `title` "Payments", `subtitle` "Money owed to {space}, and how it was paid." - `actions`: primary **none by default** (nothing is created here). Outline secondary: "Receipts & @@ -189,7 +189,7 @@ Money owed to Fab Lab, and how it was paid. ### A2. Ledger — tab `ledger` - **Route** `/admin/ledger` · `panels/Ledger.tsx`, `LedgerParts.tsx`. Gate `view_inventory`. - **Job:** answer "what is out right now, with whom, and what is late?" -- **Yuyu:** org2 (stats + list), mgr1 (latest list). +- **Reference:** org2 (stats + list), mgr1 (latest list). - **Layout:** `ListLayout`. - `title` "Ledger", `subtitle` "Everything that's out right now." - `actions`: no primary (read-only). More: Export CSV, Export Excel. Superadmin: a toggle chip "All @@ -221,7 +221,7 @@ Everything that's out right now. - **Route** `/admin/transfers` · `panels/StockTransferPanel.tsx`, `StockTransferTable.tsx`. Gate `edit_inventory` or superadmin. - **Job:** move stock between containers or makerspaces, with a reason, and see the history. -- **Yuyu:** org2 list + org3 (form in its own surface, not above the list). +- **Reference:** org2 list + org3 (form in its own surface, not above the list). - **Layout:** `ListLayout` for history. The create form moves out of the page top into a **`FlowLayout` dialog** "New transfer" (two steps: *From and to* → *What's moving*, with Reason on step 2). - `title` "Transfers", `subtitle` "Stock moved between boxes and spaces." @@ -242,7 +242,7 @@ Everything that's out right now. - **Route** `/admin/stocktake` · `panels/StocktakeSessionPanel.tsx`, `StocktakeCountSection.tsx`, `StocktakeDiscipline.tsx`. Gate `edit_inventory`; policy needs `canConfigure`. - **Job:** start a count, count items (by scan), complete, approve, apply adjustments. -- **Yuyu:** org2 (session list with status), mgr4 (detail with sections), mgr2 (scan + recent with Undo). +- **Reference:** org2 (session list with status), mgr4 (detail with sections), mgr2 (scan + recent with Undo). - **Split:** today one page holds policy, schedules, a create form, every session and an inline count table. Split into: 1. **Sessions list** — `ListLayout`. @@ -266,7 +266,7 @@ Everything that's out right now. `ProcurementReceipts.tsx`, `ProcurementMoveModal.tsx`, `ReorderSuggestions.tsx`. Gate `canUseToBuy`; kind chooser for `manage_makerspace`. - **Job:** keep a shopping list, track it from requested to received, then move it into inventory. -- **Yuyu:** org2 (tabs by lifecycle, rows with inline next-step action). +- **Reference:** org2 (tabs by lifecycle, rows with inline next-step action). - **Layout:** `ListLayout`. - `title` "To buy", `subtitle` "What the space needs, from request to shelf." - `actions`: primary **"Add item"** (dialog: Name, Qty, Link, Est. unit cost, Machine type when @@ -292,7 +292,7 @@ Everything that's out right now. ### A6. To-be-fixed shelf — tab `needsfix` - **Route** `/admin/to-be-fixed` · `panels/NeedsFixShelf.tsx`. Gate `edit_inventory`. - **Job:** repair or scrap units that came back broken. -- **Yuyu:** org2 rows with an inline action, mgr2 Undo. +- **Reference:** org2 rows with an inline action, mgr2 Undo. - **Layout:** `ListLayout`. - `title` "To be fixed", `subtitle` "Units returned broken. Repair them back into stock, or scrap them." - `actions`: none (items arrive from returns). @@ -309,7 +309,7 @@ Everything that's out right now. - **Route** `/admin/notifications` · `panels/NotificationInbox.tsx` → `notifications/PersonalInbox.tsx`, `staff/BroadcastComposer.tsx`. Gate any staff action. - **Job:** catch up on what happened that needs me; managers can also send a member announcement. -- **Yuyu:** org1/org2 Recent activity rail (this page is its "See all →" destination). +- **Reference:** org1/org2 Recent activity rail (this page is its "See all →" destination). - **Layout:** `ListLayout`. - `title` "Notifications", `subtitle` "{n} unread" (live; keep the unread count in an `aria-live` region and in the sidebar item's accessible name, as pinned today). @@ -320,7 +320,7 @@ Everything that's out right now. - `tabs`: Unread {n} · All. - `toolbar`: Family chips (Hardware, Printing, Events, Bookings, Maintenance, Members, Payments, Stock, Certifications, Devices, API access). Raw keys become labels. - - `list`: activity-style rows (Yuyu rail, full width). Avatar/icon tile by family · **bold title** + + - `list`: activity-style rows (activity rail, full width). Avatar/icon tile by family · **bold title** + summary (2 lines, clamp) · relative time ("2 min ago") · unread dot. Inline action "Open" when there is a safe link. Row click marks it read. Unread rows get a teal left rule, not a teal border box. - Date group headers: Today · Yesterday · Earlier this week · Older. @@ -334,7 +334,7 @@ Everything that's out right now. in this revamp.) - **Job:** check that alerts and emails went out, retry failures, send a test, manage suppressed addresses. -- **Yuyu:** org2 (tabs + rows + inline action), org6 (developer-ish surface kept calm). +- **Reference:** org2 (tabs + rows + inline action), org6 (developer-ish surface kept calm). - **Layout:** `ListLayout` with two tabs that split today's stacked sections. - `title` "Delivery logs", `subtitle` "Every email and alert this space sent, and whether it arrived." - `actions`: primary **"Send test"**. It opens a small dialog "Send a test to…" (Email to my account / @@ -354,7 +354,7 @@ Everything that's out right now. ### A9. Accountability — tab `accountability` - **Route** `/admin/accountability` · `panels/AccountabilityPanel.tsx`. Gate `view_audit`. - **Job:** follow up on overdue loans and reported problems, and restrict or restore repeat offenders. -- **Yuyu:** org2 (stats + tabs), mgr1 ("Latest …" lists). +- **Reference:** org2 (stats + tabs), mgr1 ("Latest …" lists). - **Layout:** `ListLayout` with tabs that replace the four stacked `Panel`s and the "Show all accountability sections" button. The existing `section` list filter becomes the tab key. - `title` "Accountability", `subtitle` "Late, lost or damaged — and who it's tied to." @@ -374,7 +374,7 @@ Everything that's out right now. - **Route** `/admin/warranty` · `panels/WarrantyPanel.tsx` (+ `WarrantySection`, `WarrantyStatusBadge`). Gate `edit_inventory` or printing/machines. - **Job:** see what's covered, what's expiring, and what's missing documents. -- **Yuyu:** org2. +- **Reference:** org2. - **Layout:** `ListLayout`. - `title` "Warranties", `subtitle` "Coverage for {scope} in {space}." - `stats`: Active · Expiring soon · Expired · Missing docs. Each is `[+query]` filtered count. If that @@ -390,7 +390,7 @@ Everything that's out right now. - **Route** `/admin/organized-events` · `panels/OrganizedEventsPanel.tsx` (+ `EventDrawer`). Gate: non-superadmin, not single-tenant. - **Job:** an organization organizer sees the events they run across venues. -- **Yuyu:** org2 exactly (date-chip rows, going/capacity progress). +- **Reference:** org2 exactly (date-chip rows, going/capacity progress). - **Layout:** `ListLayout`. - `title` "Organized events", `subtitle` "Events you run with your organization, across spaces." - `tabs`: Upcoming · Past (client split by date on the loaded page, or a query param if the API @@ -405,7 +405,7 @@ Everything that's out right now. - **Route** `/admin/member-cards` · `panels/MemberCardsPanel.tsx`, `memberCards/*`. Gate member-card actions; module `membership`. - **Job:** issue, print, reissue and revoke member cards; scan a card at the desk. -- **Yuyu:** org4 (invite card on top + table), mgr2 (scan block). +- **Reference:** org4 (invite card on top + table), mgr2 (scan block). - **Split:** today one long page holds the cards table, a scan box, an issue roster and the card layout form. Use **tabs**: Cards · Issue · Scan · Card layout. - **Layout:** `ListLayout`. @@ -426,7 +426,7 @@ Everything that's out right now. - **Route** `/admin/devices` · `panels/DevicesPanel.tsx`, `DeviceIdentitySection.tsx`, `DeviceEnrollmentModal.tsx`. Gate `manage_devices`; module `devices`. - **Job:** register printers and label printers, see which are offline, enrol or revoke them. -- **Yuyu:** org4 (people table → device table), org6 (one-time secret handling). +- **Reference:** org4 (people table → device table), org6 (one-time secret handling). - **Layout:** `ListLayout` + detail drawer. - `title` "Devices", `subtitle` "Printers and other hardware that talk to Space Works." - `actions`: primary **"Add device"** (dialog: Name, Kind (Generic / Printer / Label printer), then @@ -445,7 +445,7 @@ Everything that's out right now. ### A14. Data export — tab `exports` - **Route** `/admin/data-export` · `features/staff/DataExportsPanel.tsx`. Gate `manage_makerspace`. - **Job:** get a readable archive of this space's records. -- **Yuyu:** org6 (warning strip + clients list), auth7 (callout with action). +- **Reference:** org6 (warning strip + clients list), auth7 (callout with action). - **Layout:** `ListLayout`. - `title` "Data export", `subtitle` "A readable copy of your space's records. Not a backup." - `actions`: primary **"Request export"** (disabled while one is running; tooltip "An export is already @@ -462,7 +462,7 @@ Everything that's out right now. ### A15. Categories — tab `categories` - **Route** `/admin/categories` · `panels/Categories.tsx`. Gate `edit_inventory`. - **Job:** organise products into categories. -- **Yuyu:** org4 (simple table) + org3 (create in a focused surface). +- **Reference:** org4 (simple table) + org3 (create in a focused surface). - **Layout:** `ListLayout`. - `title` "Categories", `subtitle` "How items are grouped in the catalogue." - `actions`: primary **"Add category"**. A dialog replaces the always-visible 5-field inline row: Name, @@ -475,7 +475,7 @@ Everything that's out right now. ### A16. Containers — tab `containers` - **Route** `/admin/containers` · `panels/ContainersPanel.tsx`. Gate `manage_qr`; module `containers`. - **Job:** find a box, see what's in it and its scan history, edit its label or location. -- **Yuyu:** org2 list → mgr1 detail with tabs. +- **Reference:** org2 list → mgr1 detail with tabs. - **Layout:** `ListLayout`; each container opens a **detail drawer** with tabs (B3), replacing the inline Edit / Contents / History toggles inside each card. - `title` "Containers", `subtitle` "Boxes, bins and shelves with QR labels." @@ -490,7 +490,7 @@ Everything that's out right now. - **Route** `/admin/handover` · `panels/machine/HandoverConsole.tsx`. Gate `collect_service_request` or `manage_machines`; module `machine_service`. - **Job:** at the desk, hand finished jobs to the people who requested them. -- **Yuyu:** mgr2 "Recent check-ins" rows with an inline action; org2 rows. +- **Reference:** mgr2 "Recent check-ins" rows with an inline action; org2 rows. - **Layout:** `ListLayout`. - `title` "Ready to collect" (today the panel title is "Awaiting collection"; the tab label "Job handover" stays in the nav), `subtitle` "Finished jobs waiting for their owners." @@ -510,7 +510,7 @@ Everything that's out right now. - **Route** `/admin/email-templates` · `panels/EmailTemplatesPanel.tsx`. Gate inventory, printing or devices. - **Job:** find the email to customise. -- **Yuyu:** org2 list → mgr1/mgr3 detail. +- **Reference:** org2 list → mgr1/mgr3 detail. - **Layout:** `ListLayout` index, then `DetailLayout` editor (B1). This replaces the 18rem left button column plus an always-mounted editor. - `title` "Email templates", `subtitle` "What people receive from {space}, in your words." @@ -527,7 +527,7 @@ Everything that's out right now. ### B1. Email template editor - **Route** `/admin/email-templates?template=…` · `panels/EmailTemplateEditor.tsx`, `EmailTemplateTypeSelector.tsx`. -- **Yuyu:** mgr1 (back + title + tabs), mgr3 (segmented control), org5 (Discard/Save). +- **Reference:** mgr1 (back + title + tabs), mgr3 (segmented control), org5 (Discard/Save). - **Layout:** `DetailLayout`. - `back` "← All templates", `title` template label, `status` pill "Edited" / "Default", `meta` "{stream} · to {audience}". @@ -544,7 +544,7 @@ Everything that's out right now. ### B2. Stocktake session (count) - **Route** `/admin/stocktake?session={id}` · `StocktakeCountSection.tsx`. -- **Yuyu:** mgr2 (scan + find + recent with Undo), mgr1 header. +- **Reference:** mgr2 (scan + find + recent with Undo), mgr1 header. - **Layout:** `DetailLayout`. - `back` "← All counts", `identity` chip "#{id}", `title` notes or "Count of {container}", `status` pill, `meta` "Started {date} · {blind ? 'Blind count' : 'Expected shown'}". @@ -569,8 +569,8 @@ Everything that's out right now. ### B5. Machine service console / printer service console (tab body) - **Where:** inside the core machine-type page (`panels/machine/MachineTypePage.tsx`; the page frame is - owned by `ux-yuyu-spec.md`). Components `MachineServiceConsole.tsx`, `PrinterServiceConsole.tsx`. -- **Yuyu:** org2 tabs + rows, mgr2 inline actions. + owned by `ux-spec.md`). Components `MachineServiceConsole.tsx`, `PrinterServiceConsole.tsx`. +- **Reference:** org2 tabs + rows, mgr2 inline actions. - **Slot fill:** the type page's `DetailLayout` gets a **"Queue"** tab (and "Manual usage" and, for printers, "Printer reports" tabs) instead of stacked `Subsection`s. - Queue tab: sub-tabs Pending · Accepted · Printing (in progress) · Ready (completed) · Failed, each with @@ -593,7 +593,7 @@ Everything that's out right now. `ApiClientListCard.tsx`, `ApiClientScopePicker.tsx`, `ApiClientsAccessSummary.tsx`, `ApiClientsTelegramSettings.tsx`. - **Job:** let your own tools read and write this space without sharing a login; or request access. -- **Yuyu:** **org6 directly.** +- **Reference:** **org6 directly.** - **Layout:** `SettingsLayout` with an org6 body (cards, not label-left rows, because this is a create surface). - `title` "API access", `subtitle` "Let your own tools work with {space}, without sharing anyone's @@ -618,7 +618,7 @@ Everything that's out right now. - **Route** `/admin/modules` · `ModulesPanel.tsx` (superadmin). `SpacePacksPanel` is mounted inside Makerspace settings today. - **Job:** switch whole feature families on or off for a space. -- **Yuyu:** org5 (rows + helper text), org6 (accordion reference). +- **Reference:** org5 (rows + helper text), org6 (accordion reference). - **Layout:** `SettingsLayout`. - `title` "Modules", `subtitle` "Switch features on for {space}. Turning one off keeps its data; turning it back on restores everything." @@ -638,7 +638,7 @@ Everything that's out right now. - **Route** `/admin/platform` · `PlatformEmailPanel.tsx`, `PlatformSocialAuthPanel.tsx`, `PlatformStripeConnectPanel.tsx` (stacked today). - **Job:** set deployment-wide email, sign-in providers and Stripe Connect. -- **Yuyu:** org5. +- **Reference:** org5. - **Layout:** `SettingsLayout` with `subnav`: **Email** · **Sign-in providers** · **Stripe Connect** (the last only when `platform_hosting`). `?section=` query. - Email: rows SMTP host / port / username / password ("Saved — leave blank to keep"), From email, @@ -655,7 +655,7 @@ Everything that's out right now. - **Route** `/admin/backup-restore` · `BackupRestorePanel.tsx` (+ `RestoreDecision`). Gate `manage_makerspace`; deployment part superadmin. - **Job:** make and download backups; superadmins schedule full backups and restore. -- **Yuyu:** org5 (rows + danger zone), org2 (archive list). +- **Reference:** org5 (rows + danger zone), org2 (archive list). - **Layout:** `SettingsLayout` with `subnav`: **This space** · **Deployment** (superadmin). - This space: yellow callout "**Backups contain live member data.** Store downloads somewhere private." · primary **"Create backup"** · archive list rows (scope · date · size · status pill · @@ -676,7 +676,7 @@ Everything that's out right now. (`OrganizationCreate`, `OrganizationLinks`), `OrganizationProfileForm.tsx`, `OrganizationMemberships.tsx`, `OrganizationInvitations.tsx`. - **Job:** run an organization: its public profile, people, invitations and (superadmin) linked spaces. -- **Yuyu:** **organiser workspace boards directly** — org1 (org cards), org3 (create with preview), org4 +- **Reference:** **organiser workspace boards directly** — org1 (org cards), org3 (create with preview), org4 (members + invite card), org5 (settings + danger zone). - **Layout:** two levels. 1. **Index** — `DashboardLayout`-style card grid (org1 "Your organisations"): one card per org @@ -705,7 +705,7 @@ Everything that's out right now. - **Route** `features/members/MemberAccountPage.tsx`, `MemberAccountProfile.tsx`, `MemberAccountSessions.tsx`, `MemberAccountMembership.tsx`, `MemberAccountExports.tsx`. - **Job:** manage my profile, sign-in security, membership (cancel renewal / leave) and data export. -- **Yuyu:** **auth6 + auth7 directly.** +- **Reference:** **auth6 + auth7 directly.** - **Layout:** member `AppShell` + `SettingsLayout`. - `title` "Account", `subtitle` "Your personal profile and sign-in security." (replaces "Your account" + "Back to membership" button; the back link becomes the shell's nav). @@ -733,7 +733,7 @@ Everything that's out right now. ### C7. Staff/member MFA management — `/mfa` - **Route** `features/auth/MfaAccountPage.tsx` (standalone route; reused by members via "Manage two-step sign-in"). -- **Yuyu:** auth7 (security tab), auth2 (centred card). +- **Reference:** auth7 (security tab), auth2 (centred card). - **Layout:** `CenteredCardLayout` (this route renders outside any shell, for staff and members alike). - Not signed in: icon tile (shield) · "Two-step sign-in" · "Sign in to manage your authenticator." · two links "Staff sign-in" / "Member sign-in". @@ -753,7 +753,7 @@ Everything that's out right now. ### D1. Platform dashboard — tab `platform-dashboard` (global, superadmin) - **Route** `/admin/platform-dashboard` · `PlatformDashboardPanel.tsx`. - **Job:** "is the deployment healthy, and what needs me?" -- **Yuyu:** org1/org2 (greeting, stat cards, callouts, list + rail). +- **Reference:** org1/org2 (greeting, stat cards, callouts, list + rail). - **Layout:** `DashboardLayout`. - `greeting` "Checked {relative time}", `title` "Platform", `actions`: outline **"Run checks now"** (replaces "Refresh dashboard and checks"; there is no primary). @@ -772,7 +772,7 @@ Everything that's out right now. ### D2. Usage & limits — tab `usage` - **Route** `/admin/usage` · `TenantUsagePanel.tsx` (`UsageTable` is shared with D1). - **Job:** "how close is this space to its limits?" -- **Yuyu:** mgr2 progress stat card. +- **Reference:** mgr2 progress stat card. - **Layout:** `DashboardLayout`. - `title` "Usage & limits", `greeting` "Counters reset with each quota period.", `actions` outline "Refresh". @@ -789,7 +789,7 @@ Everything that's out right now. ### D3. Organization analytics — tab `organization-analytics` - **Route** `/admin/organization-analytics` · `panels/OrganizationAnalyticsPanel.tsx`. - **Job:** compare an organization's spaces and see its combined total. -- **Yuyu:** org2 stats + list. +- **Reference:** org2 stats + list. - **Layout:** `DashboardLayout`. - `title` "Organization analytics", `greeting` "Your organization's spaces, side by side." - `actions`: none. Controls row under the title: **Organization picker** (a select fed by the existing @@ -804,7 +804,7 @@ Everything that's out right now. - **Route** any `/admin/*` when superadmin has no selection · `MakerspacePicker.tsx` (rendered by `StaffApp` gate). - **Job:** choose which space to operate. -- **Yuyu:** **org1 Home** ("Your organisations" cards). +- **Reference:** **org1 Home** ("Your organisations" cards). - **Layout:** `DashboardLayout`, ideally inside `AppShell` in a **no-workspace mode** (the sidebar shows only global items, Platform dashboard and Organizations, and the switcher card reads "Choose a space"). See gap §11.4. Fallback if the shell cannot render without a workspace: the same body in @@ -824,7 +824,7 @@ Everything that's out right now. ### E1. Bulk import — tab `bulk` - **Route** `/admin/bulk-import` · `panels/BulkImport.tsx`, `BulkImportHelpers.ts`. - **Job:** add or update many items from a spreadsheet, safely (preview first). -- **Yuyu:** org3 (focused form), mgr2 (progress). +- **Reference:** org3 (focused form), mgr2 (progress). - **Layout:** `FlowLayout` (page), steps: 1. **Upload** — a big drop zone (org3 logo drop style): "Drop a CSV or Excel file here, or choose a file" + "or paste a table" (expands the textarea). Advanced JSON is behind "Use JSON instead" @@ -846,7 +846,7 @@ Everything that's out right now. ### E2. QR tools (print batch builder) — tab `qr` - **Route** `/admin/qr-tools` · `panels/QrTools.tsx`, `QrImage.tsx`. Module `qr_print_batches`. - **Job:** build a sheet of QR labels for items, units and boxes, then download them to print. -- **Yuyu:** mgr4 (date cards → batch cards), mgr3 (columns + dashed add). +- **Reference:** mgr4 (date cards → batch cards), mgr3 (columns + dashed add). - **Layout:** `ListLayout` (batches) → `DetailLayout` (a batch) as the working surface. It is not a strict wizard, because users add labels in any order. - Index: `title` "QR labels", `subtitle` "Print labels for items, units and boxes." · primary **"New @@ -863,7 +863,7 @@ Everything that's out right now. ### E3. Create makerspace — inside Users (core page), superadmin - **Route** `/admin/users` (opened from the core Users page) · `usersModals/CreateMakerspaceModal.tsx`, `CreationWizardSteps.tsx`. -- **Yuyu:** org3 (form + live preview). +- **Reference:** org3 (form + live preview). - **Layout:** `FlowLayout`, **page presentation** via `?create=makerspace` (no route change). Today it is a modal with "Step 1 of 5" text. - `stepper`: Identity · Profile & hours · Modules · First staff · Review. @@ -884,7 +884,7 @@ Everything that's out right now. `TenantMigrationExports.tsx`, `TenantMigrationPairings.tsx`, `TenantMigrationImports.tsx`, `TenantMigrationIdentityDecisions.tsx`. - **Job:** move one space from a managed deployment to a self-hosted one, irreversibly, in a safe order. -- **Yuyu:** mgr2's door-station card (strong, staged controls) + org5 danger zone. +- **Reference:** mgr2's door-station card (strong, staged controls) + org5 danger zone. - **Layout:** `FlowLayout` (page) with a **visible, persistent stepper**, because the order is the safety model. Steps (with the side each one runs on): 1. **Review disclosure** (source) — closure summary, identity list, acknowledgement checkbox, @@ -905,7 +905,7 @@ Everything that's out right now. ### E5. Password recovery — `/reset-password` - **Route** `features/staff/ResetPasswordPage.tsx` → `auth/PasswordRecoveryFlow.tsx` (code flow), `auth/LegacyPasswordResetForm.tsx` (uid/token link), `auth/NewPasswordFields.tsx`. -- **Yuyu:** **auth2** (set new password), auth3 (confirm card). +- **Reference:** **auth2** (set new password), auth3 (confirm card). - **Layout:** `CenteredCardLayout` per step (a 3-step flow rendered as a sequence of centred cards, not as `FlowLayout`): 1. Request: icon tile (key) · "Reset your password" · "We'll email you a code." · Email · primary @@ -933,7 +933,7 @@ All of these use the same card: logo top-left, icon tile, title, body, one prima | F2 | **MFA challenge / enrol** | `auth/MfaFlow.tsx` · login returns a challenge | shield · Enrol: "Set up two-step sign-in" + QR (white tile) + "Can't scan? Enter this key" (mono, Copy) · Verify: "Enter your 6-digit code" + "Use a recovery code instead" link · Codes: grid + Copy all / Download | **Verify** / "Back to sign in"; codes: **I've saved my codes** · footnote "This sign-in expires in {n} minutes." | | F3 | **Access denied (single-tenant)** | `staff/StaffAccessDenied.tsx` | lock · "You don't have access to {space}" · "This console is only for {space}'s team. Sign in with an account that's a member here." | **Sign out** / theme toggle moves to footer | | F4 | **Recovery quarantine** (superadmin) | `staff/RecoveryQuarantinePanel.tsx` | danger tile · eyebrow "Recovery quarantine" · "Staff access is paused" · reason · residual risk in a bordered danger box · confirm checkbox | **Lift quarantine** (danger pill, disabled until ticked) | -| F5 | **Org invitation redeem** | `organizations/OrganizationInvitationRedeemPage.tsx` · `/organization-invitations/redeem/:token` | **auth4**: overlapping avatar tiles (org + you) · "You've been invited to join" · org name (if known) · scope pill · "This link works once." · Not signed in: Username or email + Password + **Sign in and continue** · Redeemed: success state "You're in" + **Open organizations** | footnote "Signed in as {user} · Switch account". **Owner decision:** today redemption fires automatically once signed in. Yuyu asks for an explicit **Accept invite** click. Recommended (frontend-only: call the mutation on click), but it is a behaviour change. | +| F5 | **Org invitation redeem** | `organizations/OrganizationInvitationRedeemPage.tsx` · `/organization-invitations/redeem/:token` | **auth4**: overlapping avatar tiles (org + you) · "You've been invited to join" · org name (if known) · scope pill · "This link works once." · Not signed in: Username or email + Password + **Sign in and continue** · Redeemed: success state "You're in" + **Open organizations** | footnote "Signed in as {user} · Switch account". **Owner decision:** today redemption fires automatically once signed in. The UX spec asks for an explicit **Accept invite** click. Recommended (frontend-only: call the mutation on click), but it is a behaviour change. | | F6 | **404** | `AppRoutes.NotFoundPage` | compass · eyebrow "404" · "We couldn't find that page" · "The link may be old, or the page moved." | **Go to home** (`/` or tenant home) / "Staff sign-in" (only on `/admin*`-like paths) | | F7 | **Session / route loading** | `StaffApp.StaffLoading`, `AppRoutes.RouteFallback` | logo + spinner · "Restoring your session…" / "Opening {space}…" | none. Keep these messages in a `role=status` region | | F8 | **No makerspace assigned** | `StaffTabContent` `Panel title="No makerspace"` | empty tile · "No space assigned yet" · "Ask a space manager to add you to their team." | **Sign out** | @@ -950,7 +950,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head `/admin`; keep a footer link "Staff sign-in"). ### G1. Landing directory (central mode) — `/` -- **Component** `features/LandingPage.tsx`. **Yuyu:** org1 (cards), att1 hero tone. +- **Component** `features/LandingPage.tsx`. **Reference:** org1 (cards), att1 hero tone. - **Layout:** `PublicLayout` (deployment-level `PublicShell`: Space Works logo, "About", "Sign in"). - `hero`: eyebrow "Space Works" · **one Unbounded accent line** "Find a space. Borrow the tool." · intro "Public catalogues from community workshops, labs and makerspaces." · big search field "Search @@ -970,7 +970,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head - `content`: two link cards "Browse spaces →" and "Staff sign-in →" (org1 card style). ### G3. Public machines — `/machines`, `/m/:slug/machines` -- **Component** `inventory/PublicMachinesPage.tsx` (+ `MachineIntakeForm`). **Yuyu:** org2 list → card +- **Component** `inventory/PublicMachinesPage.tsx` (+ `MachineIntakeForm`). **Reference:** org2 list → card grid, att1 tone. - **Layout:** `PublicLayout`. - `hero`: eyebrow "Machines" · title "What you can use at {space}" · intro. @@ -985,7 +985,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head ### G4. Public bookings — `/bookings`, `/m/:slug/bookings` - **Component** `bookings/PublicBookingsPage.tsx`, `PublicBookingForm.tsx`, `SpaceDiscoveryCalendar.tsx`, - `discovery/*`. **Yuyu:** att1 (detail + sticky action card). + `discovery/*`. **Reference:** att1 (detail + sticky action card). - **Layout:** `PublicLayout` (list of spaces) → selecting a space opens **`PublicDetailLayout`** in place (`?space={token}`, no new route). - List: `hero` "Book a space at {space}" · `toolbar` = `DiscoveryControls` (search, kind chips, @@ -998,8 +998,8 @@ the "space works" credit in the footer. The "Staff login" buttons in public head - Header nav "My bookings" → member Bookings tab (H2). - **States:** "No bookable spaces yet." · "No spaces match these filters." · not enabled / throttled as G3. -### G5. Public events list — `/events`, `/m/:slug/events` (only if `ux-yuyu-spec.md` leaves it out) -- **Component** `inventory/PublicEventsPage.tsx`. **Yuyu:** org2 rows from the attendee side. +### G5. Public events list — `/events`, `/m/:slug/events` (only if `ux-spec.md` leaves it out) +- **Component** `inventory/PublicEventsPage.tsx`. **Reference:** org2 rows from the attendee side. - **Layout:** `PublicLayout`: `hero` "What's on at {space}" · `toolbar` discovery controls (list / calendar) · **date-chip rows** (title, time · place, price, availability pill "6 spots left" / "Waitlist") → event page (core). @@ -1009,7 +1009,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head - **Component** `printing/PublicPrintRequestPage.tsx`, `PublicPrintRequestForm.tsx`, `PublicPrintRequestParts.tsx` (`StatusResult` stepper), `PublicPrintRequestPanels.tsx`. - **Job:** send a 3D-print job with files, then track it. -- **Yuyu:** att1 (main + sticky card), att9 (confirmation). +- **Reference:** att1 (main + sticky card), att9 (confirmation). - **Layout:** `PublicDetailLayout`. - `hero`: eyebrow "3D printing" · title "Send a print job to {space}". - `main`: the form in three titled groups. **Your print** (Title, Project brief) · **Material** @@ -1026,7 +1026,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head again in a moment." · upload progress: inline "Uploading 2 of 3 files…". ### G7. Public stats — `/stats`, `/m/:slug/stats` -- **Component** `stats/PublicStatsPage.tsx`, `StatsSections.tsx`, `StatsParts.tsx`. **Yuyu:** org2 stat +- **Component** `stats/PublicStatsPage.tsx`, `StatsSections.tsx`, `StatsParts.tsx`. **Reference:** org2 stat cards. - **Layout:** `PublicLayout` with a `DashboardLayout`-style body: `hero` eyebrow "Live stats" · title "{space} in numbers" · "Updated live from the public catalogue." · sections **Printing** (stat cards: @@ -1037,7 +1037,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head fill in as people borrow and print." ### G8. Public organization — `/o/:organizationSlug` -- **Component** `organizations/PublicOrganizationPage.tsx`. **Yuyu:** org3 preview card → full page; +- **Component** `organizations/PublicOrganizationPage.tsx`. **Reference:** org3 preview card → full page; att1. - **Layout:** `PublicLayout`: `hero` avatar tile · org name · description · "Visit website ↗" (outline) · `content` "Upcoming events across spaces": **date-chip rows** (title, "{host space} · time · place", @@ -1047,7 +1047,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head with Try again. ### G9. Event feedback — `/events/:token/feedback` -- **Component** `inventory/PublicEventFeedbackPage.tsx` (+ `forms/CustomFormFields`). **Yuyu:** auth3 +- **Component** `inventory/PublicEventFeedbackPage.tsx` (+ `forms/CustomFormFields`). **Reference:** auth3 (centred, focused), att9. - **Layout:** `PublicLayout`, narrow single column (form) → `CenteredCardLayout` for the end states. - `hero`: eyebrow = event title · title = survey title · helper: anonymous "We don't store who sent @@ -1062,7 +1062,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head - **Component** `inventory/PublicSelfCheckoutPage.tsx`, `PublicEvidenceUpload.tsx`, `PublicToolScanPanel.tsx`, `PublicToolResultCard.tsx`, `KitChecklist`. - **Job:** on a phone at the shelf: borrow or return a QR-tagged tool with photo proof. -- **Yuyu:** att11 (scan + big result), auth3 (one thing per screen). +- **Reference:** att11 (scan + big result), auth3 (one thing per screen). - **Layout:** `PublicLayout` (minimal `PublicShell`), with a **`FlowLayout` body** that is mobile-first. - Top: segmented control **Borrow** · **Return** (replaces "Use (check out)" / "Return" tabs). - Borrow steps: 1 **Photo** ("Take a photo of the tool" camera/upload tile, preview) → 2 **Scan** @@ -1077,7 +1077,7 @@ the "space works" credit in the footer. The "Staff login" buttons in public head inline danger callout on the Confirm step with the server message. ### G11. Archived payments — `/member/archived` -- **Component** `members/ArchivedPayments.tsx`. **Yuyu:** auth6 calm list. +- **Component** `members/ArchivedPayments.tsx`. **Reference:** auth6 calm list. - **Layout:** `PublicLayout` (deployment-level shell) narrow column: `hero` eyebrow "Closed spaces" · "Payments and receipts" · per-space sections (space name header, charge rows with amount + status pill + receipt download). @@ -1088,19 +1088,19 @@ the "space works" credit in the footer. The "Staff login" buttons in public head ## 9. Batch H — Member area tab bodies (member `AppShell`) -The member shell, its nav and the **Overview** tab are in `ux-yuyu-spec.md`. These are the bodies of the +The member shell, its nav and the **Overview** tab are in `ux-spec.md`. These are the bodies of the other `memberTabs()` (gating unchanged). Each tab body uses `ListLayout` slots without the shell header repeating the space name. ### H1. Loans — `/member/loans` - **Component** `MemberActivity.tsx` (section `loans`), `MemberDashboardSections.tsx` - (`MemberHistory`), `MemberLoanRenew.tsx`. **Yuyu:** org2 tabs + rows. + (`MemberHistory`), `MemberLoanRenew.tsx`. **Reference:** org2 tabs + rows. - `title` "Loans" · `tabs` **Out now** · **History** · rows: item, "Due Fri" / "Overdue" pill, inline **"Renew"** when allowed, history rows with returned date. - Empty: "Nothing out right now. Browse the catalogue to borrow something." + "Browse catalogue". ### H2. Bookings — `/member/bookings` -- **Component** `bookings/MyBookings.tsx`, `BookingReschedule.tsx`. **Yuyu:** org2 date-chip rows. +- **Component** `bookings/MyBookings.tsx`, `BookingReschedule.tsx`. **Reference:** org2 date-chip rows. - `title` "Bookings" · `actions` primary **"Book a space"** (→ public bookings) · `tabs` Upcoming · Past (replaces the two `aria-pressed` buttons) · **date-chip rows** (space, time range, status pill) · inline "Move" · `[...]` "Cancel booking…" (inline confirm stays, restyled). @@ -1108,7 +1108,7 @@ repeating the space name. ### H3. Events — `/member/events` (+ partner events) - **Component** `MemberActivity` (events), `MemberCalendarActions`, `MemberEventCancel`, - `MemberEventFeedback`, `PartnerEvents.tsx`. **Yuyu:** att9 ticket, org2 rows. + `MemberEventFeedback`, `PartnerEvents.tsx`. **Reference:** att9 ticket, org2 rows. - `title` "Events" · `tabs` **Going** · **Past** · **Partner events** (only when `PartnerEvents` has rows). - Going rows: date chip · title · time · place · inline "Ticket" (opens an att9 ticket card dialog with QR, "Add to calendar", "Can't make it? Cancel"). @@ -1120,7 +1120,7 @@ repeating the space name. ### H4. Payments — `/member/payments` - **Component** `MemberPayments.tsx`, `MemberDashboardSections` (`MemberDues`), `payments/PaymentDocuments` - (member audience). **Yuyu:** org2 stats + rows. + (member audience). **Reference:** org2 stats + rows. - `title` "Payments" · `stats`: Outstanding (per currency) `[loaded]` · Membership fee `[loaded]` · rows: what, due, amount, status pill, inline **"Pay now"** (online rail) or muted "Pay at the desk" · secondary outline header action "Receipts" (drawer with documents). @@ -1128,7 +1128,7 @@ repeating the space name. ### H5. Inbox — `/member/inbox` - **Component** `notifications/MemberNotifications.tsx`, `AnnouncementFeed.tsx`, `PreferenceCentre.tsx`, - member notices. **Yuyu:** activity rail. + member notices. **Reference:** activity rail. - `title` "Inbox" · `tabs` **Notices** · **Announcements** · **Preferences** (Preferences uses `SettingsLayout` rows: channel switches, digest frequency, Save). - Empty: "No notices need your attention." / "No announcements yet." @@ -1164,7 +1164,7 @@ repeating the space name. ### I1. Event check-in station — `/event-check-in/:stationToken`, `/m/:slug/event-check-in/:stationToken` - **Component** `events/EventCheckInStationPage.tsx` + `staff/OfflineCheckInOperator.tsx` (`eventCheckInOfflineStore`). -- **Yuyu:** **att10 (PIN pad) + att11 (door scanner) directly.** +- **Reference:** **att10 (PIN pad) + att11 (door scanner) directly.** - **Layout:** `KioskLayout`, two states: 1. **Locked:** `stage` left = space avatar tile, "{event} · Door check-in", big title "Enter the station PIN", body "Ask an organiser for the 8-digit PIN. It unlocks check-in on this device only.", **8 PIN @@ -1194,7 +1194,7 @@ repeating the space name. ### I3. Scanner page — `/scanner` - **Component** `kiosk/ScannerPage.tsx`. - **Job:** a staff device that only resolves QR codes. -- **Yuyu:** att11. +- **Reference:** att11. - **Layout:** `KioskLayout`: `station bar` "Scanner" + signed-in staff chip · `stage` = **ScanWorkbench** (§11.2: camera + paste field + Resolve) · `side` = **resolved-object card** (the same card as the staff Scanner tab: "Box B-04 · 12 items", "Asset · Multimeter #3 · Out", allowed actions as labelled @@ -1280,7 +1280,7 @@ No screen in this doc needs a new page layout. These are the places where the cl ### J3. Setup checklist - **Component** `staff/SetupChecklist.tsx` (`setupChecklistState.ts`). Shown on Dashboard to space managers. -- **Change (coordinate with the core Dashboard spec):** a **Yuyu callout card** at the top of Home: +- **Change (coordinate with the core Dashboard spec):** a **callout card** (P5) at the top of Home: "**Finish setting up {space}** — 3 of 5 done" + progress bar + the next undone step as the action link ("Set opening hours →"). It expands to the full list (Done ✓ / To do rows linking to their tabs) and a "Hide" control that persists per browser (unchanged). @@ -1310,7 +1310,7 @@ No screen in this doc needs a new page layout. These are the places where the cl | Accountability (4 stacked panels) | → tabs | — | — | | Member cards (4 stacked sections) | → tabs | — | — | | QR tools (3 stacked forms) | → batch list + batch detail with segmented "Add labels" | — | — | -| Organizations select + pager | → org cards index + org settings sub-nav | Yuyu organiser workspace | — | +| Organizations select + pager | → org cards index + org settings sub-nav | reference organiser workspace | — | | Dead code | `panels/EmailLogPanel.tsx` (exported, not routed) | — | separate cleanup | --- @@ -1325,37 +1325,37 @@ No screen in this doc needs a new page layout. These are the places where the cl |---|---|---|---|---|---| | `platform-dashboard` | Platform dashboard | `PlatformDashboardPanel` | longtail | D1 | DashboardLayout | | `usage` | Usage & limits | `TenantUsagePanel` | longtail | D2 | DashboardLayout | -| `dashboard` | Dashboard | `panels/DashboardPanel` | ux-yuyu-spec | Staff Home | DashboardLayout | +| `dashboard` | Dashboard | `panels/DashboardPanel` | ux-spec | Staff Home | DashboardLayout | | `notifications` | Notifications | `panels/NotificationInbox` | longtail | A7 | ListLayout | -| `requests` | Requests | `panels/RequestsPanel` | ux-yuyu-spec | Requests | ListLayout / DetailLayout | -| `direct` | Direct handout | `DirectLoans` | ux-yuyu-spec | Direct handout | (core) | +| `requests` | Requests | `panels/RequestsPanel` | ux-spec | Requests | ListLayout / DetailLayout | +| `direct` | Direct handout | `DirectLoans` | ux-spec | Direct handout | (core) | | `handover` | Job handover | `panels/machine/HandoverConsole` | longtail | A17 | ListLayout | -| `inventory` | Inventory | `panels/Inventory` | ux-yuyu-spec | Inventory | ListLayout / DetailLayout | +| `inventory` | Inventory | `panels/Inventory` | ux-spec | Inventory | ListLayout / DetailLayout | | `needsfix` | To-be-fixed | `panels/NeedsFixShelf` | longtail | A6 | ListLayout | | `categories` | Categories | `panels/Categories` | longtail | A15 | ListLayout | -| `machines` | Machines | `panels/MachinesPanel` | ux-yuyu-spec (+ longtail B5 for service-console tab bodies) | Machines | (core) DetailLayout | -| `events` | Events | `EventsPanel` | ux-yuyu-spec (+ longtail §13 note: collaboration inbox → callout) | Events | (core) | +| `machines` | Machines | `panels/MachinesPanel` | ux-spec (+ longtail B5 for service-console tab bodies) | Machines | (core) DetailLayout | +| `events` | Events | `EventsPanel` | ux-spec (+ longtail §13 note: collaboration inbox → callout) | Events | (core) | | `organized` | Organized events | `panels/OrganizedEventsPanel` | longtail | A11 | ListLayout | -| `bookings` | Bookings | `BookingsPanel` | ux-yuyu-spec | Bookings | (core) | -| `members` | Members | `MembersPanel` | ux-yuyu-spec | Members | (core) | +| `bookings` | Bookings | `BookingsPanel` | ux-spec | Bookings | (core) | +| `members` | Members | `MembersPanel` | ux-spec | Members | (core) | | `cards` | Member cards | `panels/MemberCardsPanel` | longtail | A12 | ListLayout | | `tobuy` | To Buy | `panels/ProcurementPanel` | longtail | A5 | ListLayout | | `transfers` | Transfers | `panels/StockTransferPanel` | longtail | A3 | ListLayout + FlowLayout(dialog) | | `stocktake` | Stocktake | `panels/StocktakeSessionPanel` | longtail | A4, B2 | ListLayout / DetailLayout | | `containers` | Containers | `panels/ContainersPanel` | longtail | A16, B3 | ListLayout / DetailLayout(drawer) | | `ledger` | Ledger | `panels/Ledger` | longtail | A2 | ListLayout | -| `reports` | Reports | `panels/OperationsReports` | ux-yuyu-spec | Reports | (core) | +| `reports` | Reports | `panels/OperationsReports` | ux-spec | Reports | (core) | | `accountability` | Accountability | `panels/AccountabilityPanel` | longtail | A9 | ListLayout | | `warranty` | Warranties | `panels/WarrantyPanel` | longtail | A10 | ListLayout | | `bulk` | Bulk import | `panels/BulkImport` | longtail | E1 | FlowLayout | | `qr` | QR Tools | `panels/QrTools` | longtail | E2 | ListLayout / DetailLayout | | `scanner` | Scanner | `panels/ScannerPanel` | longtail | §10 "Staff Scanner tab" | ListLayout + ScanWorkbench | | `api` | API access | `ApiClientsPanel` | longtail | C1 | SettingsLayout | -| `settings` | Settings | `MakerspaceSettingsPanel` | ux-yuyu-spec | Makerspace settings | (core) SettingsLayout | +| `settings` | Settings | `MakerspaceSettingsPanel` | ux-spec | Makerspace settings | (core) SettingsLayout | | `emailtemplates` | Email templates | `panels/EmailTemplatesPanel` | longtail | A18, B1 | ListLayout / DetailLayout | -| `users` | Users | `panels/Users` | ux-yuyu-spec (+ longtail E3 for Create makerspace) | Staff & roles | (core) | +| `users` | Users | `panels/Users` | ux-spec (+ longtail E3 for Create makerspace) | Staff & roles | (core) | | `platform` | Platform settings | `PlatformEmail/SocialAuth/StripeConnect` | longtail | C3 | SettingsLayout | -| `audit` | Audit log | `panels/AuditLog` | ux-yuyu-spec | Audit log | (core) | +| `audit` | Audit log | `panels/AuditLog` | ux-spec | Audit log | (core) | | `devices` | Devices | `panels/DevicesPanel` | longtail | A13 | ListLayout | | `email-logs` | Delivery logs | `panels/DeliveryLogPanel` | longtail | A8 | ListLayout | | `payments` | Payments | `PaymentsPanel` | longtail | A1 | ListLayout | @@ -1366,47 +1366,47 @@ No screen in this doc needs a new page layout. These are the places where the cl | `backups` | Backup & restore | `BackupRestorePanel` | longtail | C4 | SettingsLayout | | `migration` | Tenant migration | `TenantMigrationPanel` | longtail | E4 | FlowLayout | -Totals: 12 tabs in `ux-yuyu-spec.md`, 30 in this doc, 42 in all. +Totals: 12 tabs in `ux-spec.md`, 30 in this doc, 42 in all. ### 14.2 Routes (`AppRoutes.tsx`, both modes) | Route (single · central) | Component | Spec | Section | Layout | |---|---|---|---|---| -| `/` · `/m/:slug` → `FamilyHome` → inventory/events/bookings/machines/membership | per `publicHome()` | ux-yuyu-spec (catalogue, event, member home), longtail (machines G3, bookings G4) | — | PublicLayout | +| `/` · `/m/:slug` → `FamilyHome` → inventory/events/bookings/machines/membership | per `publicHome()` | ux-spec (catalogue, event, member home), longtail (machines G3, bookings G4) | — | PublicLayout | | `FamilyHome` error / "No public programme" | `AppRoutes.FamilyHome` | longtail | F10 | PublicLayout | | `/` (central) | `LandingPage` | longtail | G1 | PublicLayout | | `/about` | `AboutPage` | longtail | G2 | PublicLayout | | `/o/:organizationSlug` | `PublicOrganizationPage` | longtail | G8 | PublicLayout | -| `/items/:itemId` · `/m/:slug/items/:itemId` | `PublicItemPage` | ux-yuyu-spec | Public item | PublicDetailLayout | +| `/items/:itemId` · `/m/:slug/items/:itemId` | `PublicItemPage` | ux-spec | Public item | PublicDetailLayout | | `/checkout` · `/m/:slug/checkout` | `PublicSelfCheckoutPage` | longtail | G10 | PublicLayout + FlowLayout body | -| `/events` · `/m/:slug/events` | `PublicEventsPage` | ux-yuyu-spec if covered, else longtail | G5 | PublicLayout | -| `/events/:publicToken` · `/m/:slug/events/:publicToken` | `PublicEventDetailPage` | ux-yuyu-spec | Public event page | PublicDetailLayout | +| `/events` · `/m/:slug/events` | `PublicEventsPage` | ux-spec if covered, else longtail | G5 | PublicLayout | +| `/events/:publicToken` · `/m/:slug/events/:publicToken` | `PublicEventDetailPage` | ux-spec | Public event page | PublicDetailLayout | | `/events/:publicToken/feedback` · `/m/:slug/…/feedback` | `PublicEventFeedbackPage` | longtail | G9 | PublicLayout / CenteredCardLayout | | `/event-check-in/:stationToken` · `/m/:slug/event-check-in/:stationToken` | `EventCheckInStationPage` | longtail | I1 | KioskLayout | | `/machines` · `/m/:slug/machines` | `PublicMachinesPage` | longtail | G3 | PublicLayout | | `/bookings` · `/m/:slug/bookings` | `PublicBookingsPage` | longtail | G4 | PublicLayout / PublicDetailLayout | | `/print` · `/m/:slug/print` | `PublicPrintRequestPage` | longtail | G6 | PublicDetailLayout | -| `/member` · `/m/:slug/member` (overview, sign-in) | `MemberArea` | ux-yuyu-spec | Member home / Member sign-in | AppShell / AuthLayout | +| `/member` · `/m/:slug/member` (overview, sign-in) | `MemberArea` | ux-spec | Member home / Member sign-in | AppShell / AuthLayout | | `/member/:memberTab` · `/m/:slug/member/:memberTab` — `loans` | `MemberArea` | longtail | H1 | AppShell + ListLayout | | — `bookings` | `MemberArea` → `MyBookings` | longtail | H2 | AppShell + ListLayout | | — `events` | `MemberArea` → activity + `PartnerEvents` | longtail | H3 | AppShell + ListLayout | | — `payments` | `MemberArea` → `MemberPayments` | longtail | H4 | AppShell + ListLayout | | — `inbox` | `MemberArea` → notifications | longtail | H5 | AppShell + ListLayout / SettingsLayout | | — `account-portal` | `MemberArea` → profile, card, directory, referrals | longtail | H6 | AppShell + SettingsLayout | -| — `overview` | `MemberArea` | ux-yuyu-spec | Member home | AppShell + DashboardLayout | +| — `overview` | `MemberArea` | ux-spec | Member home | AppShell + DashboardLayout | | `/member/account` · `/m/:slug/member/account` | `MemberAccountPage` | longtail | C6 | AppShell + SettingsLayout | | `/member/archived` | `ArchivedPayments` | longtail | G11 | PublicLayout | | `/stats` · `/m/:slug/stats` | `PublicStatsPage` | longtail | G7 | PublicLayout | | `/reset-password` | `ResetPasswordPage` | longtail | E5 | CenteredCardLayout | | `/mfa` | `MfaAccountPage` | longtail | C7 | CenteredCardLayout | -| `/staff-invitations/accept` | `StaffInvitationAcceptPage` | ux-yuyu-spec | Invitation accept | CenteredCardLayout | +| `/staff-invitations/accept` | `StaffInvitationAcceptPage` | ux-spec | Invitation accept | CenteredCardLayout | | `/organization-invitations/redeem/:token` | `OrganizationInvitationRedeemPage` | longtail | F5 | CenteredCardLayout | -| `/admin/*` · `/m/:slug/admin/*` (sign-in) | `StaffApp` → `LoginPanel` | ux-yuyu-spec | Staff sign-in | AuthLayout | +| `/admin/*` · `/m/:slug/admin/*` (sign-in) | `StaffApp` → `LoginPanel` | ux-spec | Staff sign-in | AuthLayout | | `/admin/*` gates: MFA, change password, quarantine, access denied, picker, loading | `StaffApp` | longtail | F1–F4, F7, D4 | CenteredCardLayout / DashboardLayout | | `/admin/{tab}` | `StaffWorkspace` → tab | per 14.1 | — | AppShell + per 14.1 | | `/guest-admin/*` | `StaffApp guestOnly` | same as `/admin/*` (narrow tab set, same layouts) | — | — | | `/scanner` | `ScannerPage` | longtail | I3 | KioskLayout | -| `/kiosk/:stationToken` · `/m/:slug/kiosk/:stationToken` | `KioskStationPage` | ux-yuyu-spec | Kiosk station | KioskLayout | +| `/kiosk/:stationToken` · `/m/:slug/kiosk/:stationToken` | `KioskStationPage` | ux-spec | Kiosk station | KioskLayout | | `/kiosk/:slug` (central) | `PublicDeskKioskPage` | longtail | I2 | KioskLayout | | `/superadmin` (central) | `SuperadminPage` → `StaffApp` | same as `/admin/*` (D4 picker) | D4 | — | | `*` | `NotFoundPage` | longtail | F6 | CenteredCardLayout | @@ -1424,8 +1424,8 @@ Totals: 12 tabs in `ux-yuyu-spec.md`, 30 in this doc, 42 in all. | Panel error boundary | `StaffPanelErrorBoundary` | longtail | F9 | | Payment dialogs | `SettlementDialog`, `PaymentRefundDialog`, `RefundReceiptAmendDialog` | longtail | A1, J4 | | Payment reconcile actions (embedded in bookings/events/admissions) | `PaymentReconcileActions` | host screen's spec (core); restyle per A1 dialogs | A1 | -| Event collaboration inboxes (inside Events) | `CollaborationInbox`, `EventSeriesCollaborationInbox` | ux-yuyu-spec Events page; recommended as a yellow callout "{n} spaces invited you to co-host" → review list | §13 | -| Integration health (inside Settings) | `IntegrationHealthPanel` | ux-yuyu-spec (Settings) | — | +| Event collaboration inboxes (inside Events) | `CollaborationInbox`, `EventSeriesCollaborationInbox` | ux-spec Events page; recommended as a yellow callout "{n} spaces invited you to co-host" → review list | §13 | +| Integration health (inside Settings) | `IntegrationHealthPanel` | ux-spec (Settings) | — | | Machine service / printer consoles (inside machine type) | `MachineServiceConsole`, `PrinterServiceConsole` | longtail | B5 | --- diff --git a/docs/ux-yuyu-spec.md b/docs/ux-spec.md similarity index 91% rename from docs/ux-yuyu-spec.md rename to docs/ux-spec.md index ea6ea8d..0ef9961 100644 --- a/docs/ux-yuyu-spec.md +++ b/docs/ux-spec.md @@ -1,47 +1,52 @@ -# Yuyu UX patterns → SpaceWorks screen spec +# Space Works UX spec + +> **Space Works UX.** This is our own interaction system: the patterns (P1–P30), layouts (L1–L10) and +> screen specs that every Space Works product follows. It was **inspired by +> [Yuyu](https://github.com/twilighty-abhi/yuyu)**, whose screens we studied as reference boards R1–R28 +> (kept in `references/yuyu/`). The visual identity comes only from the Space Works brand guidelines. Status: research input for `docs/design-revamp/PLAN.md` Phases 0 and 2–6. No app code is changed here. Scope: layout, information architecture, interaction, states and copy structure. Colour, type families, logo and icons come from the brand spec (PLAN §3.1–3.3). Where this document says -"primary fill" it means whichever token wins PLAN open question 3 (teal or Deep Ink). Yuyu uses a +"primary fill" it means whichever token wins PLAN open question 3 (teal or Deep Ink). The reference boards use a near-black pill. -Sources: 28 Yuyu screens in 4 boards, each cropped one per file into the scratchpad +Sources: 28 reference screens (R1–R28) in 4 boards, each cropped one per file into the scratchpad `crops/` folder (`full-org1..6`, `full-mgr1..4`, `full-auth1..7`, `full-att1..8`, `top-att3..11`). All app screens are **1280 px wide** (the attendee ticket is 390 px), so every measurement below is a native pixel count read off the crop. Treat values as ±2 px. SpaceWorks components were read in this worktree at `d3e5ed29`. -| Ref | Yuyu screen | Board | +| Ref | Reference screen | Board | |---|---|---| -| Y1 | Organiser home ("Good morning, Priya", org cards, Coming up, Recent activity) | Organiser workspace | -| Y2 | Org Events list (callout, 4 stat cards, tabs, date-chip rows, activity rail) | Organiser workspace | -| Y3 | New organisation form + live public-page preview | Organiser workspace | -| Y4 | Members (invite-link card, table, dashed empty state) | Organiser workspace | -| Y5 | Settings › General (settings rows, Discard/Save, danger zone) | Organiser workspace | -| Y6 | Settings › API access (form card, checkbox cards, code block, accordions) | Organiser workspace | -| Y7 | Event › Overview (detail header, 8 tabs, stat cards, latest list, At a glance, CTA card) | Managing an event | -| Y8 | Event › Check-in (progress stat, offline callout, camera, find, toggles, recent + Undo, door station) | Managing an event | -| Y9 | Event › Page & programme (segmented control, track chips, session cards, delay stepper) | Managing an event | -| Y10 | Series › Overview (upcoming-date cards, details form, radio chips, agenda, danger zone) | Managing an event | -| Y11 | Sign in (split screen) | Sign in, invites & account | -| Y12 | Set a new password (centred card, strength meter) | idem | -| Y13 | Confirm your email (centred card) | idem | -| Y14 | Org invite accept (avatar pair, role pill) | idem | -| Y15 | Event co-organiser invite (banner card) | idem | -| Y16 | Account › Profile (settings rows, verified pill) | idem | -| Y17 | Account › Security (big callout with button, destructive outline) | idem | -| Y18 | Public event page with cover + sticky "Join this event" card | Attending an event | -| Y19 | Same page, no cover, tenant accent colour | idem | -| Y20 | Public programme (time grid, track filter pills) | idem | -| Y21 | Public session detail (right rail "Next on main stage") | idem | -| Y22 | Speaker page | idem | -| Y23 | Series event page (inline register form inside sticky card, date pills) | idem | -| Y24 | Live running order (NOW / NEXT / LATER, delay pill) | idem | -| Y25 | Feedback form (rating tiles) | idem | -| Y26 | Mobile ticket with QR (390 px) | idem | -| Y27 | Kiosk: enter station PIN (digit boxes + keypad) | idem | -| Y28 | Kiosk: door check-in station (camera + big result card + Undo + recent) | idem | +| R1 | Organiser home ("Good morning, Priya", org cards, Coming up, Recent activity) | Organiser workspace | +| R2 | Org Events list (callout, 4 stat cards, tabs, date-chip rows, activity rail) | Organiser workspace | +| R3 | New organisation form + live public-page preview | Organiser workspace | +| R4 | Members (invite-link card, table, dashed empty state) | Organiser workspace | +| R5 | Settings › General (settings rows, Discard/Save, danger zone) | Organiser workspace | +| R6 | Settings › API access (form card, checkbox cards, code block, accordions) | Organiser workspace | +| R7 | Event › Overview (detail header, 8 tabs, stat cards, latest list, At a glance, CTA card) | Managing an event | +| R8 | Event › Check-in (progress stat, offline callout, camera, find, toggles, recent + Undo, door station) | Managing an event | +| R9 | Event › Page & programme (segmented control, track chips, session cards, delay stepper) | Managing an event | +| R10 | Series › Overview (upcoming-date cards, details form, radio chips, agenda, danger zone) | Managing an event | +| R11 | Sign in (split screen) | Sign in, invites & account | +| R12 | Set a new password (centred card, strength meter) | idem | +| R13 | Confirm your email (centred card) | idem | +| R14 | Org invite accept (avatar pair, role pill) | idem | +| R15 | Event co-organiser invite (banner card) | idem | +| R16 | Account › Profile (settings rows, verified pill) | idem | +| R17 | Account › Security (big callout with button, destructive outline) | idem | +| R18 | Public event page with cover + sticky "Join this event" card | Attending an event | +| R19 | Same page, no cover, tenant accent colour | idem | +| R20 | Public programme (time grid, track filter pills) | idem | +| R21 | Public session detail (right rail "Next on main stage") | idem | +| R22 | Speaker page | idem | +| R23 | Series event page (inline register form inside sticky card, date pills) | idem | +| R24 | Live running order (NOW / NEXT / LATER, delay pill) | idem | +| R25 | Feedback form (rating tiles) | idem | +| R26 | Mobile ticket with QR (390 px) | idem | +| R27 | Kiosk: enter station PIN (digit boxes + keypad) | idem | +| R28 | Kiosk: door check-in station (camera + big result card + Undo + recent) | idem | --- @@ -69,11 +74,11 @@ components were read in this worktree at `d3e5ed29`. inline pill actions 28 · status pills 20–22 · nav items 40 · table rows 64 · list-card rows 82 · kiosk keys 76. -### P1. App shell and sidebar (Y1–Y10, Y16, Y17) +### P1. App shell and sidebar (R1–R10, R16, R17) ``` ┌──────────── 240–300 ────────────┬───────────────────── main ─────────────────────┐ -│ [Y] Yuyu │ 36px gutter │ +│ [S] Space Works │ 36px gutter │ │ ┌─────────────────────────────┐ │ │ │ │[SD] Screenshot Demo Coll… ⇕ │ │ │ │ │ Owner │ │ │ @@ -102,17 +107,17 @@ components were read in this worktree at `d3e5ed29`. - **Nav item:** 40 tall with a 43 px pitch (3 px gap), radius 8, 14 px inset from the sidebar edge, 10 px text padding. An 18 px line icon, 10 px gap, then the 15 px label. **Active:** a filled warm-grey pill and a semibold label; no accent bar and no coloured text. The home - sidebar (Y1) has no icons. The org sidebar has icons on the four top items only. + sidebar (R1) has no icons. The org sidebar has icons on the four top items only. - **Section label:** "ORGANISATIONS", 12 caps tracked and muted. 24 px above, 8 px below. - **Sub-items** appear only under the active parent: 14 px labels indented 24, with the same - active pill. Y5 and Y6 show Settings › General / API access. + active pill. R5 and R6 show Settings › General / API access. - **Bottom block,** pinned with `margin-top:auto`: a "View public page ↗" text link (14), a user chip (24 px initials circle plus name; it becomes a filled pill while on Account), then "Sign out" as plain text. **There is no theme toggle and no help item in the sidebar.** -- **Interaction:** the sidebar never scrolls on Yuyu because it has 4 items. SpaceWorks needs +- **Interaction:** the sidebar never scrolls on the reference boards because they have 4 items. SpaceWorks needs its own scroll region (see §2). The active state comes from the route. -### P2. Page header (Y1, Y2, Y4–Y6, Y16) +### P2. Page header (R1, R2, R4–R6, R16) ``` Good morning, Priya ← 15 muted (greeting or date line) y+40 @@ -129,7 +134,7 @@ People who can work on this organisation's events. ← optional 15 muted subti glyph prefixes create actions. The secondary pill is an outline on white. - 24 px from the header block to the first content element. -### P3. Detail header with back link, identity chip and underline tabs (Y7–Y10) +### P3. Detail header with back link, identity chip and underline tabs (R7–R10) ``` ← All events 13 semibold link @@ -145,51 +150,51 @@ Overview Guests Page & programme Registration form Invites Check-in Feedba - **Meta line:** the status pill (P10) first, then "Public · Tomorrow, 9:30 AM" at 14 muted, separated by " · ". - **Contextual primary:** "Open check-in" on Overview. On the Check-in tab the primary - **disappears** (Y8) and only "View page ↗" stays. On Page & programme it becomes - "View programme ↗" (Y9). **Rule: the header primary never points at the screen you are on.** + **disappears** (R8) and only "View page ↗" stays. On Page & programme it becomes + "View programme ↗" (R9). **Rule: the header primary never points at the screen you are on.** - **Tabs:** 15 px labels, 12 px horizontal padding, a 40 px tall bar. The active tab is ink and semibold with a 2 px ink underline as wide as the label plus its padding. Inactive tabs are muted. A 1 px hairline runs **full bleed across the main area**, not just the content column. Tabs are links (URL-addressable). With 8 tabs on 944 px they fit on one line; below that they - must scroll horizontally with edge fades (Yuyu does not show this). + must scroll horizontally with edge fades (the reference boards do not show this). - Series uses a **coloured icon tile** (a recurrence glyph on a purple 52 px tile) instead of a date chip. **Generalisation: every detail page has an identity tile; a date where one exists, otherwise a type icon or initials.** -### P4. Stat-card row (Y2, Y7, Y8) +### P4. Stat-card row (R2, R7, R8) - **Grid:** 4 equal columns with 12 gaps, 102 tall, radius 12, 1 px border, 16 px padding. - **Anatomy:** label at 14 muted on top, number at 28–30 bold 12 px below. Variants: - **Fraction:** "5 / 120" with the denominator at 16 semibold muted. - **Progress:** a 4 px track (8 px on check-in) with the fill in the accent colour, 12 px below the number. - - **Wide stat:** spans 1.5–2 columns (Y8 "Checked in 2 of 5 · 40%") with a callout beside it. -- **Not clickable** in Yuyu. **SpaceWorks deviation:** stat cards that represent a queue are + - **Wide stat:** spans 1.5–2 columns (R8 "Checked in 2 of 5 · 40%") with a callout beside it. +- **Not clickable** on the reference boards. **SpaceWorks deviation:** stat cards that represent a queue are links to the filtered list (the dashboard does this today). Keep the card flat but give it a hover border and focus ring. -- Zeros are shown as "0", not hidden (Y7 "Waiting for approval 0"). +- Zeros are shown as "0", not hidden (R7 "Waiting for approval 0"). -### P5. Contextual callout (Y2, Y6, Y8, Y17, Y7 CTA card) +### P5. Contextual callout (R2, R6, R8, R17, R7 CTA card) Three sizes, all in warm-yellow fill with dark-brown text, radius 14, no border: -1. **Banner** (Y2): one line, 50 tall, full content width, 16 px padding. A circled "!" icon, +1. **Banner** (R2): one line, 50 tall, full content width, 16 px padding. A circled "!" icon, then a **bold lead clause** ("Demo Summit is tomorrow.") and a regular follow-on ("Download the guest list for offline check-in before you leave."). A right-aligned underlined bold link with "→" ("Prepare check-in →"). -2. **Panel with button** (Y8, Y17): two lines, about 105 tall. A bold title ("Not ready for +2. **Panel with button** (R8, R17): two lines, about 105 tall. A bold title ("Not ready for offline yet", "Two-step sign-in is off"), one sentence of consequence, and a **filled dark-brown pill button** on the right ("Download guest list", "Set up authenticator"). -3. **CTA card** (Y7 "Get the door ready"): sits in a side column, the whole card is a link, with +3. **CTA card** (R7 "Get the door ready"): sits in a side column, the whole card is a link, with a bold title, a body line and a "→" at the right. -4. **Inline advisory** (Y6): one line, "!" plus text, no action ("Keep keys in a secret manager. +4. **Inline advisory** (R6): one line, "!" plus text, no action ("Keep keys in a secret manager. Never put them in browser code, URLs, source control or logs."). **Rules observed:** a callout exists only when something is **time-sensitive or unsafe and the user can act now**. There is never more than one on a screen. It always names the consequence. There are no info-blue "tips". **A callout replaces a toast or banner for standing conditions.** -### P6. List rows as bordered cards (Y1, Y2) +### P6. List rows as bordered cards (R1, R2) ``` ┌──────────────────────────────────────────────────────────────────────────┐ @@ -213,7 +218,7 @@ There are no info-blue "tips". **A callout replaces a toast or banner for standi - a **two-line nudge**: muted state ("No guests yet") over a teal text link ("Send invites"). - **Drafts:** dashed border, off-white fill, grey chip, and meta that says who can see it ("Draft · only your team can see it"). -- Y1 "Coming up" uses the same row with the action pill only on the most urgent item. +- R1 "Coming up" uses the same row with the action pill only on the most urgent item. ### P7. Date chip / identity tile @@ -224,7 +229,7 @@ There are no info-blue "tips". **A callout replaces a toast or banner for standi - Variants: initials tile (org avatar: rounded square, coloured fill, white initials), person avatar (circle, pastel fill, coloured initials at 11 bold), icon tile (series). -### P8. Underline tabs with inline counts (Y2, Y7, Y16) +### P8. Underline tabs with inline counts (R2, R7, R16) - On list pages the tabs **are the filter** ("Upcoming · Drafts 1 · Past"). The count follows the label in the same muted weight with no badge background. A count is shown only where it @@ -232,7 +237,7 @@ There are no info-blue "tips". **A callout replaces a toast or banner for standi - Tabs sit **between** the stat row and the list, aligned to the main column (not full bleed on list pages; full bleed on detail pages). -### P9. Segmented control (Y9, Y11) +### P9. Segmented control (R9, R11) - A grey track pill 44–50 tall with a white thumb, soft shadow and semibold active text. - **Use:** two to three mutually exclusive **views of the same object** ("Page content | @@ -243,11 +248,11 @@ There are no info-blue "tips". **A callout replaces a toast or banner for standi - **Status pill:** 20–22 tall, 8 px horizontal padding, 12 semibold, tinted fill with dark text in the same hue. Observed: Published, Going, Verified and Unused in green; Waitlist and "Running 5 min late" in yellow; Owner and "as a Member" in grey; Live in green with a 6 px dot. -- **Meta chips** (Y6, Y18): grey tint, 26–28 tall, 13 regular ("Bearer auth", "In person", +- **Meta chips** (R6, R18): grey tint, 26–28 tall, 13 regular ("Bearer auth", "In person", "3 sessions · 2 speakers"). The first chip may carry the brand tint ("Free"). -- **Choice chips** (Y10 "Who can join"; Y20 track filter; Y23 dates): 36 tall outline pills. The - selected one has a 1.5 px dark border (Y10) or a dark fill with white text (Y20 "All tracks"). -- **Track tag** (Y18 schedule): a tinted pill aligned right in the row. +- **Choice chips** (R10 "Who can join"; R20 track filter; R23 dates): 36 tall outline pills. The + selected one has a 1.5 px dark border (R10) or a dark fill with white text (R20 "All tracks"). +- **Track tag** (R18 schedule): a tinted pill aligned right in the row. ### P11. Buttons @@ -260,12 +265,12 @@ There are no info-blue "tips". **A callout replaces a toast or banner for standi red **outline** pill with a trailing "…" ("Delete organisation…", "Delete series…", "Sign out everywhere") for actions that open a confirmation. Destructive is never filled. - **Callout button:** a filled dark-brown pill inside a yellow callout. -- **Disabled:** a grey fill with grey text (Y27 "Open check-in"), not reduced opacity. +- **Disabled:** a grey fill with grey text (R27 "Open check-in"), not reduced opacity. - **Paired form footer:** "Discard" (outline) and "Save changes" (primary), right-aligned at the end of the form. On a create form the pair is "Create organisation" (primary, left) and "Cancel" (plain text, right of it). -### P12. Recent-activity rail (Y1, Y2) +### P12. Recent-activity rail (R1, R2) - A right column 260 wide with a 28 px gap from the main column (main is 622). Heading "Recent activity" at 20 bold. @@ -273,24 +278,24 @@ There are no info-blue "tips". **A callout replaces a toast or banner for standi name in bold** and the target after " · " ("**Kiran Reddy** joined the waitlist · Demo Summit"), with the relative time at 14 muted on the next line. 77 px pitch. No dividers. - Footer link: "See all activity →", underlined 14 semibold. -- The rail **sits beside the list, not above it,** and starts level with the tab bar (Y2) or the - first section heading (Y1). It drops entirely on narrow screens (inferred). +- The rail **sits beside the list, not above it,** and starts level with the tab bar (R2) or the + first section heading (R1). It drops entirely on narrow screens (inferred). -### P13. Key–value "At a glance" card (Y7) +### P13. Key–value "At a glance" card (R7) - A bordered card 280 wide with 42 px rows split by hairlines. Label at 14 muted on the left, value at 14 ink **right-aligned**. Below it, the CTA card (P5.3). -### P14. Tables inside a card (Y4, Y8, Y10) +### P14. Tables inside a card (R4, R8, R10) - One bordered card radius 12. Header row 40 tall with labels at 13 muted, sentence case, no uppercase. Body rows 64 tall with hairlines. - Person cell: a 36 px initials circle, name at 15 semibold plus "(you)" muted, and email at 14 muted below. - The last column is a row menu ("—"/"⋯") or an **inline text action** ("Undo", underlined). - The agenda table (Y10) puts the status pill right-aligned. + The agenda table (R10) puts the status pill right-aligned. -### P15. Settings rows (Y5, Y16, Y17) +### P15. Settings rows (R5, R16, R17) ``` Logo [SD] ( Upload logo ) @@ -301,7 +306,7 @@ Shown at the top of your public [ Screenshot Demo Collective ] page. Description [ A demo organisation used to… ] ───────────────────────────────────────────────────────────────── hairline -Web address [ yuyu.idliapps.com/ shots-org Copy ] (read-only: grey fill) +Web address [ space-works.tech/ shots-org Copy ] (read-only: grey fill) Set when the organisation was created. ───────────────────────────────────────────────────────────────── ( Discard ) [■ Save changes ] @@ -319,17 +324,17 @@ Set when the organisation was created. ("Changing email is coming in a future update."). - **Footer:** one Discard/Save pair for the whole page. The account page puts a meta fact on the left of the save row ("Member since 13 October 2026"). -- **Independent action rows** (Y17 password) carry their own button inside the right column +- **Independent action rows** (R17 password) carry their own button inside the right column ("Update password"), because they are not part of the page save. -### P16. Danger zone (Y5, Y10) +### P16. Danger zone (R5, R10) - Always last, 40 px below the save row. A card with a 1 px pale-red border, radius 14 and 20 px padding. Title at 16 semibold red ("Delete organisation"), one sentence of consequence at 14 ink, and a red outline pill with "…" on the right. **One danger card per irreversible action.** -### P17. Empty states (Y4, Y6, Y3 preview) +### P17. Empty states (R4, R6, R3 preview) - A dashed-border card (1 px dashed, radius 12, 20–24 px padding) with **one centred sentence** at 15 muted. **No illustration and no heading.** The sentence states the situation and the @@ -339,18 +344,18 @@ Set when the organisation was created. - The same dashed treatment marks **"+ Add session"** and "+ New organisation" placeholders. The "add" affordance sits where the next item will appear. -### P18. Create form with live preview (Y3) +### P18. Create form with live preview (R3) - Form column 636 wide and preview column 300, gap 32. The preview is labelled with an eyebrow ("PUBLIC PAGE PREVIEW") and updates as you type. -- Prefixed URL input: a grey static prefix segment ("yuyu.idliapps.com/") joined to an editable +- Prefixed URL input: a grey static prefix segment ("space-works.tech/") joined to an editable segment, with an **inline availability check** underneath ("✓ Available" at 13 green). - Optional fields are marked inline ("Logo Optional", "Optional · shown on your public page"). Required fields carry no asterisk. - Dropzone: dashed card with a 56 px icon tile, "Drop an image here, or **choose a file**" and the constraints line "Square, JPEG, PNG or WebP, up to 5 MB". -### P19. Form card with checkbox cards and developer reference (Y6) +### P19. Form card with checkbox cards and developer reference (R6) - A "New API client" form inside a bordered card with 3-column inputs. - **Checkbox cards:** a 3-up grid, each a bordered card with a checkbox, a 15 semibold label and @@ -360,7 +365,7 @@ Set when the organisation was created. - **Accordion rows:** a disclosure triangle, the label and a monospace hint, at 52 tall with hairlines. This is progressive disclosure for reference material. -### P20. Session/track board (Y9) +### P20. Session/track board (R9) - Track columns are tinted panels (off-white fill, radius 16, 16 px padding) with a header ("Main stage" plus "2 sessions" right-aligned and muted). @@ -371,7 +376,7 @@ Set when the organisation was created. handle and an edit pencil, plus "+ Add track". A right-aligned muted hint explains the rule ("Delays only move later sessions in the same track."). -### P21. Check-in workspace (Y8) +### P21. Check-in workspace (R8) - Top: a progress stat (2 cols) plus the "Not ready for offline yet" callout (2 cols). - Left (446): the camera block (dark, radius 16, 240 tall, centred "Camera is off") with a @@ -384,11 +389,11 @@ Set when the organisation was created. only check people in — no guest lists or exports"), then the buttons "Copy link" (primary), "Show PIN", "New PIN" and "Turn off station" (red text). -### P22. Auth split screen (Y11) +### P22. Auth split screen (R11) ``` ┌──────────────── 640 ────────────────┬──────────── 604 (16 inset) ────────────┐ -│ [Y] Yuyu │ Free events. │ +│ [S] Space Works │ Free events. │ │ │ Your community's │ │ Welcome back. (44 bold)│ data stays yours. (40 bold) │ │ [ Sign in | Create account ] │ │ @@ -412,7 +417,7 @@ Set when the organisation was created. product objects** (date-chip cards, a check-in toast), slightly rotated with soft shadows. This is product proof, not stock art. -### P23. Centred task card (Y12–Y15) +### P23. Centred task card (R12–R15) - 440 wide (480 for invites), radius 20, 32 px padding, 1 px border, on the off-white canvas with the logo top-left. @@ -431,14 +436,14 @@ Set when the organisation was created. - Password strength: a 4-segment meter (4 px bars, 4 px gaps) plus a live rule list ("✓ At least 12 characters" in green). -### P24. Account tabs (Y16, Y17) +### P24. Account tabs (R16, R17) - Title plus subtitle, then a 2-tab underline bar (Profile / Security) at content width, then settings rows. - Security leads with a **panel callout** when a protection is off. Sessions use a destructive outline ("Sign out everywhere"). -### P25. Public event page (Y18, Y19, Y23) +### P25. Public event page (R18, R19, R23) ``` [SD] Screenshot Demo Collective Discover events ( Sign in ) 48h header @@ -459,24 +464,24 @@ Schedule ............................ Live schedule → 60 min Asha Menon Speakers (2-col cards) · Partners (2-col) · Venue (map | text, Get directions ↗) · FAQ (disclosure) ────────────────────────────────────────────────────────────────────────────────── -[Y] Free event, powered by Yuyu Privacy · Terms · Hosted on Idlistack +[S] Powered by Space Works Privacy · Terms ``` - Container 1110 with 84 px gutters at 1280. Main column 545 and sticky card 318, gap 66. -- **Tenant theming** (Y19) recolours only the title, the chip tint, the date-chip band, the +- **Tenant theming** (R19) recolours only the title, the chip tint, the date-chip band, the register button and the highlight tile. Everything else stays neutral, and the cover image is optional. - The sticky card carries the **only primary on the page**, and it changes with state: the price is folded into the label ("Register — it's free"); a waitlist would read "Join waitlist". Social proof (avatars plus "Meera, Dev and 3 others") sits above the button; the capacity bar sits under the counts. -- **Inline-form variant (Y23):** the registration fields live inside the sticky card ("Join on +- **Inline-form variant (R23):** the registration fields live inside the sticky card ("Join on 20 October"; name, email, helper "We'll send your ticket and the call link here.", register; "Have an account? **Sign in to register faster**"). A date-pill picker sits in the main column. -- Secondary pages (Y20–Y22) **move the primary into the header** ("Register — free" pill) +- Secondary pages (R20–R22) **move the primary into the header** ("Register — free" pill) because there is no sticky card. They use a breadcrumb "Demo Summit 2026 / Programme". -### P26. Live and status pages (Y24) and feedback (Y25) +### P26. Live and status pages (R24) and feedback (R25) - **Running order:** a "● Live · updates automatically" green pill in the header. A **NOW** card in solid brand fill (white text, title 36 bold, "6:30 – 6:40 PM · 4 min left", white progress @@ -491,7 +496,7 @@ Speakers (2-col cards) · Partners (2-col) · Venue (map | text, Get directions a privacy helper. The full-width brand primary names both outcomes ("Send feedback & get certificate"). -### P27. Mobile ticket (Y26, 390 px) +### P27. Mobile ticket (R26, 390 px) - "← Event page" sits beside a "✓ Confirmed" green pill. Title "You're in, Meera!" at 32 bold. - **Ticket card:** a brand header band (org name small, event at 24 bold), a 2-column grid of @@ -501,7 +506,7 @@ Speakers (2-col cards) · Partners (2-col) · Venue (map | text, Get directions - Two half-width buttons (primary "Add to calendar", outline "Save ticket") and an underlined destructive escape ("Can't make it? Cancel registration"). -### P28. Kiosk PIN (Y27) +### P28. Kiosk PIN (R27) - Two columns on a 1180 canvas. **Left:** org tile 64, a context line ("Demo Summit 2026 · Door check-in"), the title at 52 bold ("Enter the station PIN"), the helper ("Ask an organiser for @@ -511,7 +516,7 @@ Speakers (2-col cards) · Partners (2-col) · Venue (map | text, Get directions "⌫" on grey keys, and a full-width CTA "Open check-in" (58 tall) that stays **disabled grey until all 8 digits are in**. -### P29. Kiosk station (Y28) +### P29. Kiosk station (R28) - A 100 px header: event name (22 bold), "Door check-in · Station 1", and on the right a big count "**3** / 5 checked in", a "● Online" pill and an outline "Lock station". @@ -522,13 +527,13 @@ Speakers (2-col cards) · Partners (2-col) · Venue (map | text, Get directions (white outline). Below it a "RECENT" list (✓ name, relative time). The result **is the screen**; there is no modal. -### P30. Undo and confirmation behaviour (Y8, Y28) +### P30. Undo and confirmation behaviour (R8, R28) -- **Inline Undo** appears in the "Recent check-ins" rows and on the station result card. Yuyu - shows no toast and no "Are you sure?" for check-ins. Reversible high-frequency actions use +- **Inline Undo** appears in the "Recent check-ins" rows and on the station result card. The reference boards + show no toast and no "Are you sure?" for check-ins. Reversible high-frequency actions use **do it now, offer Undo in place.** - Irreversible actions use a **danger card plus a "…" button** that leads to a confirmation. -- **Toasts do not appear on any Yuyu board.** The closest artefact is the floating +- **Toasts do not appear on any reference board.** The closest artefact is the floating "✓ Meera checked in" chip on the auth hero, which is a reasonable visual model. SpaceWorks already has `components/ui/toastStore.ts` with an awaited `undo` compensation, used by inventory archive. **Spec:** toast = bottom-centre (mobile) or bottom-left of the main area @@ -538,11 +543,11 @@ Speakers (2-col cards) · Partners (2-col) · Venue (map | text, Get directions ### 1.31 Interaction model summary -| Concern | Yuyu rule | +| Concern | Space Works rule | |---|---| | Primary action | One per page header, one per sticky card, one per callout. Never points at the current screen. | | Secondary | Outline pill beside the primary; everything else is a text link or a row menu. | -| Where counts live | Stat cards (overview numbers), tab labels (queue sizes), row right slots ("5 / 120"), header meta, the kiosk header. **Never** in the sidebar on Yuyu (SpaceWorks keeps its unread count there, see §2). | +| Where counts live | Stat cards (overview numbers), tab labels (queue sizes), row right slots ("5 / 120"), header meta, the kiosk header. **Never** in the sidebar on the reference boards (SpaceWorks keeps its unread count there, see §2). | | Progressive disclosure | Sub-nav only under the active parent; accordions for reference; the camera off until "Start scanning"; the live preview beside the create form; optional fields labelled rather than hidden. | | Inline vs menu | The **next step** is inline (pill or link in the row); rare or destructive actions go in "⋯" / "—" menus; destructive-irreversible actions go in the danger zone. | | Navigation | Back link on detail pages; breadcrumb only on public sub-pages; tabs are URLs. | @@ -606,7 +611,7 @@ type PageHeaderProps = { eyebrow?: string; title: string; subtitle?: string; met Breakpoints used below: **375** (phone), **768** (tablet portrait), **1280** (desktop). Gutters 16 / 24 / 36. Content max-width 1120 (Inventory/Reports/Audit may opt into `wide` = full). -### L1. `AppShell` — sidebar + content (Yuyu Y1–Y10, Y16–Y17) +### L1. `AppShell` — sidebar + content (R1–R10, R16–R17) ```ts AppShell({ @@ -634,7 +639,7 @@ AppShell({ Used by: every staff tab (§2), the member area (S16). Not used by public, auth, kiosk. -### L2. `DashboardLayout` — greeting header + stat row + main + right rail (Y1, Y2) +### L2. `DashboardLayout` — greeting header + stat row + main + right rail (R1, R2) ```ts DashboardLayout({ @@ -655,7 +660,7 @@ DashboardLayout({ Used by: staff Home (S1), member Home (S16), organizations home (governance-only), Platform dashboard. -### L3. `ListLayout` — header + callout + stats/tabs + filters + list/table + pagination (Y2, Y4) +### L3. `ListLayout` — header + callout + stats/tabs + filters + list/table + pagination (R2, R4) ```ts ListLayout({ @@ -681,7 +686,7 @@ ListLayout({ Used by: Requests, Direct handout, Inventory, Members, Staff & roles, Events, Bookings, Machines index (rows→cards), Audit log, Inbox, Payments, Ledger, Transfers, Stocktake, To buy, Repair shelf, Containers, Categories, Warranties, Devices, Member cards, Accountability, Organizations, Organized events, Delivery log, Email templates, public events list (inside L9). -### L4. `DetailLayout` — back + identity + title/status/meta + actions + underline tabs + main/aside (Y7–Y10) +### L4. `DetailLayout` — back + identity + title/status/meta + actions + underline tabs + main/aside (R7–R10) ```ts DetailLayout({ @@ -705,7 +710,7 @@ DetailLayout({ Used by: Request detail (drawer), Inventory item (drawer), Machine type page, Machine detail (drawer, wide), Event detail, Series detail, Bookable space, Member detail (drawer), Role detail, Report viewer (main = report, aside = report list on the left variant `asideSide="start"`). -### L5. `SettingsLayout` — sub-nav + sections of rows + sticky Discard/Save + DangerZone (Y5, Y6, Y16, Y17) +### L5. `SettingsLayout` — sub-nav + sections of rows + sticky Discard/Save + DangerZone (R5, R6, R16, R17) ```ts SettingsLayout({ @@ -729,7 +734,7 @@ DangerZone({ title: string; consequence: string; action: Action & { confirms: tr Used by: Makerspace settings (S9), Account/MFA, member Account tab, Machine type › Settings tab content, item Details tab content (rows only), API access, Email settings, Modules, Usage & limits, Backup & restore, Data export, Platform settings, Evidence retention. -### L6. `FormPageLayout` — create/edit form + optional live preview (Y3, Y6 form card) +### L6. `FormPageLayout` — create/edit form + optional live preview (R3, R6 form card) ```ts FormPageLayout({ @@ -748,7 +753,7 @@ FormPageLayout({ Used by: New event / New series, Add item, Create makerspace, New organization, Create bookable space, New API client, Add staff (full page variant), QR tools, staff Scanner, Bulk import, Email template editor. -### L7. `FlowLayout` — step rail + work panel + guarded footer (Y8/Y28 visual, SpaceWorks wizards) +### L7. `FlowLayout` — step rail + work panel + guarded footer (R8/R28 visual, SpaceWorks wizards) ```ts FlowLayout({ @@ -772,7 +777,7 @@ FlowLayout({ Used by: request issue/return (S3), direct handout (S4), job pickup (`HandoverConsole`), kiosk handover, offline replay correction, stocktake count session, tenant migration / backup restore wizards, create-makerspace onboarding. -### L8. `AuthLayout` (Y11) and `CenteredCardLayout` (Y12–Y15) +### L8. `AuthLayout` (R11) and `CenteredCardLayout` (R12–R15) ```ts AuthLayout({ @@ -800,7 +805,7 @@ CenteredCardLayout({ Used by: staff sign-in, member sign-in/sign-up (Auth); reset password, verify email, MFA challenge, staff/organization invitation accept, change-password gate, access denied, "event not found" (Centred). -### L9. `PublicLayout` (Y18–Y25 chrome) and `PublicDetailLayout` (Y18, Y19, Y21, Y23) +### L9. `PublicLayout` (R18–R25 chrome) and `PublicDetailLayout` (R18, R19, R21, R23) ```ts PublicLayout({ @@ -834,7 +839,7 @@ type ActionCardProps = { title: string; summary?: ReactNode /* counts + bar, ava Used by: catalogue (PublicLayout + `aside`), item detail, public event, events list, programme, session, feedback (narrow), running order (narrow, `liveStatus`), ticket (narrow), public machines, public bookings, public stats, organization page, about/landing. -### L10. `KioskLayout` — full screen, large targets (Y27, Y28) +### L10. `KioskLayout` — full screen, large targets (R27, R28) ```ts KioskLayout({ @@ -861,20 +866,20 @@ All targets ≥56 px, text ≥18, no hover-only affordances. Used by: kiosk hand ### 1A.11 Pattern → screen-type → layout map (shared with `ux-screens-longtail.md`) -| Screen type | Layout | Yuyu source | Patterns it composes | SpaceWorks screens (examples) | +| Screen type | Layout | Reference | Patterns it composes | SpaceWorks screens (examples) | |---|---|---|---|---| -| Dashboard home | L1 + **L2 DashboardLayout** | Y1, Y2 | P2 eyebrow header, P5 banner, P4 stats, P6 rows, P12 rail | Staff Home, member Home, organizations home, Platform dashboard | -| List + stats | L1 + **L3 ListLayout** | Y2, Y4 | P2, P5, P4, P8 tabs, P6 rows or P14/§5.1 table, P17 empty, P30 undo | Requests, Inventory, Members, Events, Bookings, Audit, Payments, every long-tail table tab | -| Detail with tabs | L1 + **L4 DetailLayout** (page or drawer) | Y7–Y10 | P3, P7, P4, P13 aside, P5 CTA card, P16 | Request, item, machine type, machine, event, series, space, member, role | -| Settings rows | L1 + **L5 SettingsLayout** | Y5, Y6, Y16, Y17 | P15, P16, P5 panel, P19, P24 | Makerspace settings, account, API access, modules, backups, exports | -| Create / tool form | L1 + **L6 FormPageLayout** | Y3, Y6 | P18 preview, P19 cards, P17 dropzone | New event/item/makerspace/space/client, QR tools, bulk import, template editor | -| Wizard / flow | **L7 FlowLayout** (task mode over L1, or inside L10) | Y8, Y28 (visual) | P21 camera, evidence tile (§5.2), P29 result card | Issue/return, direct handout, job pickup, stocktake session, restore/migration | -| Scanner / kiosk | **L10 KioskLayout** | Y27, Y28 | P28 PIN, P29 station, P30 undo, P5 panel | Kiosk station, door check-in, desk kiosk, scanner page | -| Public list | **L9 PublicLayout** | Y18 chrome, Y20 | P6 rows, P10 chips, P9 segmented | Catalogue (with `aside` cart), events list, machines, bookings | -| Public detail | L9 + **PublicDetailLayout** | Y18, Y19, Y21, Y23 | P25, P10, action card | Event page, item page, session, speaker, organization page | -| Public narrow task | L9 `width="narrow"` | Y24, Y25, Y26 | P26, P27 | Feedback, running order, ticket, self-checkout result | -| Auth | **L8 AuthLayout** | Y11 | P22, P9 | Staff sign-in, member sign-in/up | -| Centred task card | **L8 CenteredCardLayout** | Y12–Y15 | P23 | Reset, verify, MFA, invitations, access denied, not found | +| Dashboard home | L1 + **L2 DashboardLayout** | R1, R2 | P2 eyebrow header, P5 banner, P4 stats, P6 rows, P12 rail | Staff Home, member Home, organizations home, Platform dashboard | +| List + stats | L1 + **L3 ListLayout** | R2, R4 | P2, P5, P4, P8 tabs, P6 rows or P14/§5.1 table, P17 empty, P30 undo | Requests, Inventory, Members, Events, Bookings, Audit, Payments, every long-tail table tab | +| Detail with tabs | L1 + **L4 DetailLayout** (page or drawer) | R7–R10 | P3, P7, P4, P13 aside, P5 CTA card, P16 | Request, item, machine type, machine, event, series, space, member, role | +| Settings rows | L1 + **L5 SettingsLayout** | R5, R6, R16, R17 | P15, P16, P5 panel, P19, P24 | Makerspace settings, account, API access, modules, backups, exports | +| Create / tool form | L1 + **L6 FormPageLayout** | R3, R6 | P18 preview, P19 cards, P17 dropzone | New event/item/makerspace/space/client, QR tools, bulk import, template editor | +| Wizard / flow | **L7 FlowLayout** (task mode over L1, or inside L10) | R8, R28 (visual) | P21 camera, evidence tile (§5.2), P29 result card | Issue/return, direct handout, job pickup, stocktake session, restore/migration | +| Scanner / kiosk | **L10 KioskLayout** | R27, R28 | P28 PIN, P29 station, P30 undo, P5 panel | Kiosk station, door check-in, desk kiosk, scanner page | +| Public list | **L9 PublicLayout** | R18 chrome, R20 | P6 rows, P10 chips, P9 segmented | Catalogue (with `aside` cart), events list, machines, bookings | +| Public detail | L9 + **PublicDetailLayout** | R18, R19, R21, R23 | P25, P10, action card | Event page, item page, session, speaker, organization page | +| Public narrow task | L9 `width="narrow"` | R24, R25, R26 | P26, P27 | Feedback, running order, ticket, self-checkout result | +| Auth | **L8 AuthLayout** | R11 | P22, P9 | Staff sign-in, member sign-in/up | +| Centred task card | **L8 CenteredCardLayout** | R12–R15 | P23 | Reset, verify, MFA, invitations, access denied, not found | --- @@ -932,14 +937,14 @@ quotes are proposed display labels; the key and URL do not change. | # | Section header | Items (key → label) | Notes | |---|---|---|---| -| 0 | *(no header)* | **dashboard → "Home"**, **notifications → "Inbox"** (unread badge) | Yuyu "Home" pattern. The unread count stays in the accessible name ("Inbox, 3 unread"). | +| 0 | *(no header)* | **dashboard → "Home"**, **notifications → "Inbox"** (unread badge) | The "Home" pattern. The unread count stays in the accessible name ("Inbox, 3 unread"). | | 1 | Front desk | **requests → "Requests"** (count badge: pending + ready), **direct → "Direct handout"**, **handover → "Job pickups"**, **payments → "Payments"** | The daily loop. Payments sits here because `record_payment` is a handout action and front-desk staff take money at the desk. | | 2 | Inventory | **inventory → "Items"**, **needsfix → "Repair shelf"**, **stocktake → "Stocktake"**, **transfers → "Transfers"**, **tobuy → "To buy"**; *categories → "Categories"*, *containers → "Containers"*, *ledger → "Stock ledger"*, *warranty → "Warranties"*, *qr → "QR tools"*, *scanner → "Scanner"*, *bulk → "Bulk import"* | Page `h1` stays "Inventory" (the test pin is the panel heading, not the nav label). "Ledger" gains "Stock" so it is not mistaken for money. | -| 3 | Machines | **machines → "Machines"** + dynamic sub-items per machine type when active, **devices → "Devices"** | Sub-items come from the machine-type list already fetched by `MachinesPanel`, linked with `staffTabPath("machines", …, machineTypeSegment(type))`. This uses the existing `TABS_WITH_SUBPATHS` route, so it is the Yuyu "Settings › General" sub-nav pattern on an existing route. | +| 3 | Machines | **machines → "Machines"** + dynamic sub-items per machine type when active, **devices → "Devices"** | Sub-items come from the machine-type list already fetched by `MachinesPanel`, linked with `staffTabPath("machines", …, machineTypeSegment(type))`. This uses the existing `TABS_WITH_SUBPATHS` route, so it is the "Settings › General" sub-nav pattern on an existing route. | | 4 | Community | **members → "Members"**, **events → "Events"**, **bookings → "Bookings"**, *cards → "Member cards"* | | | 5 | Insights | **reports → "Reports"**, **audit → "Audit log"**, *accountability → "Accountability"* | | | 6 | Organizations | **organizations → "Organizations"**, **organized → "Organized events"**, **organization-analytics → "Org analytics"** | All three are cross-venue. The section shows a muted caption "Across venues" so the user knows the selected makerspace does not scope it. In single-tenant mode all three are hidden by gating, so the section disappears. For a governance-only actor this is the only section (see 2.5). | -| — | *(bottom block, above the user chip)* | **settings → "Settings"** with sub-items when any Settings-family tab is active: settings → "General", users → "Staff & roles", emailtemplates → "Email templates", email-logs → "Delivery log", api → "API access", exports → "Data export", backups → "Backup & restore", usage → "Usage & limits", modules → "Modules", migration → "Tenant migration" | Yuyu Settings › General / API access. "Admin" becomes one item that expands in place. If `settings` itself is not allowed but a child is (e.g. a role with only `edit_inventory` sees Email templates), the parent link goes to the **first allowed child**. | +| — | *(bottom block, above the user chip)* | **settings → "Settings"** with sub-items when any Settings-family tab is active: settings → "General", users → "Staff & roles", emailtemplates → "Email templates", email-logs → "Delivery log", api → "API access", exports → "Data export", backups → "Backup & restore", usage → "Usage & limits", modules → "Modules", migration → "Tenant migration" | The Settings › General / API access sub-nav (R5, R6). "Admin" becomes one item that expands in place. If `settings` itself is not allowed but a child is (e.g. a role with only `edit_inventory` sees Email templates), the parent link goes to the **first allowed child**. | | — | Platform *(bottom, superadmin only)* | **platform-dashboard → "Platform dashboard"**, **platform → "Platform settings"** | Rendered under a hairline with a muted "Platform" label so it is visibly a different scope. | Item order inside a section is by frequency of use, not alphabetical. @@ -954,7 +959,7 @@ Item order inside a section is by frequency of use, not alphabetical. (wrapped in try/catch, a convenience only). **A section auto-expands when it contains the active tab**, so the active item is never hidden (this matches the unmounted-collapsed-content accessibility rule in PLAN §5). -4. Settings sub-items show only while a Settings-family tab is active (Yuyu rule); otherwise +4. Settings sub-items show only while a Settings-family tab is active (sub-nav rule); otherwise Settings is one row. 5. Machine-type sub-items show only while on `machines`, at most 8, then "All types →". 6. The sidebar body scrolls independently (`overflow-y:auto`) between the switcher and the @@ -1002,7 +1007,7 @@ Item order inside a section is by frequency of use, not alphabetical. **Governance-only** (`allowedTabs = ["organizations"]`): sidebar = Organizations only, and no switcher menu entries other than "View public page" (there is no makerspace scope). The page uses -the Yuyu home pattern Y1 ("Your organizations" cards). +the home pattern R1 ("Your organizations" cards). **Machine-only** (`isMachineOnly`): typically Home, Inbox, Machines (with type sub-items), Warranties, Reports, Email templates. Rule 2 keeps it flat. The machine-type sub-items become the @@ -1029,7 +1034,7 @@ column, limited to 3 items with "See all"; row cards keep the chip and stack the the meta; header actions collapse to the primary plus an overflow "⋯"; underline tabs scroll horizontally with the active tab scrolled into view; drawers open full screen. -### S1. Staff Home / Dashboard (`dashboard`) ← Y1 + Y2 +### S1. Staff Home / Dashboard (`dashboard`) ← R1 + R2 **Layout:** L1 `AppShell` + L2 `DashboardLayout`. The slots are filled as follows: `header` = date eyebrow, "Dashboard" `h1` (greeting as eyebrow), Quick add primary; @@ -1080,7 +1085,7 @@ Everything else ▸ (disclosure: remaining non-zero tiles as a compact 2-col li the greeting as the eyebrow, **or** update the pin deliberately in Phase 4. Recommendation: `h1` "Dashboard" with the greeting above it. -### S2. Requests list (`requests`) ← Y2 (tabs + rows) + Y8 (inline actions) +### S2. Requests list (`requests`) ← R2 (tabs + rows) + R8 (inline actions) **Layout:** L3 `ListLayout`. The slots are filled as follows: `header` = "Requests", with the subtitle and a conditional primary; `tabs` = Pending / Ready / On loan / History with counts; @@ -1127,7 +1132,7 @@ Pending 4 │ Ready 2 │ On loan 7 │ History [ Search name, e - **Mobile:** rows stack; the inline action becomes a full-width button at the bottom of the card (44 tall). -### S3. Request detail + issue/return flow ← Y7 (detail header, at-a-glance) + Y8/Y28 (scan workspace) +### S3. Request detail + issue/return flow ← R7 (detail header, at-a-glance) + R8/R28 (scan workspace) **Layout:** the detail is L4 `DetailLayout presentation="drawer"`. The slots are filled as follows: `back` = Requests; `identity` = requester avatar; `title` = "Request #id"; `status`; @@ -1216,7 +1221,7 @@ Desktop: full-screen task mode (no sidebar), 2 columns sign-off:** the abandoned-upload cleanup is default-off, so the claim must match the deployment. -### S4. Direct handout (`direct`) ← Y2 list + S3 flow +### S4. Direct handout (`direct`) ← R2 list + S3 flow **Layout:** L3 `ListLayout`. The slots are filled as follows: `header` = "+ New handout" primary; `tabs` = On loan / Returned / Claim codes; `body` = rows with inline Return. New handout @@ -1240,7 +1245,7 @@ On loan 5 │ Returned │ Claim codes - **Mobile:** the primary becomes a full-width sticky bottom button above the bottom tab bar (handout console). -### S5. Inventory list (`inventory`) ← Y2 header/stat cards + adapted dense table (§5) +### S5. Inventory list (`inventory`) ← R2 header/stat cards + adapted dense table (§5) **Layout:** L3 `ListLayout width="wide"`. The slots are filled as follows: `header` = title, meta counts, "+ Add item" primary, an Export menu as secondary; `callout` = reorder suggestions; @@ -1279,7 +1284,7 @@ All 248 │ Low stock 12 │ Out of stock 4 │ Archived [ Search ite - **Mobile:** table becomes stacked cards (image, name, availability pill, "Issued 15 · Damaged 1 · Lost 1"), selection via long-press or a checkbox; bulk bar fixed at the bottom. -### S6. Inventory item detail ← Y7 (detail header + tabs) + Y5 (settings rows, danger zone) +### S6. Inventory item detail ← R7 (detail header + tabs) + R5 (settings rows, danger zone) **Layout:** L4 `DetailLayout presentation="drawer" width=720`. The slots are filled as follows: `identity` = image; `status` = availability; `tabs` = Details / Stock / QR & assets / History / @@ -1307,14 +1312,14 @@ Details: settings rows (Name & description │ Photo │ Category │ Public vis The last two only when `canViewAudit`. - **Public visibility row** groups `is_public`, `public_availability_mode` (segmented: Exact count / Status only / Hidden) and `show_public_count`, with a **live preview pill** of what the - public sees (Y3 live-preview pattern). The rule from CLAUDE.md is computed client-side only for + public sees (R3 live-preview pattern). The rule from CLAUDE.md is computed client-side only for display. "Exact count" with `show_public_count=false` previews as "Limited" and the helper says why. -- **Add item** uses the same layout as a create page (Y3): form left, "Public catalogue preview" +- **Add item** uses the same layout as a create page (R3): form left, "Public catalogue preview" card right (ProductCard rendering). Then, as today, it reopens in edit mode to add a photo, with the copy "Item created. Add a photo so members can recognise it." -### S7. Members (`members`) ← Y4 +### S7. Members (`members`) ← R4 **Layout:** L3 `ListLayout`. The slots are filled as follows: `header` = "Invite member" primary; `callout` = pending requests; `tabs` = Members / Requests / Present now / Plans / Waivers; @@ -1332,7 +1337,7 @@ Members 128 │ Requests 3 │ Present now 4 │ Plans │ Waivers [ Se └──────────────────────────────────────────────────────────────────────────────────────┘ ``` -- **Invite** = the Y4 invite card pattern, opened from the primary into a modal: email, role +- **Invite** = the R4 invite card pattern, opened from the primary into a modal: email, role select (defaults to first role, as today) and "Send invite". The Requests tab lists both membership requests and staff invitations (today's second panel), each with inline "Approve" (primary pill) and "Decline…" (red text). The plan picker appears inline in the @@ -1349,7 +1354,7 @@ Members 128 │ Requests 3 │ Present now 4 │ Plans │ Waivers [ Se empty:** "No one is waiting. New membership requests appear here." - **Mobile:** the table becomes cards (name, role and status pills, ⋯). -### S8. Staff & roles (`users`) ← Y4 (table) + Y6 (checkbox cards for permissions) +### S8. Staff & roles (`users`) ← R4 (table) + R6 (checkbox cards for permissions) **Layout:** L3 `ListLayout`. The slots are filled as follows: `tabs` = Staff / Roles / Invitations; `body` = table. The role detail is L4 `DetailLayout` (page). Its `main` = @@ -1364,17 +1369,17 @@ Staff table: Person · Role (pill) · Makerspace* · Status (Restricted pill if ``` \* superadmin / multi-makerspace only. -- The first-staff invitation link renders as the **Y4 invite-link card** (read-only field, +- The first-staff invitation link renders as the **R4 invite-link card** (read-only field, "Unused" pill, "Copy link" primary, "Dismiss"). - **Roles tab:** role list rows (name, the "Default" pill for the 4 protected roles, "n actions", "m staff") leading to a role detail with the `RoleEditor` actions grouped as **checkbox cards** - (Y6) by area (Lending, Inventory, Machines, Community, Money, Admin), each card showing the + (R6) by area (Lending, Inventory, Machines, Community, Money, Admin), each card showing the human label plus the action key in mono, and `RoleMachineScopeEditor` as its own settings row. Save via a Discard/Save pair. Danger zone: "Delete role…" for custom roles. - **Invitations tab:** `StaffInvitations` (when `staff.invitations`). - **Empty:** "No staff yet. Add staff to share the work." -### S9. Makerspace settings (`settings`) ← Y5 + Y6 + Y17 +### S9. Makerspace settings (`settings`) ← R5 + R6 + R17 **Layout:** L5 `SettingsLayout subnav="auto"`. The slots are filled as follows: `sections` = the 12 groups in the table below, each with its own `save`; `danger` = the archive request. The @@ -1416,7 +1421,7 @@ scroll-spy, so no routing change is needed. Optional later: `settings/
` - **Loading:** row skeletons per section. **Error:** per-section error with Retry (the sections load independently). -### S10. Machines ← Y1 cards (index), Y7 detail header + tabs (type page), Y5 rows (machine detail) +### S10. Machines ← R1 cards (index), R7 detail header + tabs (type page), R5 rows (machine detail) **Layout:** the index is L3 `ListLayout`. The slots are filled as follows: `body` = rows, rendered as a card grid via the `ListRow` card variant; `callout` = overdue maintenance. The type @@ -1471,7 +1476,7 @@ Overview: stat cards (Usage hours · Jobs this month · Next maintenance · Open - **Empty:** "No machines are registered for this type. Add the first one with Add machine." Keep the "requires a successful load" rule from `MachineTypePage`. -### S11. Events list (`events`) ← Y2 exactly +### S11. Events list (`events`) ← R2 exactly **Layout:** L3 `ListLayout`. The slots are filled as follows: `header` = greeting eyebrow, "New series" secondary, "+ New event" primary; `callout` = collaboration invites or door-ready; @@ -1489,7 +1494,7 @@ date-chip rows: right slot = "12 / 30 going" bar │ "Finish & publish" pill │ ``` - **Remove the always-open create form** from the list page. "+ New event" opens a create page - (Y3 layout: form left, public event card preview right) and "New series" opens the series + (R3 layout: form left, public event card preview right) and "New series" opens the series variant. The `useQuickCreate("event")` hook opens the same page. - `CollaborationInbox` and `EventSeriesCollaborationInbox` collapse into **one callout** when invitations are pending ("2 venues invited you to co-host. Review →") that opens a drawer. No @@ -1502,7 +1507,7 @@ date-chip rows: right slot = "12 / 30 going" bar │ "Finish & publish" pill │ Cancelled (red word pill), Completed (grey). - **Empty (no events):** dashed "+ New event" placeholder row: "Your events will appear here." -### S12. Event detail ← Y7, Y8, Y9, Y10 +### S12. Event detail ← R7, R8, R9, R10 **Layout:** L4 `DetailLayout` (page). The slots are filled as follows: `identity` = date chip; `actions.primary` = status-driven (with `targetsTab="checkin"` so it hides on that tab); `tabs` = @@ -1524,7 +1529,7 @@ Overview Registrations 14 Check-in Tickets & invites Feedback Team Setting |---|---| | Overview | Stat cards (Going n/cap with bar · Waitlist · Awaiting approval · Invites sent), "Latest registrations" list (5 rows + "All registrations →"), an "At a glance" card (When, Where, Who can join, Price), and the door-ready CTA card when tomorrow or today and offline check-in is enabled. | | Registrations | `EventRegistrationRoster` as a P14 table with status pills (Going, Waitlist, Pending) and inline Approve / Decline… for pending; `EventRegisterMember` as "+ Add registration" (outline). | -| Check-in | The Y8 layout: progress stat plus offline callout (`OfflineCheckInOperator`), camera and Start scanning (`EventCheckInScanner`), find a guest, toggles, Recent check-ins with **inline Undo only if the backend supports reversing a check-in** (otherwise omit; do not fake it), and the door-station card (`EventStationSettings`). | +| Check-in | The R8 layout: progress stat plus offline callout (`OfflineCheckInOperator`), camera and Start scanning (`EventCheckInScanner`), find a guest, toggles, Recent check-ins with **inline Undo only if the backend supports reversing a check-in** (otherwise omit; do not fake it), and the door-station card (`EventStationSettings`). | | Tickets & invites | `EventTicketsPanel`, `EventPromosPanel`, `EventInvitesPanel`, each a section; shown only for enabled `ticketing_features`. | | Feedback | `EventFeedbackPanel` and responses; certificates (`EventCertificateActions`). | | Team | `EventOrganizers`, `EventCollaborators`. | @@ -1533,10 +1538,10 @@ Overview Registrations 14 Check-in Tickets & invites Feedback Team Setting - **Header primary by status:** draft → "Publish…" (confirm, because it notifies the public); published → "Open check-in" (hidden on the Check-in tab); completed → "View feedback". Cancelled or completed: a read-only banner "This event is completed. It can't be edited." -- **Series** (`EventSeriesDrawer`) gets the same page treatment per Y10: upcoming-date cards, +- **Series** (`EventSeriesDrawer`) gets the same page treatment per R10: upcoming-date cards, details, a "Who can join" chip radio, agenda, and the "Delete series…" danger zone. -### S13. Bookings (`bookings`) ← Y2 + Y7 +### S13. Bookings (`bookings`) ← R2 + R7 **Layout:** L3 `ListLayout`. The slots are filled as follows: `stats`; `tabs` = Requests / Upcoming / Spaces (or Spaces only in the minimum variant); `body` = rows. The space detail is L4 @@ -1553,11 +1558,11 @@ Spaces tab rows: [tile] Woodshop bench · Workshop · (Staff approval) (Public) **Minimum change:** the spaces list as P6 rows with pills replacing the 5-column table, and the "Awaiting approval" stat linking to the first space with pending requests. **Better** (needs a cross-space bookings list endpoint, so flag it): the Requests and Upcoming tabs. -- The space detail opens as a page (Y7 pattern) with tabs Calendar, Requests, Settings (the +- The space detail opens as a page (R7 pattern) with tabs Calendar, Requests, Settings (the `BookableSpaceForm` rows) and Danger (deactivate). - **Empty:** "No bookable spaces yet. Create one to start taking bookings." -### S14. Reports (`reports`) ← Y6 (form card) + P4 +### S14. Reports (`reports`) ← R6 (form card) + P4 **Layout:** L4 `DetailLayout` (page) with `asideSide="start"`. The slots are filled as follows: `aside` = the grouped report list; `title` = the selected report; `actions` = Export primary and @@ -1584,7 +1589,7 @@ Operations reporting for Fab Lab Kochi. - **Mobile:** the report list becomes a select sheet at the top. - **Empty data:** "No activity in this period. Try a longer range." -### S15. Audit log (`audit`) ← Y1/Y2 activity rail as a full list + P14 +### S15. Audit log (`audit`) ← R1/R2 activity rail as a full list + P14 **Layout:** L3 `ListLayout width="wide"`. The slots are filled as follows: `callout` = verification status (success pill, or a danger panel if broken); `filters` = AuditLogFilters with @@ -1610,7 +1615,7 @@ YESTERDAY … - **Empty with filters:** "No entries match these filters. Clear filters." - No Undo or edit affordances anywhere (append-only). -### S16. Member area home and tabs (`/member`, `/member/:tab`) ← Y1 (home), Y26 (card), Y16 (account) +### S16. Member area home and tabs (`/member`, `/member/:tab`) ← R1 (home), R26 (card), R16 (account) **Layout:** L1 `AppShell variant="member" compactNav="bottom-tabs"`. Home = L2 `DashboardLayout` (`rail` = notices). Loans, Bookings, Events, Payments and Inbox = L3 @@ -1637,7 +1642,7 @@ Your member card ▸ (ticket card with QR — opens the full card) convert to links (PLAN Phase 4 decision). Keep the `#main-content` target. - **Callout priority:** waiver acceptance needed, then a payment due, then an overdue loan, then application status ("Your application is waiting for 2 vouches"). -- **Not a member / signed out:** the home becomes the join page. A Y23-style card ("Join Fab Lab +- **Not a member / signed out:** the home becomes the join page. A R23-style card ("Join Fab Lab Kochi": policy summary, `AdmissionJoin` / `JoinMembershipCta` as the primary, "Have an account? Sign in") beside a description of what membership gives. `InvitationRequestForm` and `MemberClaimRedemption` are secondary links. @@ -1645,15 +1650,15 @@ Your member card ▸ (ticket card with QR — opens the full card) (`MemberHistory`). **Payments:** dues stat cards plus `MemberPaymentRows` with an inline "Pay" primary pill per row; archived payments as a link row at the bottom. **Inbox:** `MemberNotices` and `MemberNotifications`; empty "No notices need your attention." **Account:** - the Y16 settings rows (profile, two-step sign-in link, `MemberCertifications`, referrals, + the R16 settings rows (profile, two-step sign-in link, `MemberCertifications`, referrals, directory listing) and the member card. -- **Member card** (`MemberCardPanel`) = the **Y26 ticket**: a makerspace header band, a name / +- **Member card** (`MemberCardPanel`) = the **R26 ticket**: a makerspace header band, a name / member since / role grid, a perforation, the QR, the caption "Show this at the desk. It only holds a private member link." (verify that statement against the card payload before using it), and "Add to wallet" / "Save card" where supported. Offline note when cached. - **Restoring state:** a full-page skeleton of the shell, not "Restoring session…" text. -### S17. Public catalogue (`/m/:slug`) ← Y18 (sticky right card) + P10 chips +### S17. Public catalogue (`/m/:slug`) ← R18 (sticky right card) + P10 chips **Layout:** L9 `PublicLayout width="wide"`. The slots are filled as follows: `aside` = `ActionCard` (the request cart: `form` = purpose and contact fields, `primary` = "Send request — @@ -1676,11 +1681,11 @@ Sort: Name ▾ │ What's it for? [ footer: [logo] Powered by space works · Privacy · Terms ``` -- **Shared `PublicShell`** (PLAN §3.4): a Y18 48 px header with the makerspace brand, then nav +- **Shared `PublicShell`** (PLAN §3.4): a R18 48 px header with the makerspace brand, then nav links (only the modules that are on), then a "Sign in" outline pill. - The category sidebar becomes a **chip row** (scrollable) at <1280 and stays a left list at ≥1280 if there are more than 8 categories. -- **Request panel = the Y18/Y23 sticky card.** Its counts and title change with state: empty +- **Request panel = the R18/R23 sticky card.** Its counts and title change with state: empty ("Nothing added yet. Tap Add on any tool."), with items, submitted (a success state in the card: "Request sent. We'll email you when it's ready.", plus the public token link). The Borrow/Scan switch is a P9 segmented control. Access-policy copy (the three variants in @@ -1692,7 +1697,7 @@ footer: [logo] Powered by space works · Privacy · Terms - **Never expose** locations, box IDs, QR, evidence or hidden counts. The availability pill shows `exact_count` only when allowed, else the status word. -### S18. Public item detail (`/items/:itemId`) ← Y21 (detail with right rail) + Y18 sticky card +### S18. Public item detail (`/items/:itemId`) ← R21 (detail with right rail) + R18 sticky card **Layout:** L9 `PublicLayout` + `PublicDetailLayout`. The slots are filled as follows: `cover` = item image; `chips` = category and kit; `sections` = Description and Borrowing terms; @@ -1713,7 +1718,7 @@ Borrowing terms (grey inset box) └─────── - **Not found:** a centred card "This item isn't in the public catalogue." with "Back to the catalogue". -### S19. Public event page (`/events/:publicToken`) ← Y18, Y19, Y23, plus Y26 confirmation +### S19. Public event page (`/events/:publicToken`) ← R18, R19, R23, plus R26 confirmation **Layout:** L9 `PublicLayout` + `PublicDetailLayout`. The slots are filled as follows: `facts` = date chip block and venue block; `sections` = About / Schedule / Speakers / Venue / FAQ (only @@ -1726,21 +1731,21 @@ order = L9 `width="narrow"`. `EventPrice` folded into the CTA label ("Register — ₹200", "Register — it's free", "Join waitlist", "Apply"). - **Registration form placement:** inside the sticky card when the form is short (no - `custom_form` and no ticket choice, the Y23 pattern); otherwise the card CTA opens a modal or + `custom_form` and no ticket choice, the R23 pattern); otherwise the card CTA opens a modal or sheet with `EventRegistrationForm` (tickets, promo, custom fields). - "Add to calendar" and "Share" are the two outline half-buttons in the card. The share error becomes a toast ("Couldn't share. Link copied instead." when the clipboard fallback succeeds). - **Closed:** the card shows "Registration is closed" plus the cutoff time, with no primary. **Not found:** a centred card. - **After registering:** a confirmation state in the card, then a link to the **ticket page** - (Y26) if tickets or check-in exist. + (R26) if tickets or check-in exist. - **Events list** (`/events`): P6 date-chip rows grouped by series (series eyebrow), with search and filters (Free, Registration open) as chips and the calendar view as a segmented toggle (List | Calendar). The inline "Register" expand-in-place becomes a link to the event page (one primary per page; forms live on the event page). -- **Feedback page** (`/events/:token/feedback`) = Y25 exactly. +- **Feedback page** (`/events/:token/feedback`) = R25 exactly. -### S20. Staff sign-in (`LoginPanel`) ← Y11 +### S20. Staff sign-in (`LoginPanel`) ← R11 **Layout:** L8 `AuthLayout`. The slots are filled as follows: `brand.headline` = the accent phrase; `brand.showcase` = floating SpaceWorks object cards; `eyebrow` = Staff console or Front @@ -1760,7 +1765,7 @@ desk; `title` = "Welcome back."; `social`; `form` = Username and Password; no `m - **No segmented Sign in / Create account** (staff accounts are invited). - **Mobile:** the brand panel collapses to a 120 px band above the form or is hidden. -### S21. Member sign-in / sign-up (`MemberAuthPanel`) ← Y11, Y12, Y13 +### S21. Member sign-in / sign-up (`MemberAuthPanel`) ← R11, R12, R13 **Layout:** L8 `AuthLayout` with `brand.tenant`. The slots are filled as follows: `modeSwitch` when self-serve. Verify, MFA and reset = L8 `CenteredCardLayout` (`media.icon`, `content` = code @@ -1770,16 +1775,16 @@ boxes or the strength meter). logo, the welcome line from branding). The segmented control **Sign in | Create account** appears only when `selfServeAccounts`; otherwise the muted line "This space doesn't run self sign-up. Ask a staff member to add you." -- Sign-up fields: Name, Email, Password (with the Y12 strength meter and rule list), Phone · +- Sign-up fields: Name, Email, Password (with the R12 strength meter and rule list), Phone · Optional. Helper under Create account: "One account works at every makerspace you join." -- **Verify email** = a Y13 centred card (envelope tile): "Check your email", "We sent a code to +- **Verify email** = a R13 centred card (envelope tile): "Check your email", "We sent a code to **meera@…**", a 6-box code input (the P28 digit boxes at 46×56 with `autocomplete=one-time-code` on a single hidden input for paste), primary "Verify email", link "Resend code" (with a countdown). - **MFA** (`MfaFlow`) uses the same centred card with code boxes and "Use a recovery code - instead". **Reset password** (`PasswordRecoveryFlow`, `ResetPasswordPage`) = Y12 exactly. + instead". **Reset password** (`PasswordRecoveryFlow`, `ResetPasswordPage`) = R12 exactly. -### S22. Invitation accept (`StaffInvitationAcceptPage`, `OrganizationInvitationRedeemPage`) ← Y14, Y15 +### S22. Invitation accept (`StaffInvitationAcceptPage`, `OrganizationInvitationRedeemPage`) ← R14, R15 **Layout:** L8 `CenteredCardLayout width=480`. The slots are filled as follows: `media.avatars` for a staff invite or `media.banner` for an organization invite; `content` = the password field @@ -1809,10 +1814,10 @@ plus the MFA disclosure; `primary` = "Accept invitation"; `footer` = account hin - **Success:** a centred success card "You're in." plus a "Sign in" primary (today a link). - **Invalid or used token:** "This invite link has already been used or has expired. Ask the person who invited you for a new one." -- An organization invite uses the Y15 banner variant (organization colour band) with the scope +- An organization invite uses the R15 banner variant (organization colour band) with the scope sentence. -### S23. Kiosk station (`/kiosk/:stationToken`, `KioskStationPage`) ← Y27, Y28 +### S23. Kiosk station (`/kiosk/:stationToken`, `KioskStationPage`) ← R27, R28 **Layout:** L10 `KioskLayout`. The slots are filled as follows: `mode` lock → ready → work; `lock.pinLength=8`; `work.capture` = `KioskOnlineScanner`; `work.panel` = L7 `FlowLayout @@ -1820,7 +1825,7 @@ presentation="kiosk"` (the handover) or the result card; `confirmLock` = the pen dialog; `idleCountdown`. The offline queue renders in `work.panel` as a table plus a panel callout. -- **Locked:** the Y27 layout. Makerspace tile, the context line "Fab Lab Kochi · Handover +- **Locked:** the R27 layout. Makerspace tile, the context line "Fab Lab Kochi · Handover station", the title "Enter the station PIN", the helper "Ask a manager for the 8-digit PIN. It unlocks this device only.", **8 digit boxes** (the current PIN is exactly 8 digits) plus the keypad, and "Unlock station" disabled until 8. The keypad must not break hardware-keyboard entry @@ -1828,7 +1833,7 @@ callout. replaced by "Reconnect to unlock this station" and an "Offline" pill. - **Unlocked, no operator:** a centred card "Station ready", the station name and kind, and the primary "Start operator shift" (disabled offline, with the reason). -- **Working:** the Y28 layout. Header: station name, "Fab Lab Kochi · Handover station", +- **Working:** the R28 layout. Header: station name, "Fab Lab Kochi · Handover station", right side shows the **queue count** ("2 waiting"), an "● Online" / "● Offline · 3 queued" pill, "Full screen" (icon button) and "Lock station" (outline). Body: camera left (`KioskOnlineScanner`), right = the current handover (`KioskOnlineHandover`) as the S3 steps in @@ -1843,7 +1848,7 @@ callout. primary. - **Idle reset** shows a 10 s countdown overlay ("Locking for privacy in 10… Tap to keep working") before locking when no work is pending. -- **Event door station** (`/event-check-in/:stationToken`) = Y27 then Y28 one-to-one. +- **Event door station** (`/event-check-in/:stationToken`) = R27 then R28 one-to-one. ### S24. Remaining staff tabs (Phase 6 sweep), pattern assignment @@ -1860,8 +1865,8 @@ must use the layout names from §1A.11. The assignments below are the default la | warranty, devices | L3 + L4 drawer · Table plus drawer detail with P15 rows. | | cards (Member cards) | L3 · Camera scan block plus the card list; the scan result as a P29 result card. | | accountability | L3 · Table of borrowers with restriction status pills; row menu Restrict… / Restore. | -| organized, organization-analytics, organizations | L2 / L3 / L4-report · Y1 org cards for organizations; organized events reuse S11 rows; analytics reuses S14. | -| emailtemplates, email-logs, api | L6 / L3 / L5 · Y6 patterns (form card, empty state, code/accordion) for api; logs as a P14 table with status pills; templates as a list plus an editor page with a live preview (P18). | +| organized, organization-analytics, organizations | L2 / L3 / L4-report · R1 org cards for organizations; organized events reuse S11 rows; analytics reuses S14. | +| emailtemplates, email-logs, api | L6 / L3 / L5 · R6 patterns (form card, empty state, code/accordion) for api; logs as a P14 table with status pills; templates as a list plus an editor page with a live preview (P18). | | exports, backups, migration, usage, modules, platform* | L5 (restore/migration runs = L7) · P15 settings rows plus P16 danger cards for restore/purge; jobs show progress rows (JobTray). | --- @@ -1932,14 +1937,14 @@ across staff, member, public, auth and kiosk. Run it per screen at review time. --- -## 5. Where Yuyu doesn't fit, and the adaptation +## 5. Where the reference boards don't scale, and our adaptation ### 5.1 Dense operational tables (Inventory, Ledger, Stocktake, Transfers, Audit, Reports, Payments) -Yuyu's biggest list has 4 rows and 3 columns. SpaceWorks lists run to hundreds of rows and 8+ +The reference boards' biggest list has 4 rows and 3 columns. SpaceWorks lists run to hundreds of rows and 8+ numeric columns, sorted, selected in bulk and exported. **Card rows (82 px) would cut density by about 60%.** -- **Adaptation:** keep `DataTable`, but inside the Y4 card chrome (bordered, radius 12; header row +- **Adaptation:** keep `DataTable`, but inside the R4 card chrome (bordered, radius 12; header row 40 tall with 13 muted sentence-case labels; body rows **52–56** tall; hairlines; tabular numerals right-aligned). Sticky header. A column picker. A selection checkbox column, and the bulk bar (sticky) instead of toolbar buttons. Row click opens the detail; the row menu holds @@ -1952,7 +1957,7 @@ about 60%.** ### 5.2 Evidence capture (issue/return photo, remark, box and asset QR) -Yuyu captures nothing heavier than a QR scan. SpaceWorks evidence is mandatory, immutable, +The reference boards capture nothing heavier than a QR scan. SpaceWorks evidence is mandatory, immutable, uploaded through a presign to staging, and auditable. - **Evidence tile** (new primitive): a 16:9 dashed placeholder labelled "Issue photo · Required" with "Take photo" (primary, uses the existing `CameraCapture`) and "Upload from device" (link). @@ -1968,52 +1973,52 @@ uploaded through a presign to staging, and auditable. ### 5.3 Multi-step guided flows -Yuyu has none (its forms are single page). SpaceWorks has 5-step handovers in modals. +The reference boards have none (their forms are single page). SpaceWorks has 5-step handovers in modals. **Adaptation:** a task-mode shell (sidebar hidden, step rail plus a camera/work panel, the same -on kiosk), visually derived from Y28. A "Compact view" for experts. Each completed step echoes +on kiosk), visually derived from R28. A "Compact view" for experts. Each completed step echoes its captured value in the rail. ### 5.4 Permission and module density -Yuyu has 4 nav items and owner/admin roles. SpaceWorks has 42 gated tabs, custom roles with +The reference boards have 4 nav items and owner/admin roles. SpaceWorks has 42 gated tabs, custom roles with dozens of actions, and module switches. **Adaptation:** §2 sections with "More" disclosure and -flattening rules. The role editor uses Y6 checkbox cards grouped by area rather than a flat +flattening rules. The role editor uses R6 checkbox cards grouped by area rather than a flat checkbox list. Gated items are **absent, never disabled**, except reports, where an unavailable report explains which module enables it. ### 5.5 Settings scale -Yuyu settings is 3 rows. SpaceWorks has about 30 sections. **Adaptation:** an in-page sub-nav +The reference settings page has 3 rows. SpaceWorks has about 30 sections. **Adaptation:** an in-page sub-nav with scroll-spy (S9), a per-section dirty-state Save, and one danger zone at the very end. ### 5.6 Dashboard breadth -Yuyu shows 4 stats. SpaceWorks has 17 possible tiles. **Adaptation:** priority-ranked top 4 plus +The reference boards show 4 stats. SpaceWorks has 17 possible tiles. **Adaptation:** priority-ranked top 4 plus an "Everything else" disclosure. Tone on the number, not the fill. ### 5.7 Multi-tenant switching -Yuyu shows one org in the switcher. Superadmins may see dozens of makerspaces. **Adaptation:** the +The reference boards show one org in the switcher. Superadmins may see dozens of makerspaces. **Adaptation:** the switcher menu has search, recents first, and a "Switch makerspace" full list for superadmin. Single-tenant lock hides the chevron (the card is static). ### 5.8 Offline and kiosk integrity -Yuyu's offline story is a callout with "Download guest list". SpaceWorks kiosks keep an encrypted +The reference offline story is a callout with "Download guest list". SpaceWorks kiosks keep an encrypted command queue with replay recovery. **Adaptation:** a persistent connection pill in the station header, P5 panel callouts for offline state, a queue table with per-command status, and a destructive-confirm dialog for "lock with pending work" (safe action as primary). ### 5.9 Money -Yuyu has free events only. Payments need receipts and append-only corrections. **Adaptation:** no +The reference boards cover free events only. Payments need receipts and append-only corrections. **Adaptation:** no Undo and no inline edit. "Record payment…" opens `SettlementDialog` (method, reference, date). Corrections appear as "Amended" rows linked to the original. Amounts are always shown with currency, right-aligned with tabular numerals. ### 5.10 Activity rail data -Yuyu's rail is free. SpaceWorks audit is permission-gated and can be large. **Adaptation:** the +The reference rail is free. SpaceWorks audit is permission-gated and can be large. **Adaptation:** the rail renders only when `canViewAudit` (or uses the actor's own notifications), is capped at 6 items, and links to S15 with filters. It is hidden on narrow screens and for scoped roles whose audit would be empty. diff --git a/site/astro.config.mjs b/site/astro.config.mjs index 07e86e5..4e81969 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -19,7 +19,7 @@ export default defineConfig({ starlight({ title: 'Space Works Design', description: - 'The Space Works design system: logo, colour, type, voice, design tokens and UI specs.', + 'The Space Works design system: logo, colour, type, voice, design tokens, UI specs and Space Works UX.', logo: { light: './src/assets/kit/lockup-horizontal.svg', dark: './src/assets/kit/lockup-horizontal-reversed.svg', @@ -61,6 +61,7 @@ export default defineConfig({ ], }, { label: 'Tokens', items: ['tokens/using-the-kit', 'tokens/reference'] }, + { label: 'UX', items: ['ux/introduction'] }, { label: 'Specs', items: [{ autogenerate: { directory: 'specs' } }] }, { label: 'About', items: ['about/credits'] }, ], diff --git a/site/scripts/sync-kit.mjs b/site/scripts/sync-kit.mjs index 33f340d..706394d 100644 --- a/site/scripts/sync-kit.mjs +++ b/site/scripts/sync-kit.mjs @@ -35,7 +35,7 @@ cpSync(join(root, 'tokens', 'tokens.json'), join(assets, 'tokens.json')); // Spec pages: the first "# Heading" becomes the page title; the rest is published unchanged. const specPages = [ { file: 'ui-brand-spec.md', slug: 'ui-brand-spec', order: 1, label: 'UI brand spec' }, - { file: 'ux-yuyu-spec.md', slug: 'ux-patterns', order: 2, label: 'UX patterns' }, + { file: 'ux-spec.md', slug: 'ux-patterns', order: 2, label: 'Space Works UX spec' }, { file: 'ux-screens-longtail.md', slug: 'ux-long-tail-screens', order: 3, label: 'Long-tail screens' }, ]; for (const { file, slug, order, label } of specPages) { diff --git a/site/src/content/docs/about/credits.md b/site/src/content/docs/about/credits.md index 8e10acf..810f10e 100644 --- a/site/src/content/docs/about/credits.md +++ b/site/src/content/docs/about/credits.md @@ -9,10 +9,11 @@ The colour, type, logo, icons and voice come only from the Space Works brand gui the connected Figma file on 15 October 2026. `brand/figma-styles.json` records the source node IDs, the seven paint styles and the ten text styles. -## UX patterns +## Space Works UX -The UX patterns, layouts and interaction details in the specs are modelled on -[Yuyu](https://github.com/twilighty-abhi/yuyu). The reference boards are kept in +[Space Works UX](/ux/introduction/) is our own interaction system: the patterns, layouts and +interaction details in the specs. It was inspired by [Yuyu](https://github.com/twilighty-abhi/yuyu), whose +screens we studied as reference boards. The reference boards are kept in [`references/yuyu/`](https://github.com/SpaceWorks-HQ/SpaceWorks-Design/tree/main/references/yuyu) and are not republished on this site. diff --git a/site/src/content/docs/brand/introduction.mdx b/site/src/content/docs/brand/introduction.mdx index 424407c..e08ad6b 100644 --- a/site/src/content/docs/brand/introduction.mdx +++ b/site/src/content/docs/brand/introduction.mdx @@ -36,4 +36,5 @@ The symbol is a familiar box, built for a space full of possibilities. The visual identity comes only from the [Space Works brand guidelines](/brand/guidelines/), exported from the Figma file on 15 October 2026. This site -publishes that kit; when the two disagree, the guidelines win. +publishes that kit; when the two disagree, the guidelines win. How screens behave comes from +[Space Works UX](/ux/introduction/), our interaction system. diff --git a/site/src/content/docs/index.mdx b/site/src/content/docs/index.mdx index 2b5573a..a2595cb 100644 --- a/site/src/content/docs/index.mdx +++ b/site/src/content/docs/index.mdx @@ -35,6 +35,7 @@ structure gives every part a place, and the colours do the rest: teal grounds it + ## One source of truth diff --git a/site/src/content/docs/ux/introduction.mdx b/site/src/content/docs/ux/introduction.mdx new file mode 100644 index 0000000..f455f27 --- /dev/null +++ b/site/src/content/docs/ux/introduction.mdx @@ -0,0 +1,60 @@ +--- +title: Space Works UX +description: Our interaction system. The patterns, layouts and quality bar every Space Works screen follows. +--- + +import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; + +**Space Works UX** sets how every Space Works screen works: where things go, how you move between them, and +what happens when you act. The brand decides how a screen looks. Space Works UX decides how it behaves. + +## The rules to remember + + + + One filled primary action per page header, sticky card or callout. Everything else is an outline pill, + a text link or a row menu. + + + Staff and manager screens stay quiet: cream canvas, white panels, hairlines, no shadows. Public pages + get one bold moment. + + + Reversible actions happen straight away and offer Undo in place. Irreversible ones live in a danger + zone, behind a button that ends in "…" and a confirmation. + + + Numbers live on stat cards, tab labels and row slots, so the queue that needs you is never hidden. + + + "No parts added yet. Add your first item." An empty list always says what to do, and says something + different when a filter hides everything. + + + 44 px targets, a visible focus ring, one `h1` per page, a skip link, and tabs that are real URLs. + + + +## What it covers + +- **30 patterns (P1–P30):** the app shell and sidebar, page and detail headers, stat cards, callouts, list + rows, tabs, pills, buttons, the activity rail, settings rows, the danger zone, empty states, forms, auth, + public pages, kiosk screens, and Undo and confirmation behaviour. +- **10 layouts (L1–L10):** a closed set. Every screen is the app shell plus exactly one of `AppShell`, + `DashboardLayout`, `ListLayout`, `DetailLayout`, `SettingsLayout`, `FormPageLayout`, `FlowLayout`, + `AuthLayout` / `CenteredCardLayout`, `PublicLayout` / `PublicDetailLayout` and `KioskLayout`. +- **Screen specs:** the core screens, then the long-tail screens, each mapped to a layout and its patterns. +- **A quality bar** that every screen has to pass before it ships. + + + + + + + +## Where it comes from + +Space Works UX is our own. It was inspired by [Yuyu](https://github.com/twilighty-abhi/yuyu), whose screens we +studied as reference boards (R1–R28 in the spec) and then adapted for Space Works: bigger tables, guided +flows, payments, kiosks and many roles. The visual identity comes only from the +[Space Works brand guidelines](/brand/guidelines/). See [Credits and licences](/about/credits/).