Skip to content

feat(builder): wave 3, one place for every property - #926

Draft
mortondev wants to merge 18 commits into
feat/builder-8-wave2from
feat/builder-9-wave3
Draft

mortondev wants to merge 18 commits into
feat/builder-8-wave2from
feat/builder-9-wave3

Conversation

@mortondev

Copy link
Copy Markdown
Member

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

  • One row per property (F14).
    • Style tiles become their property's row (Shape becomes Corners, Fill becomes Background, Scale becomes Text size, and so on), with a Custom tile that reveals the exact field.
    • Each row shows the value actually in use.
  • Simple by default (F51).
    • Panels open with Background, Text colour, Text size and Corners. The rest is under More.
    • The state tabs are now a "State: Normal" chip, and values are rounded to whole pixels.
  • Exact values in one layer (F52). At the everywhere scope an exact value is written to the tune when the tune can hold it. Reset now returns to the theme default instead of an older AI value; there's a test for the critique's case.
  • No focus ring colour (F66). It's removed from the panel and the AI contract. Stored values are dropped when a design is read.
  • Plain tags (F50).
    • Canvas tags read like "Headline · Hero", with a database glyph only on customer content.
    • The Live pill and the kind tags are gone.
    • Scope wording comes from one table, and a plain sentence ("Applies to the header") replaces scope pickers that had only one choice.

Elements catalogue

  • Post cards and Panels (F10). Post cards (Normal, Hover, Focus, lift, shadow) are split from static Panels (Normal only). Stored card looks are migrated when a design is read, and a click on a feed card picks Post cards.
  • Tabs and filters (F11). A new element with Normal, Hover, Current and Focus states, marked data-qb-part="tab". aria-pressed, aria-selected and aria-current count as Current, and its corners follow the button corners.
  • One hover source (F65). The Theme Hover tiles write element hover values, and the separate MOTION_CSS lift is removed. Stored shape.hover: 'lift' is migrated when read.
  • Summary rows (F49). "Post card look ›", "Status look ›" and "Menu look ›" link to the element instead of being a second editor. Shadow moves into Post cards.

Theme, logo, composer

  • Fixed Theme order (F28): Brand kit, Colours, Fonts, Style, Elements (always visible), Site images, Advanced.
  • The logo is its own pickable element (F07). Header › Logo has Icon, Title, Full logo, Height (16–64px) and Logo colours, and the Brand kit links to it. Styles › Logo and Advanced › Logo size are gone.
  • A Composer part (F15).
    • Its words are edited in place, in the View › Language locale.
    • The empty feed title moves to the Feed part, with "Preview empty".
    • The legacy Words panel is removed.
  • Dead code removed (F77). The old Appearance control files and their tests are gone. A new guard test fails if a design field gains a second editor.

Quick find (F09)

  • Generated from the rows the panels draw. There are 317 entries for a typical design, including every element property, each with a readable path such as "Elements › Main buttons › Background".
  • Choosing a result closes sheets that don't contain the target, navigates and opens the section, then waits for the control, focuses it and outlines it in neutral grey.
  • A focus test opens every entry and checks that focus lands on that entry's own control, never on the page body.

Testing

  • Typecheck, lint and Prettier are clean.
  • 6,391 tests pass across the designer, appearance, design (shared and domain), public and portal components, locales, policy and client.
  • Checked in a browser on a seeded database:
    • Theme sections appear in the new order.
    • Clicking the logo opens Logo, and clicking a feed card opens Post card in the simple view with its State chip.
    • Clicking a sort tab opens Tab.
    • Ctrl+K for "button colour", "custom css", "density" and "corners" focuses the right control each time.
    • No page errors.

Notes

  • Merge choices across the three parallel parts:
    • Post card Shadow is a property row with Custom tiles.
    • Choosing a card Look clears any shared border or shadow set from the theme, since card values are now shared between Post cards and Panels.
  • Not in Quick find yet: the Brand match row, Upload a font, extra colours, the hero's focal point and alt text, and block Column, Row and Height. Picked-element rows are indexed for the Normal state only.

…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

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant