TSP: theme-token styling, print-and-cut stationery, setup wizard on shared Stepper (CS-12101)#663
Merged
Conversation
…n default italian-cathedral and double-happiness-banquet keep their file paths (so instance links are untouched) but slim from BrandGuide documents to plain Theme cards: semantic tokens in :root, dark-mode variables in .dark, font cssImports, plus a small --tsp-* extras block (motif, grid, accent/primary shades). parisian.json is new — the components' built-in fallback palette promoted to a linkable Theme so the classic look is an explicit choice.
…cope The portaled root now takes the anchor's data-boxel-theme-scope attribute, so the theme's scoped stylesheet styles it directly — the full token vocabulary (semantic pairs, custom namespaces, future additions) flows with nothing to enumerate, and dark-mode container queries keep working. Anchors with no theme scope fall back to copying the resolved semantic tokens (full fill/foreground pairs) from the anchor, which is what carries an element-pinned default palette across the portal; the previous list missed the companion foregrounds and produced unreadable text on themed fills.
A soft accent wash now sits behind the active rail row, so 'you are here' stays readable even once the step completes and its dot flips to the done fill; the active dot's halo is stronger, and a completed step that is also current keeps the on-accent foreground for its check instead of being repainted in ink.
Styling now flows through the semantic theme tokens with a three-scope chain on every value — var(--tsp-X, var(--semantic, literal)). The 25 bespoke Parisian names (--paper/--navy/--gold/...) are retargeted onto --background/--primary/--accent and friends; the bespoke buildThemeVars inline pipeline is gone in favor of the standard CardContainer scoped style (dark mode works); font @imports move to the themes' cssImports; SVG decor derives from --accent via currentColor; a tsp-default-theme class pins the Parisian palette when no theme is linked so app-level tokens can't restyle the card. Standard-shaped controls adopt boxel-ui: toolbar and rail actions and the save-template form use <Button>/<BoxelInput>, re-skinned by the existing classes and anchored to the --primary/--primary-foreground pair. The planner's fitted template is rebuilt as four container-query format families (badge/strip/tile/card) with the real floor plan as the tile/card hero and content panels painted with the theme's primary. UX: the inspector handle gains a vertical Details label; host avatars in the header are display-only with a hover top-right unlink button.
Host gains an editorial isolated view (masthead, gold portrait ring, editable name, role as enum pills reusing the guest chip pattern) and role becomes an enumField over the six wedding roles. Both cards get four-family fitted templates (badge/strip/tile/card via the fitted-card container) in the invitation language — Host on the primary spine, Guest on cream with category dot and VIP star — with names painted in the theme's primary. Contrast pairs are enforced throughout (content on accent fills uses --accent-foreground; accent is never light-background text), and the isolated/fitted roots pin the default palette when no theme is linked. Also fixes two invalid color: var(#hex) declarations.
…mandatory The wizard's own shell (step state machine, rail, dots, action bar, modal scrim — ~250 lines) is replaced by the catalog <Stepper>; this file keeps only the four domain steps, the gold corner ornament, and three Stepper knobs (serif heading font, gold rail accents with a cream check, primary-tinted scrim). The --wz-* palette layer is gone — colors flow from the semantic tokens. The flow is now mandatory: step 1 needs name, date, AND venue; hosts and guests each need at least one entry; there is no Skip. The ✕ close remains as the escape hatch — dismissal isn't persisted, so the wizard reopens for incomplete planners. Newly added hosts and guests render as fitted cards in a responsive grid inside the step panels, and the event placeholders use generic examples.
The setup wizard now builds on the catalog Stepper, so the listing's spec set includes that cross-listing dependency alongside the planner's own 26 module specs.
The header and setup wizard now use datetime-local inputs so guests know when, not just which day; old date-only instance data still deserializes.
Host never had a custom edit, so Guest matching it makes editing consistent (default field editors; category stays an enumField dropdown). The strip fitted view gains a hairline inset frame, a soft gradient wash, and a muted ❧ ornament where the VIP tag would sit.
- table name in the theme primary with flanking hairline rules; the bottom ✦ ornament echoes it - fit-to-width names: font size scales by character count (the print sheet is hidden on screen, so it can't be measured) - optional event-logo watermark at 8% behind the card content - print deepens the small-caps kicker (accent mixed 50% into ink) for paper contrast, and print-color-adjust keeps backgrounds without the browser's background-graphics option
Printing (A4 print & cut): - print sheet hoisted to <body> on beforeprint/afterprint with a global print stylesheet, so ANY print path (menu, Ctrl+P, headless page.pdf) emits only the cards — no operator-mode chrome, no clipped pagination - the hoisted sheet re-attaches its theme (adopts the linked theme's scope attribute, or the pinned default palette class) so stationery keeps its colors outside the planner subtree - explicit A4 pagination (printPages): rows are packed per page so every page keeps a 12mm margin on all four sides and a card can never straddle a page boundary - each cell is its own bleed box (trim + 2mm bleed) with Illustrator-style trim marks outside the bleed; no outline or decorative frame at the cut line - @page { size: A4; margin: 0 } so the browser has nowhere to draw its date/URL header and footer UX: - inspector panel and its Details handle only render when a table, fixture, or seat element is selected; the stale Compose placeholder is gone - seats stepper in the table popover pinned to a compact −/value/+ group (BoxelInput defaults to width:100%) - icon-label gap restored on Duplicate/Delete and other flex buttons - header date is a datetime-local input; invite copy and fitted labels include the time - consecutive @Tracked declarations tightened to single lines
Staging Submissions PreviewThis PR's content is pushed to the staging submissions realm: https://realms-staging.stack.cards/submissions/ Changed listings: Changed folders:
Updated at 2026-07-17 07:51:25 UTC for commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Converts the Table Seating Planner to the pure theming system, adopts boxel-ui components across the planner, and layers a full print-and-cut stationery pipeline on top.
Theming adoption (CS-12101)
Theme cards instead of BrandGuide
Theme/italian-cathedral.jsonandTheme/double-happiness-banquet.jsonrewritten in place as lean plainThemeinstances (cssVariableswith:root{}+.dark{},cssImportsfor fonts); newTheme/parisian.jsondefaultbuildThemeVars()regex pipeline (inlining a BrandGuide's:rootonto the card root) is deleted — themes now flow through the standard CardContainer scoped-style pipeline, which also makes dark mode work for free via the container-query.darkblockThree-scope token resolution everywhere
tsp.gts,guest.gts,host.gts,table.gts,fixture.gts,person.gts,layout-template.gts,card-views.gts,seating-plan-popover.gts,setup-wizard.gts,fixture-glyph.gts,layout-preview.gts) resolves throughvar(--tsp-X, var(--semantic, literal))— component knob → semantic theme token (shadcn vocabulary) → brand literal--paper,--ink,--navy,--gold, …) and ~445 hardcoded hexes are gone; fills and text always use paired tokens (--primary/--primary-foreground,--accent/--accent-foreground) so contrast holds under any theme@importfont lines removed from component CSS — fonts come from the theme'scssImports, with literal font names kept as var-chain fallbacksThemeless ≠ ugly
tsp-default-themeclass pins the full Parisian semantic set on the card root, so app-level defaults can't restyle the planner arbitrarily; linked themes omit the class and winTheme across portals
46f065-popover) bridges the theme into its portal: primary = adopt the anchor'sdata-boxel-theme-scopeattribute; fallback = copy resolved semantic pairs for element-pinned palettesboxel-ui adoption
<Button>(@kindmapped: primary CTA →primary, outlined →secondary, ghost →text-only) while keeping the existing scoped classes as re-skins — focus states, disabled semantics, and theme-token defaults come from boxel-ui underneath<BoxelInput>(header event title/date/venue, table name, seats stepper, wizard fields); the seats stepper pins BoxelInput's defaultwidth:100%back to a compact −/value/+ groupgapon labeled buttons (including the portaled popover's Duplicate/Delete)Setup wizard
aef6db-stepperStepper component; all steps mandatory, ✕ close as the escape hatch; host/guest fitted cards render in the wizard panels via field componentsHost / Guest
@container fitted-cardqueries) plus editorial isolated views;Host.roleis an enumField (Guest.category precedent); Guest's custom edit template removed so both use the default editorEvent date
eventDateis aDatetimeField; header + wizard use datetime-local inputs and invite copy / fitted labels include the time; old date-only instance data still deserializesPrint & cut (A4)
<body>onbeforeprint/afterprintwith a global print stylesheet — any print path (menu, Ctrl+P, headlesspage.pdf()) emits only the cardsprintPages): rows packed per page, 12mm margins on every page, cards never straddle a page boundary@page { size: A4; margin: 0 }suppresses the browser's date/URL header/footer;print-color-adjust: exactprints backgrounds regardless of the background-graphics optionTesting
pnpm run lint(eslint + template-lint + glint) green throughoutpage.pdf({ format: 'A4' })— multi-page, themed, trim-marked, no host chromeLinear: CS-12101