[UX] Establish a design system and component library #364
Closed
opened 2026-08-25 20:39:25 +00:00 by claude-bot
·
9 comments
No Branch/Tag specified
main
feat/1328-fill-it-in
feat/1328-fill-frontend
feat/1328-fill-backend
feat/9-foundry-push
fix/9-foundry-live
feat/9-foundry-push-frontend
renovate/celery-5.x
feat/5-foundry-connect
feat/9-foundry-push-backend
feat/9-foundry-push-module
feat/5-foundry-frontend
feat/5-foundry-backend
feat/5-foundry-module
release/v4.8.0-rc1
docs/d341-convert-draft
feat/958-update-to-version
feat/958-convert-sheet
feat/958-pathbuilder-import-frontend
fix/1360-heading-order
feat/958-pathbuilder-import-backend
feat/11-weekly-digest
feat/1341-auto-update
docs/d332-superseded
chore/1345-names-cleanup
feat/1342-mentioned-pages
feat/1328-recipes
fix/1346-pgdg-key
perf/1247-shared-recording-stream
chore/132-gitleaks-allowlist
release/v4.7.1
spike/foundry-module
release/v4.7.1-rc1
docs/decisions-d332-d337
fix/bot-certifi
chore/release-4.7.0
chore/release-4.7.0-rc4
chore/deps-4.7.0
chore/release-4.7.0-rc3
fix/1280-quote-must-say-it
fix/1257-names-row-squeeze
chore/release-4.7.0-rc2
fix/1245-pg-upgrade-count
chore/release-4.7.0-rc1
feat/1257-names-in-prep
feat/1257-be
feat/1257-fe
feat/1257-rating-fixes
design/1328-fill-it-in
feat/1257-name-splits
fix/1326-redis-503
feat/1280-fact-suggestions
feat/1280-be
feat/1280-fe
design/1280-fact-suggestions
feat/1279-infobox
feat/1279-be
feat/1279-fe
feat/1321-shelf-copies
design/1279-infobox
feat/1243-table-find
feat/1257-who-rated
feat/1243-prep-drawer
feat/1243-sheet-pickers
design/1243-browse-in-place
feat/1315-hotwords-default
feat/1242-system-filters
feat/1257-more-name-styles
feat/506-leg3-hotwords
design/1242-system-filters
fix/1302-packs-polish
feat/1241-rules-library
design/1241-rules-library
feat/1245-postgres-18
integ/4.6.2
fix/1263-map-ui-2
fix/1263-map-server
fix/1263-map-ui-1
fix/1261-editor-link-menu
fix/1229-axe-4.14-name-mismatch
renovate/axllent-mailpit-1.x
renovate/eslint-monorepo
renovate/authlib-1.x
chore/release-4.6.0
chore/release-4.6.0-rc2
fix/460-rc2-screens
fix/460-rc2-server
fix/release-prerelease-exit
chore/release-4.6.0-rc1
feat/125-campaign-templates
feat/126-calendar
feat/1181-pictures-and-files
feat/1190-event-people
integ/4.6.0
feat/1191-line-of-the-night
feat/1200-starter-packs
chore/bot-multidict-and-playwright-group
fix/1202-this-week-answered-count
fix/122-124-leftovers
fix/1178-get-routes-that-write
fix/1203-journeys-check-names
design/125-campaign-templates
feat/126-calendar-engine
design/126-in-world-calendar
fix/1189-cited-stamp-in-range
fix/1189-v4-looser-check
fix/1189-richer-beats
fix/1113-export-bind-lists
fix/1154-release-notes-by-file
chore/release-4.5.0
chore/release-4.5.0-rc12
fix/1135-wiki-markup-as-text
fix/demo-usage-cost-in-window
chore/release-4.5.0-rc11
fix/1121-sheet-consistency
fix/1121-sheet-5e-terms-and-card-hp
copy/1122-busiest-pages
fix/1121-relevant-choices
fix/1121-wiki-create-npc-save-sheet-links
fix/1029-menu-stays-on-screen
docs/prototype-transcript-bin-wording
fix/1023-editor-save-row-back-scroll
fix/1020-search-exact-first
fix/1020-table-prep
fix/1020-shell-wordmark-footer-bell
fix/1020-wiki-article-party
fix/872-admin-kept-until-phone-tables
feat/l13-milestone-level-waiting
fix/1000-1001-bell-older-setup-context
fix/999-pronouns-in-prompts
fix/l19-other-class-feats
fix/1010-transcript-window-ordinary-confirm
docs/d254-d269-decisions
fix/702-member-search
fix/680-transcript-bin
fix/993-unidentified-prompts
fix/841-redis-timeouts
fix/990-binned-not-exported
fix/830-export-prep-shelf
fix/deps-audit-2026-10-01
fix/877-title-reads
fix/798-audio-received-at
build/lane11-17-j7
build/lane11-16-levelup
build/lane11-14-seams
build/lane11-14-seed
build/lane11-15-start
build/lane11-13-old-block-out
fix/980-review-r3
build/lane11-12-block
build/lane11-8-packs
fix/demo-seed-canonical-names
build/lane11-11-hot-half
docs/l11-decisions
build/lane11-7-convert
build/lane11-10-editing
fix/npm-audit-brace-expansion
build/lane11-9-sheet-page
fix/bot-pyjwt
build/lane11-6-content
fix/lane11-rank-names
build/lane11-2b-language
build/lane11-4-js-evaluator
build/lane11-3-server
build/lane11-5a-pf2e
build/lane11-5b-5e
feat/design-admin
build/lane11-2-evaluator
build/lane11-1-sheet-pieces
build/lane11-0-drawing
fix/943-quest-objectives-fk
fix/prepsheet-shelf-flake
fix/945-leftovers
build/demo-gm-copy
fix/901-email-landings
build/lane10-8-journeys
build/demo-seed-lanes-8-10
fix/887-gm-own-confirmation
build/lane9-5-j1-admin
fix/918-audience-click
fix/useasync-overtaken
ci/939-critical-path
test/937-backend-suite-speed
chore/claude-orchestration
fix/924-xp-gm-only
fix/session-stale-poll
docs/decisions-d239-d242
fix/930-pypdf
build/lane8-journeys
build/925-quest-editor
fix/894-private-page-pictures
build/lane8-p4-drafts
build/lane8-p7-map
build/lane8-p6-handouts-rules
fix/wiki-fixture-names
build/lane10-d227-test-usage
fix/rsvp-horizon-test-tuesday
fix/media-volume-owner
build/lane8-p3-editor
fix/912-unidentified-rename
build/lane8-p5-party-quests
build/lane8-handout-takeback
build/lane10-pickers
build/lane8-p2-article
build/lane8-6-map
build/lane10-7b-email-pages
build/lane8-5-handouts
build/lane10-7a-test-the-bot-pages
build/lane8-p1-pages
build/lane8-4-quests
build/lane8-2b-feeds
build/lane8-3-parties
build/lane8-2a-links
build/lane10-5-commands
build/lane9-6-seed
build/lane10-4b-reactions
build/lane9-4d-pages
build/lane9-4c-pages
build/lane10-4a-posts
build/lane10-3-email
fix/891-web-fonts
build/lane9-4b-pages
build/lane10-6-test-the-bot
build/lane10-2-notify-out
build/lane8-1-wiki-text
build/lane9-4a-frame
design/lane10-drawings
fix/j6-budget
fix/j5-phone-flake
fix/869-test-leaks
fix/843-record-start-followups
build/lane7-all
build/lane6-all
build/lane9-b2-server
chore/changelog-fragments
docs/decisions-d218
build/lane9-a-owner-overview
fix/rule-card-level-label
fix/834-retention-semantics
fix/o3c-record-without-announcement
docs/decisions-d179
docs/decisions-d131-d170
fix/851-commit-before-response
build/lane6-j3-seed
build/lane9-b1-d67-identity
fix/841-blocking-loop
build/lane9-c-backups-audit
fix/vote-race-and-start-commit
fix/recording-announcement
fix/proposal-read-leak
fix/last-admin-guard
build/lane6-p4c-planning
build/lane6-p4b-margin
build/lane6-p4a-sheet
build/lane5-j4-seed
build/lane6-base
build/lane7-pages-talk
build/lane5-pages
build/lane7-pages-safety
build/lane7-p3-safety
build/lane7-p2-notify
build/lane6-p3-planning
build/lane6-p2-history
build/lane6-p1b-items
build/lane7-p1-threads
build/lane5-p6-faces
build/lane6-p1a-sheet
build/lane5-p5-find
build/lane5-p4a-board
build/lane5-p4b-scenes
build/lane5-p3-live
build/lane5-base
build/lane5-p2-cards
docs/lane5-decisions
build/review-r2-wide
fix/review-r1
build/lane5-p1-scenes
build/demo-seed
build/lane4-journeys-j5
build/lane4-page-review-b
build/lane4-page-review-a
build/lane4-page-read
build/lane4-journeys
build/lane4-page-sess2
build/lane4-page-sess1
build/lane4-notes
build/lane4-discord-on-approval
build/lane4-scheduling
build/lane4-versions
build/760-session-number
build/lane4-decisions
build/797-erase-no-regenerate
build/test-race-audit
build/lane3-completions
build/794-dark-only
build/796-titles-scroll
build/791-retention-anchor
build/a11y-sweep
build/shell-followups
build/retention-preview
build/753-backup-readback
docs/d91-d93
build/lane3-j1
build/ci-harness-paths-selftest
fix/useauth-test-race
build/ci-harness-paths
build/777-ai-settings-read
build/lane3-settings-c
build/lane3-settings-b
build/lane3-j6
build/lane3-sessionform
build/lane3-settings-a
build/lane3-members
build/lane3-thisweek
build/lane3-story
chore/87-align-python-3.12
v4.8.0-rc1
v4.7.1
v4.7.1-rc1
v4.7.0
v4.7.0-rc4
v4.7.0-rc3
v4.7.0-rc2
v4.7.0-rc1
v4.6.2
v4.6.2-rc4
v4.6.2-rc3
v4.6.2-rc2
v4.6.2-rc1
v4.6.1
v4.6.1-rc1
v4.6.0
v4.6.0-rc2
v4.6.0-rc1
v4.5.3
v4.5.3-rc1
v4.5.2
v4.5.2-rc1
v4.5.1
v4.5.1-rc2
v4.5.1-rc1
v4.5.0
v4.5.0-rc12
v4.5.0-rc11
v4.5.0-rc10
v4.5.0-rc9
v4.5.0-rc8
v4.5.0-rc7
v4.5.0-rc6
v4.5.0-rc5
v4.5.0-rc4
v4.5.0-rc3
v4.5.0-rc2
v4.5.0-rc1
v4.4.1
v4.4.0
v4.3.0
v4.2.3
v4.2.2
v4.2.1
v4.2.0
v4.1.2
v4.1.1
v4.1.0
v4.0.1
v4.0.0
v3.11.4
v3.11.3
v3.11.2
v3.11.1
v3.11.0
v3.10.0
v3.9.1
v3.9.0
v3.8.0
v3.7.0
v3.6.1
v3.6.0
v3.5.0
v3.4.1
v3.4.0
v3.3.0
v3.2.0
v3.1.0
v3.0.0
v2.1.0
v2.0.0
v1.1.0
v1.0.0
v0.12.0
v0.11.0
v0.10.0
v0.9.1
v0.3.3
v0.9.0
v0.8.0
v0.3.2
v0.3.1
v0.3.0
v0.7.0
v0.6.0
v0.5.0
v0.4.0
v0.2.1
v0.2.0
v0.1.1
v0.1.0
Labels
Clear labels
backend
Issue related to the backend
bot
Issue related to the bot
bug
Something is not working
duplicate
This issue or pull request already exists
enhancement
New feature
frontend
Issue related to the frontend
help wanted
Need some help
invalid
Something is wrong
question
More information is needed
recording
Issue related to the recording system
security
Security hardening / vulnerability work
wontfix
This won't be fixed
No labels
backend
bot
bug
duplicate
enhancement
frontend
help wanted
invalid
question
recording
security
wontfix
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Quest-Board#364
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the August 2026 session lifecycle review (#319). Follows the mockup selection.
Why
The review found the same concept rendered differently on different pages: attendance implemented four times with different labels for the same toggle, summary editing appearing twice (editable in one place, read-only in the other with no indication that editing lives elsewhere), two full proposal-review UIs that differ in three user-visible ways, and error handling split between inline messages, page-level errors that destroy the rendered content, and native
alert()calls.That is what happens without a shared vocabulary of components. A redesign that does not produce one will drift back to the same state.
What
Extract the chosen direction into tokens and components: type scale, spacing, colour with a genuine dark mode, status and severity treatments, buttons and their hierarchy, form controls, empty states, loading states, error states, badges and counts, cards, modals and confirmations, and the async-progress pattern that v4.3.0 introduces.
Codify the interaction rules the review found violated: an error never destroys already-rendered content; every destructive action explains its consequence (the codebase already does this well in places and should be the model); every async operation shows progress and a next action on failure; disabled controls explain why.
Acceptance criteria
Two interaction rules decided for the design system (owner, 2026-09-15; surface catalogue #659):
D12, how strongly to confirm. Today it is typed names, browser
confirm(), or nothing, with no rule.D13, updates. A quiet "Updated — reload" notice when a new version is ready, replacing today's silent auto-reload. Installation stays with the browser; there is no custom install prompt.
The catalogue's shared-patterns surface (SH-7) lists every confirmation, error, empty, busy and stale state the library needs to cover.
This is lane 0 of the build (2026-09-21). Its technical design is §3 of
docs/design/v4.5.0-build-plan.md: the prototype kit's tokens verbatim intosrc/ui/tokens.cssand through Tailwind 4's@theme; dark mode by the kit'sdata-modeattribute; the sixty-seven icons as oneIconcomponent; the component list from the brief §8 made concrete (shell, page, controls, feedback, content, overlays, and the sheet's pieces for lane 11); the rules as code (D12's tiers, D22's leave guard, D36'sTarget, D65'sStop, the section boundary, the async pattern, 44 px targets); and a/designreference page in the dev build instead of Storybook. §3.4 says what happens to each of today's 20 pages and 39 components.Acceptance, beyond the list above: every component's states on
/designin both themes;eslint-plugin-jsx-a11yin the lint job (WCAG 2.2 AA is the stated standard, #367); nothing in the app uses the library yet, so main is unchanged for a person. Lane 1 (#727) is the first consumer. Umbrella: #366.Picking this up, 2026-09-22. The owner agreed the brief (#361, closed) and confirmed the plan's five assumptions, so lane 0 starts. First PR: the foundation —
src/ui/tokens.csswith Lapis Table's light and dark tokens read through Tailwind's@theme, dark mode bydata-mode, self-hosted fonts, theIconcomponent from the prototype's sixty-seven icons, the component conventions (one folder each, with astates.jsxthe/designpage collects), the/designpage itself in the dev build,eslint-plugin-jsx-a11yin lint, and the first components with their states and tests. The shell components and the content and sheet pieces follow in the same PR series. Nothing in the app uses the library until lane 1.Progress, 2026-09-22. Two PRs so far:
@theme inline; dark mode bydata-mode; self-hosted Figtree, Unbounded and Alegreya; the sixty-seven icons as oneIcon;Button,Banner,Stop,Busy,Modal,SectionBoundary,Target,useAsync,useLeaveGuard; the/designreference page (dev build only) with a test that fails if any state throws;eslint-plugin-jsx-a11yat error on the library.Field/Select/Textarea,Toggle,Chip,Segmented,Tabs,InlineError,AudiencePicker,DateTime,Card,List,Label,Meter,Fold,PersonDisc/Person,Doc/Section/KV/StatRow,Table,SaveState,Menu,useOnClickOutside. 26 components, 138 states, 1,041 tests.Remaining for lane 0: the shell components (app bar, Contents rail and phone sheet, account menu, bell, search, footer, live bar), and the sheet's pieces (Derivation, RankPips, Track, Resource, Dice, StepStrip, WhatChanges, Picker), which I intend to build when lane 11 starts rather than now, since only the sheet uses them. Nothing in the app uses the library yet; lane 1 (#727) is the first consumer.
PR #740 (the controls, content and overlays) is merged on green CI. Twenty-six components and 138 states on
/design; 1,041 frontend tests.The third and last slice is in progress on
build/lane0-shell: the app frame (AppShell,AppBar,Contentsas the desktop rail and the phone sheet with the You group,Footer,LiveBarin its three states,Bannersstacked worst first) and the three popovers (AccountMenu,Bellas arole="dialog"panel,Searchwith grouped results and D16's transcript switch). Phone versus desktop is decided by the kit's container query on the.approot, not by a prop, so one component renders both and/designshows the phone form in a 390 px box.PR #741 is the third and last slice: the shell.
AppShell/Book,AppBar,Contents/ContentsSheet,Footer,LiveBar,Banners,AccountMenu,Bell,Search. Thirty-five components, 160 states, 1,127 tests; lint clean; render-checked in both themes with headless Edge, which caught a bug from the first PR (a link drawn as a primary button had an invisible label: the base link colour outranked the button's).Reviewed against the kit rather than taken as delivered: the crumb bolds the campaign as lane 1's stills do; the phone's Contents sheet ends with Sign out after the theme, as the kit's You group does; Search gained the kit's Spoken and GM-only chips, the three-per-group cap with "All N in Sessions", the second-letter wait (CA-095) and the not-answering state. The PR body has the decisions.
After it merges, #364 stays open for the sheet pieces (Derivation, RankPips, Track, Resource, Dice, StepStrip, WhatChanges, Picker), which are best built when lane 11 (#737) starts and can use them. Lane 1 (#727) and the journeys harness (#726) are next.
PR #741 (the shell) is merged on green CI. Lane 0's three PRs are in: #739 the foundation, #740 the controls, content and overlays, #741 the shell.
src/ui/has 35 components and 160 states on/design, 1,127 frontend tests, lint clean under jsx-a11y at error, render-checked in both themes. Nothing in the app uses the library yet; that starts with lane 1 (#727), which also brings the journeys harness (#726).Left open here, deliberately: the sheet pieces (Derivation, RankPips, Track, Resource, Dice, StepStrip, WhatChanges, Picker), to be built when lane 11 (#737) starts and can use them. Anything lane 1 finds missing in the shell comes back here too.
Two corrections to the library, both found by lane 2 (#728) building on it. Neither is a question — both are fixed and merged — but this issue is still open and should carry the record.
1.
.labelshipped the version the look retired (fixed in #756)base.csshad:under a comment calling it a "small caps label". But
prototype/shared/looks.css:153says, in as many words:The port took
kit.css's rule and the look's--label-*tokens, but not the look's own three properties.:root[data-look]is on in every prototype page, so sentence case is what every drawing the owner reviewed shows — and the app readAN INVITATIONwhere the drawing reads An invitation, on the front door, sign in, Home, You and all five public pages.Now matches the look. A side benefit:
--ink-softrather than--ink-faintis 8.1:1 in light and 9.8:1 in dark against--paper, where it was 5.5:1 and 6.7:1.Worth checking for siblings. This is the failure mode of porting
kit.csswithoutlooks.csson top, andlooks.cssoverrides more than labels —:root[data-look] h1…h4is the one I checked and the port got right. Anything else the look re-declares deserves the same look.2. The base now sits in a cascade layer (fixed in #751)
Not a port error — a latent fragility that lane 2 happened to trip.
base.cssscopes the type to.qb, andAppFrameputs.qbon each chrome piece, so.qb { background: var(--paper) }and.appbar { background: var(--chrome-bg) }are equally specific on the same element. So are.qb aand each ofa.btn--primary,.foot a,.appbar a,.toc--sub a. Every one of those ties is meant to go to the component, and did — on source order alone, whichmain.jsxarranges deliberately and explains.Source order is not something the app controls. Adding a second eager consumer of
src/ui/tipped Vite into emitting the kit's CSS as a shared chunk, and a shared chunk's stylesheet is linked before the entry's. base.css landed last,.qbstarted winning, and the shell drew its near-white--chrome-inkon light paper: 82 contrast violations across the app bar, the Contents and the account links, and 18 red journeys.The fix is one line in
src/ui/index.css:An un-layered rule beats a layered one whatever order they load in, so "a component beats the base" is now a property of the stylesheet rather than of the bundler's output. Nothing else is layered, deliberately.
Two alternatives were measured and rejected:
cssCodeSplit: falseconcatenates with the entry last and makes the inversion permanent; and:where()on individual base rules treats one symptom at a time — applied to.qb ait made things worse, handing.appbar aand.toc--sub atheir win too.The rule for later lanes: do not rely on import order to win a tie against the base. If a component's rule needs to beat
base.css, it already does.Also, for whoever builds lane 11's sheet pieces
Choicesgained a named exportCheckin #759 — one.choicerow with a checkbox rather than a radio, for the setup wizard's "I have kept a copy somewhere safe".Choicesitself is untouched (zero removed lines, no CSS diff). The kit had no single checkbox:Choicesis radios andToggleis arole="switch"for a setting.And one thing that will bite any port of a
::before: Chromium folds CSS generated content into the accessible name and jsdom does not, so a state glyph drawn that way gives a component two different accessible names depending on who is asking. Unit tests pass, journeys fail, same assertion. Carry the state in anaria-labeland leave the glyph decorative.🤖 Generated with Claude Code
Signed off by the owner on the Redesign Review page, 2026-10-01, all seventeen pieces OK and the R3 re-check (/design for administrators) OK.
Their note: "Overall this looks great. However, selecting anything causes some of the modals to go blank. Not really sure why. I'm still going to sign off on this because the design work is done, I just am not 100% on the functionality, but I also don't think that this is critical to keep in the app upon release."
The blank modal specimens on /design are tracked in #1020. Lane 11's sheet pieces, the last thing this issue was open for, are on main. Closing.