Repository navigation
Conversation
…n view Theme > More hid the element catalogue, page width, motion and custom CSS behind three kinds of disclosure (a More details, an Advanced card and a Segmented control), so the properties people look for most were the hardest to find. Theme now reads in one fixed order: Brand kit, Colours, Fonts, Style, Elements, Site images, Advanced. Page width (one name: Narrow, Standard, Wide) and hover motion sit under Style as PropertyTiles beside the look, corners, appearance and background. The element list is always visible and opens each element's panel in place with Theme as its breadcrumb. Custom CSS is the only thing left closed, under Advanced, still counted when opened. Asking for Theme at a style (such as the page panel's width row) now scrolls to Style instead of opening More. Styles > Logo and the logo size under Advanced are gone from Theme; the logo becomes its own pickable element next.
The logo was edited in four places (the brand kit, Styles > Logo, the logo colours twice, and its size under Advanced), while a click on the logo picked "Header and menu", whose only logo control sent people to Theme. A click on the header logo now picks the logo itself: Header and menu > Logo, with Icon, Title, Full logo, Height (16 to 64 px, a ScrubNumber) and Logo colours. Escape goes up to the header, the header panel opens it from a summary row, the canvas outlines it by its data-qb-part, and Ask AI is scoped to the logo's fields while it is picked. Each logo field now has one writer: the rows live in logo-rows.tsx and the brand kit draws the same Icon and Title rows (the source) plus a "Size and placement" row that picks the logo. Logo colours leave the brand kit (its From your logo swatches stay beside the brand colour) and the full logo leaves Site images. LogoControls, LogoHeight and the unused logo-extras rows are removed. Every stored logo field (display, mark, wordmark, height, name, logoColours) keeps its meaning.
The element panel and the design AI both offered a focus ring colour (style.<element>.focus.ring), against the decision that focus rings stay neutral grey. The property is gone from the token registry, so the panel, the schema and the AI contract (styleText, stylePath, the edit eval) no longer offer it. The ring width stays and draws in the neutral --ring. Stored designs that still hold a ring colour, in the site, dark, block or override looks, read with it dropped quietly as a retired key, so saving and rendering them keeps working.
…oes back to the theme An element's exact values lived in two layers: the tune (where the brand match writes) and the element's style, which the panel wrote over it. A change in the panel hid the AI's value, and Reset brought it back. At the widest scope (every one of the kind, all screens, normal state), the panel now reads the element's own tune value as set here and writes there when the tune can hold the value, clearing any style copy. Values the tune cannot hold (past its range, lower case, any colour, since tune colours are light mode's only) go to the style and clear the tune. Reset clears both, back to the theme. Values the menu and buttons share (tune.label) stay a brand value, as they reach more than one element. The separate 'set exactly' path is gone: no 'From brand, set exactly' source, no 'Reset exact values' menu item. A one-off shows every one's exact value as where it comes from. A style text size now runs 10 to 200px, as the tune's does.
… exact value The element panel showed one property twice: Style tiles above a row of the same name (Shape tiles and Corners 6px, Fill tiles and Background, Scale and Size, a heading's Weight 400 in tiles and 700 in the row). A brand choice that sets one property is now that property's row at the widest scope: its tiles plus a Custom tile that shows the exact field. An exact value in either layer picks Custom and shows its number, so the row shows the value in use; picking a choice clears the exact values that took its place, in the tune and the style. One-offs keep the exact field only, as the brand's choices are site-wide. Choices that are no one property's (a menu's look, card density, status markers) stay as the Style section's tiles.
A picked element opened at full complexity: about 17 controls, five state tabs and measured values such as 'Padding across 19.35 px'. The panel now opens with the element's background, text colour and size and corners (or what most people change on it, where it has none of those). Everything else sits under one More, grouped as before, which stays open for the visit once opened and starts open where a value inside is set. The state tabs become one quiet 'State: Normal' chip with a menu of the element's states, a mark on each that holds values. Inherited and measured pixel values show as whole pixels.
One "Cards" element styled every [data-qb-surface=card], so a hover lift set for post cards also lifted the filters, the welcome message and a post's details, which nobody can click. Post cards (marked data-qb-post on the feed and roadmap cards) now have their own element with Normal, Hover and Focus (focus while what they hold is focused), and Panels have Normal only, so they never lift. A canvas click on a feed card picks Post cards, and Theme's elements list leads with the elements most changed, Post cards before Panels. Stored designs keep their look: the read path (withoutRetiredKeys) splits a "card" look into post cards (all of it) and panels (its normal look), across style, dark, phone, page overrides, the filters' part override and Home blocks' own looks. The AI registry, examples and eval cases name the new elements.
Canvas and panel tags used jargon and mixed what a thing is with how far
a change reaches: 'Headline · Hero' carried 'Static · edit in place',
data values wore a 'Live' pill, panel titles had 'Element', 'Block' or
'Every page' beside them, and a no-op scope ladder with no choices sat on
the header, words, part and section-look panels.
The canvas tag now says only what and where ('Headline · Hero'), with a
database glyph for the workspace's own content and nothing for anything
else; screen readers still hear 'your data' and 'double-click to edit'.
The Live badge and its plumbing are gone. Panels drop the kind tag. The
scope words come from one table (SCOPE_WORDS), which the scope lines,
the element panel and its undo steps all read ('..., just this one').
Where only one reach exists, a plain sentence says it ('Applies to the
header (5 pages)') instead of a ladder.
Feedback's sort tabs, the filter pills and the roadmap's tabs hard-coded a full pill, so Corners never reached them and nothing could style them; a click on a tab picked the whole Feed part, and the Current state the registry described had no element that used it. They are now marked data-qb-part=tab and take the button corners (rounded-item) by default. A "Tabs and filters" element (Normal, Hover, Current, Focus; background, colour, edge, corners, size, weight, case) treats aria-pressed, aria-selected and aria-current as Current, sits after Menu items in Theme's elements, and a canvas click on a tab picks it. The part is documented for workspace CSS and the AI's feed and filters scopes may set it.
…lace The composer's prompt, its button and the empty feed's title lived in a legacy Words panel reached only from a Words link on the page panel, with its own language picker set to English, and the outline had no composer part to click. The Words panel is gone (site-inspector.tsx, words-section.tsx, the site:words route and its outline row, and the unused LanguagePicker). The feedback page now has a Composer part, picked on the canvas by its data-qb-section, whose panel lists its words (Prompt, Button) in the language View > Language shows; Edit opens each where the page draws it, using the same in-place words editing as headlines (new feedback.prompt, feedback.submit and feedback.emptyTitle marks over pages.feedback.copy, no schema change). In the builder the prompt is drawn as words over the empty field, since a placeholder cannot be edited, and editing the button opens the composer where its button is. The empty feed's title moves to the Feed part under When empty, with a Preview empty switch: the builder asks the frame to draw the feed with no posts (a small store relayed by message and asked again on each load), turned off again when the panel closes. Posts go to and Suggest similar posts are deferred as the critique says; neither had a control. Ask AI treats the composer as the feed.
Two hover motions stacked on the same card: Theme's Hover "Lift" drew its own translate and shadow (MOTION_CSS, on data-qb-hover=lift) on top of whatever Post cards' or main buttons' Hover set, so the element panel could not say what a card did under the pointer. The Hover tiles now write Lift as post cards' and main buttons' Hover values (rise 2px, floating shadow) and read the tile back from them; None and Arrow take those values back off, keeping anything else an element's hover sets. MOTION_CSS keeps only the link arrow. "lift" is no longer a shape.hover option: designs stored with it are read with Lift written as those element values, so they keep their look.
…ne writer per field section-controls.tsx and type-shape-controls.tsx (and motion-controls.tsx, whose last caller moved to Theme's Style) were copies of the old Appearance controls imported only by their own tests, so they kept second editors for design fields alive in the codebase and kept their tests green while nothing drew them. The words field, the welcome words, the image slot and most of the tile drawings were reachable only through them. All of it is deleted with its tests; the two live exports (the Update type and presignBandImage) move to design-update.ts, and logo-extras becomes a plain .ts module of helpers. A new guard, second-writer.test.ts, reads the builder's components for writes to the draft (assignments, ??=, delete and the helpers that write a field) and fails when a design path gains a second editor component. The paths written from two components today are listed with why and the finding that removes them (F49); a listed path may lose a writer, never gain one. The logo fields are pinned to logo-rows.tsx alone.
The post cards' look, their shadow, density, the status look, the menu style and the heading weight each had two editors: the Feed and Header part panels repeated the tiles their elements' panels already had, and the heading weight sat in both the brand kit and Section headings. Part panels now own behaviour and words only. Feed shows "Post card look" and "Status look" rows and Header a "Menu look" row; each says the look in a word or two and picks its element (every post card, status or menu item) on the canvas. Shadow moves into the Post cards element, beside Look and Density, while the look is Raised. The heading weight keeps one home, the brand kit's fonts; Section headings keeps its own exact Weight row for a value of its own.
Theme's new layout takes the element panel's one-row-per-property rows: panels drop the retired kind tag, and the tests read the reach note and the Custom corners field that wave 3A introduced.
- stored-design: withoutRetiredKeys runs split-cards, hover-lift and the ring-colour removal together. - Theme Hover tiles (theme-sheet.tsx) now write element hover values via hover-lift, since motion-controls.tsx is deleted; its tests moved to theme-sheet.test.tsx along with the styles-panel element order/gating checks. - Elements list uses 3B's ORDER and gating, without the logo (3C). - element-brand: the card tune group is shared by post cards and panels, so it is no element's own home (read as brand, never written at everywhere). - element-style-section: post card Look clears the shared card border/shadow; Shadow becomes the Shadow property's row (tiles + Custom) under 3A's rules; heading weight choice removed (brand kit fonts own it). - Header and Feed panels use 3B's summary rows with 3A's ReachNote and 3C's logo row. - second-writer allow-list shrunk: layout.cards, density, status, nav, navText and tune now have one writer.
Quick find was a hand-written list. Several of its labels were no longer
drawn anywhere, raw ids leaked into results ("Corners site:brand"),
element properties were not indexed at all, choosing Density switched the
page while the Theme sheet stayed open, and Custom CSS dropped focus on
the body because its fields mount late.
The index is now generated from what draws the rows: Theme's sections
and the part panels from one row table (find-rows.ts) whose stable names
and labels the panels render with, every element and each of its
properties from the element registry (element-rows.ts, now shared with
the element panel), and Home blocks' and page sections' settings from the
block registry. Each row carries its name as data-find, so an entry and
its row cannot drift apart, and each result shows a readable path such as
"Theme › Style › Corners" or "Elements › Main buttons › Background".
Choosing a setting now closes the sheets it is not in, shows its page,
opens Theme at its element view and state or picks its part, opens
Advanced or More where it sits, then waits for the row to draw before
focusing its control and outlining it in neutral grey. A row that never
draws leaves focus in the properties, never on the body. Search matches
words by prefix across label, path and synonyms, so "button colour",
"card shadow" and "tab corners" find their element rows.
A new test opens every index entry in the real right panel and checks
focus lands on that entry's own control and never under a sheet.
The quick find index now lives in components/designer/find-index.ts and is generated from the panels' own row tables and registries, so the old hand-maintained list and its tests have no remaining callers.
…sts and renders The builder's preview state was its own module, shared by the portal's preview context and the builder, so every portal page fetched one more script; it now lives in preview-draft-context, which every page loads already. The composer prompt was a wrapper component that rendered again on every keystroke in the title; it is a plain function now, so typing a title renders as many components as before.
This branch has not been deployed
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.
Wave 3 of the portal builder critique: one place for every property. Each design field now has one editor. Anywhere else that shows the field uses a summary row linking to it. Stacked on #925.
Element panels
Elements catalogue
cardlooks are migrated when a design is read, and a click on a feed card picks Post cards.data-qb-part="tab".aria-pressed,aria-selectedandaria-currentcount as Current, and its corners follow the button corners.MOTION_CSSlift is removed. Storedshape.hover: 'lift'is migrated when read.Theme, logo, composer
Quick find (F09)
Testing
Notes