Layout
Board first. The board is not a card; it is the full-bleed layer underneath. The rails sit against it, each responsible for one job.
The leading rail is about what to add — templates, presets, assets, layers. It answers "what goes on this board", so its content is a set of interchangeable items shown as thumbnails or a list, with an unmistakable selected state. The trailing rail is about what to adjust — the property stack of the current object. It answers "what does the thing on the board look like", so its content is sections and field rows, with export pinned to its bottom rather than hidden at the end of a scroll.
That division is why the two rails are not interchangeable: adding is discrete choice, adjusting is continuous tuning. Merge them into one column and every value change makes the user re-find their place between thumbnails and sliders. Derived results (material usage, run time, piece count) don't get a column of their own — they sit with export at the bottom of the trailing rail, or are drawn straight onto the board as dimension labels.
Three columns, zero gap, zero radius. Rail widths are fixed (leading 320 · trailing 272) and the board absorbs the window — a parameter panel should not get wider just because the window did. Either rail collapses to a 40px rail; collapsing the trailing rail grows the board, and collapsing the leading rail suits the stage where the template is already chosen and it is pure parameter work. A generator with no content library (parameters only, nothing to pick from) should drop the leading rail entirely — two columns is enough, and an extra empty panel is worse than one column fewer.
The main flow has to be obvious at a glance
Input → configure → generate → review → adjust → export. Not every generator has every step: a live-preview generator has no "generate" step at all, and giving it a generate button is a defect, not a courtesy.
One vertical rhythm, one ladder
The contrast between 4 and 16 makes helper text unambiguously belong to the control above it; 12 < 16 makes a header unambiguously belong to the group below it. Panel side gutters are 16px throughout. If a layout only looks right at 14px, the problem is alignment, not the scale.
Group with lines, not boxes
Group with dividers inside a panel. The panel is already an enclosed region — drawing the boundary once is enough — and a card's radius competes with the input's radius for attention. Save cards for things that genuinely leave the plane: overlays, select popovers, dialogs. Those need a stroke-default plus a contact shadow to explain where they are floating.
The preview has three states, and all three need designing
| State | What it must show | Why |
|---|---|---|
| Initial | A sample result or the most recent one | An empty rectangle reads as "broken" |
| Computing | Stage name + a number + a way to cancel | Never an indefinite spinner. A spinner with personality is what you build when you don't know the progress — here you do |
| Done | Zoom, pan, compare, regenerate | Tuning parameters is a loop, not a one-way trip |
An error belongs next to the control that produced it, stating the cause and the next step. A toast at the top of the screen is where error messages go to be ignored.
Sizing and scrolling
Your size is set by the area the platform gives you, not by the viewport. Adapt panels with container queries, not media queries. Below 960px, switch to a stepped flow with a bottom toggle and pin the generate action to the bottom — and clear the vertical dividers when you do.
flex:1; writing only min-height:0 leaves the container at content height, so the preview column doesn't fill and the parameter column overflows instead of scrolling inside. ② Use a thin scrollbar (8px, drawn yourself) — a classic scrollbar eats 15px out of the padding and makes the right side of the panel look twice as wide as the left. ③ Never scroll horizontally, and never nest a scroll container inside a scroll container.