Tokens (light + dark, neumorphism, 54/54 WCAG AA contrast pairs), the self-hosted JetBrains Mono fonts, and interactive mockups of every screen across three iterations. Iteration 3 is final: phone, tablet and desktop tiers, device preview, and "empty spool weight" terminology. The build scripts regenerate the screens byte-identically from source. Implementation is tracked in Maestro feature UI Refresh - V3.0 (224b72a1-8d84-4970-97ea-5dafa4d8ea3b). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.9 KiB
Extrudex Soft UI (Design System v3) proposal: summary
2026-09-18. This is a proposal only. Nothing was changed in the Extrudex
repository (still clean at dev 321ce0d) or in CTXD. Nothing was deleted.
What I built and where it is
Everything is in ~/Design/extrudex-v3/.
| File | What it is |
|---|---|
00-understanding.md |
Step 1: every field on each existing screen, every stored printer field, the v2.0 palette, and how CTXD differs from the code |
design-system-v3.css |
Step 2: the token set. v2.0 base kept, dark and light themes, neumorphism tokens (surfaces, raised and inset pairs, 4 intensity levels, radius scale), state tokens, reference components |
design-system-v3.md |
Step 2: every changed or added token with its reason, the state matrix, the dark-mode approach, 2 alternative palettes, and contrast tables for both themes (54/54 pass, plus 9 more pairs) |
fonts/ |
JetBrains Mono 400/500/700/800 woff2 (the same @fontsource 5.3.0 files the app ships) + OFL license |
screens/01-dashboard.html |
New standalone Dashboard: stock tiles, running low (net + gross, tare unknown shown), net kg by material, recent prints with weight source and upper-bound duration marks, compact printer strip |
screens/02-filament.html |
Inventory: working search and filters, table/cards toggle, the 5-part name, net remaining vs tare (+ source) vs gross, Edit, Delete with a confirmation dialog |
screens/03-spool-form.html |
Add/Edit spool with a scenario switcher: 409 conflict, 400 field errors, tare unknown, add new, plus a control-state specimen |
screens/04-printers.html |
Printers: stored fields only, a "dev today / dev + feature branch" data toggle, the MQTT dropped state (A1), and a Future, not built section |
source/ |
Build inputs (build.py makes each screen self-contained with base64 fonts) and checks/table.py, which regenerates the contrast table |
Every screen has a Light/Dark toggle (also ?theme=light or ?theme=dark in
the URL), has no network dependencies, and was checked at 1440 px and 390 px in
headless Chrome.
Decisions that go against DECISIONS.MD or the v2.0 rules
- Styling: "tokens verbatim… do not edit a token to suit a component."
v3 changes the values of a few v2.0 semantic tokens (
--surface-raised,--shadow-raised,--focus-ring,--radius-sm,--radius-md,--text-on-accentin light). This isn't editing a token to suit a component. It's a proposed new version of the design system, and each change has a reason in the .md. It still needs your call: adopting it means updating the guide atpages.cclfirst and then copying it verbatim intotheme.css, as the rule intends. - v2.0 brand rules "0 radius" and "flat shadows" are overturned. Soft UI
can't exist without them.
--radius-nonestays for tables and code. - Primary buttons changed color, dark theme (
#8f7ae0→#a99af0), because v2.0's ink-on-accent is 4.32:1 and fails AA. That's a brand-color shift on the most visible control. - CONTEXT scope: "printer status pages… deliberately out until daily use."
The Printers screen was designed because you asked for it. Note that a
printers view already exists on
dev, embedded in Floor.
Things I followed even where the code has moved past the docs:
- Weight source is shown with 4 values (
typed,sliced,recalled,unknown). DECISIONS.MD lists 3. The schema addsslicedand saysrecalledis no longer written. - Tare origin: the code (
resolveTareintypes.ts) falls back to a brand tare, labeled as a guess. DECISIONS.MD says tare comes "from spool_type, never from brand". The mockups never show a brand-derived tare.
Open questions
- Adopt or reject the two brand-rule changes (radius, shadows) and the lighter primary-button violet? Everything else depends on this.
- Floor → Dashboard + Printers. The proposal splits today's Floor into a stock-first Dashboard and a separate Printers page. Is that the direction, or should Floor stay as one page restyled?
- Weight source in
GET /api/prints. It's stored but not served, so the Dashboard needs a small API change. OK to add it? recalledvssliced. Should DECISIONS.MD be updated to sayslicedexists andrecalledis legacy? That's a CTXD edit, which I didn't make.- Brand-tare fallback. Code and DECISIONS.MD disagree. Which one is right?
- Serial display. I masked the middle of serials (
01P09A••••••4821). Show in full, masked, or hidden? - "Tare unknown only" filter is new on the Filament page (it helps work down the "weigh it yourself" list). Keep it?
- The 409 "reapply my change" button assumes the client can diff the fields you changed against the new version. Today the form only shows the server's message. Do you want that behavior, or just "reload"?
- Alternative palettes A (warm graphite) and B (slate/teal) are in the .md. Do you want either mocked up?
- CTXD is out of date (Dashboard.tsx, no auth, Bambu only, the design system URL). Should someone refresh CONTEXT.MD? I made no CTXD edits.
Proposed Maestro tasks (titles only, smallest first; not created)
- Add
weight_sourceto theGET /api/printsresponse - Add v3 warning and info status tokens to
theme.css - Add the
--border-controltoken and apply it to buttons, inputs and selects - Replace the v2.0 focus ring with the v3 2px offset outline
- Fix primary-button text contrast (4.32:1 → 6.14:1)
- Show a weight-source chip on each print row
- Show an upper-bound mark on durations with connection gaps
- Show "gaps not counted" on prints with null
connection_gaps - Show tare, tare source and gross weight on the Filament table and cards
- Show "tare unknown" (never 0) wherever gross weight is derived
- Add the "Tare unknown only" filter to Filament
- Map spool 400 messages to their fields in SpoolForm
- Add the 5-part name preview to SpoolForm
- Add the net/tare/gross scale diagram to SpoolForm
- Replace the SpoolForm 409 footer text with a conflict panel and reload actions
- Add a light theme with a stored per-browser toggle
- Add the neumorphism shadow and radius tokens (after the brand decision)
- Restyle buttons, inputs and chips with the v3 state matrix
- Split Floor into a stock-first Dashboard and a Printers page
- Render the connection-lost state on printer cards (after the printer-connections feature merges)
- Publish Design System v3 to
pages.ccland re-synctheme.cssverbatim
Iteration 2 (2026-09-18): navigation, data display and interaction
Everything is in iteration-2/. Iteration 1 above is unchanged, for comparison.
| File | What it is |
|---|---|
iteration-2/00-ux-review.md |
12 findings from iteration 1, the navigation model, flows ranked by frequency, interaction patterns, data-display rules, accessibility notes, 5 new questions, 18 more task titles |
iteration-2/screens/index.html |
Start here. Flow map plus 8 guided flows that deep-link into each state |
iteration-2/screens/01-dashboard.html |
"Needs you" queue first, then stock tiles, recent prints by day, printers and running low. Everything clickable |
iteration-2/screens/02-filament.html |
Saved views with counts, Stock/Weights columns, grouping, sortable headers, row ⋯ menu, weigh session |
iteration-2/screens/03-prints.html |
New. Needs attention (inline suggestions, grams, batch) · All · Running · Archived |
iteration-2/screens/04-printers.html |
Urgency order, jump strip, holder fill + Load, stale "last known" state, dev/feature toggle |
iteration-2/source/ |
data.js (one shared model), app.js (shell, palette, panel, toasts), components.css, pages, build.py |
What's shared across every page: the spool/print/load panel (deep-linked, Esc/back closes, j/k steps), the ⌘K palette, nav badges for work waiting, toasts after each save, a freshness indicator with an offline state, and a phone tab bar with a floating Add button.
Corrected from iteration 1: it showed AMS "slot" positions, which the database doesn't store. Iteration 2 shows holder fill ("3 / 4") instead.
Verified: every page and panel state loads with no console errors in headless Chrome, at 1440 px and 390 px, in both themes. There are no network requests, and no new colors (the v3 contrast table still applies).
Iteration 3 (2026-09-18): remaining screens, tablet and phone
Everything is in iteration-3/. Iterations 1 and 2 are unchanged.
- Start here:
iteration-3/screens/index.html(flow map, 14 flows) anditeration-3/screens/devices.html(every screen in live phone 390 × 844, tablet 820 × 1180 and 1180 × 820 frames). - New screens: 05 Lists · 06 Labels · 07 Access · 08 System · 09 Scan: holder · 10 Scan: spool, plus Add/Edit printer, Manage holders and Remove printer panels on Printers.
- Your answers from
iteration-2/00-ux-review.md, applied: Change spool (with real grams-returned wording), lightest-first weigh sessions with location, Filament badge for unverified tares, and load/attribute palette commands. - Responsive: three tiers (phone < 700, tablet 700–1180 with a compact
rail, desktop). Spec in
iteration-3/00-changes.md§4. - Findings: list names are case-sensitive in the schema; audit log,
low-filament report and machine tokens have APIs but no screens on
dev. - Verified: all 12 pages and 18 panel/palette states load with no console errors; checked at 390, 820 and 1180 px and desktop, in both themes. No network requests, no new colors.
Terminology update (2026-09-18): iteration 3's UI now says "empty spool
weight" everywhere it said "tare" (verified: no rendered text, tooltip, label
or placeholder contains "tare"). Code and API names (tare_grams etc.) are
unchanged. See iteration-3/00-changes.md §1b. Open item: GLOSSARY.MD in CTXD
still uses "Tare".