Move type scale and layout ahead of the five-card hub.

Type scale and layout return to the linear path before configure; the hub shrinks to five cards with updated navigation. Also fixes palette tooltip clipping, drops the design-context masthead and icon hub notes, and locks the mosaic shell to the viewport without overflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Abdul Wahab
2026-09-01 10:02:40 +05:00
co-authored by Cursor
parent 0b867f0e25
commit adabd62011
5 changed files with 138 additions and 191 deletions
+93 -116
View File
@@ -907,104 +907,6 @@ const questions = [
</div>
</section>
{/* Screen 04b: the configure hub. Every remaining question already
holds an answer (defaults are pre-filled the moment a surface tile
is checked), so the run can end here. The cards are skeletons;
summaries and edited marks are written by renderHub() in
scripts/palette-picker.js on every arrival. Placement is the
approved masonry: tall per-surface cards anchor the outer columns. */}
<section class="picker-screen" data-screen="04b" data-step="Configure" aria-hidden="true" aria-labelledby="picker-hub-title">
<div class="picker-container">
<div class="picker-hub">
<div class="picker-question-head">
<h2 id="picker-hub-title" class="picker-question-title">What else would you like to configure?</h2>
<p class="picker-question-lede">
Every choice below already holds a sensible default for your surfaces. Open any card to adjust it, or create your design context right away.
</p>
</div>
<div class="picker-hub-grid">
<div class="picker-hub-col">
<button class="picker-hub-card" type="button" data-hub-target="06" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Motion
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
<button class="picker-hub-card" type="button" data-hub-target="09" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Corners
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
</div>
<div class="picker-hub-col">
<button class="picker-hub-card" type="button" data-hub-target="05" data-hub-category="typography">
<span class="picker-hub-eyebrow">Typography</span>
<span class="picker-hub-title">Type scale
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
<button class="picker-hub-card" type="button" data-hub-target="11" data-hub-category="iconography">
<span class="picker-hub-eyebrow">Iconography</span>
<span class="picker-hub-title">Icon set
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
<button class="picker-hub-card" type="button" data-hub-target="08" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Boundaries
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
</div>
<div class="picker-hub-col">
<button class="picker-hub-card" type="button" data-hub-target="07" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Layout
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
<button class="picker-hub-card" type="button" data-hub-target="10" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Depth
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
</div>
</div>
<div class="picker-actions-stack">
<button class="ks-button ks-button-primary" type="button" data-hub-finish data-hub-target="12">
Create my design context
<span class="ks-button-arrow" aria-hidden="true">
<svg viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square">
<path d="M0 4h14M10 0l4 4-4 4"></path>
</svg>
</span>
</button>
<button class="picker-back" type="button" data-advance="prev">Back</button>
</div>
</div>
</div>
</section>
<section class="picker-screen" data-screen="05" data-step="Type scale" aria-hidden="true" aria-labelledby="picker-scale-title">
<div class="picker-container">
<div class="picker-scale">
@@ -1154,7 +1056,91 @@ const questions = [
</div>
</section>
{questions.map((question) => <QuestionScreen {...question} />)}
{questions.filter((question) => question.id === '07').map((question) => <QuestionScreen {...question} />)}
{/* Screen 04b: the configure hub. Every remaining question already
holds an answer (defaults are pre-filled the moment a surface tile
is checked), so the run can end here. The cards are skeletons;
summaries and edited marks are written by renderHub() in
scripts/palette-picker.js on every arrival. Placement is the
approved masonry: tall per-surface cards anchor the outer columns. */}
<section class="picker-screen" data-screen="04b" data-step="Configure" aria-hidden="true" aria-labelledby="picker-hub-title">
<div class="picker-container">
<div class="picker-hub">
<div class="picker-question-head">
<h2 id="picker-hub-title" class="picker-question-title">What else would you like to configure?</h2>
<p class="picker-question-lede">
Every choice below already holds a sensible default for your surfaces. Open any card to adjust it, or create your design context right away.
</p>
</div>
<div class="picker-hub-grid">
<div class="picker-hub-col">
<button class="picker-hub-card" type="button" data-hub-target="06" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Motion
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
<button class="picker-hub-card" type="button" data-hub-target="08" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Boundaries
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
</div>
<div class="picker-hub-col">
<button class="picker-hub-card" type="button" data-hub-target="11" data-hub-category="iconography">
<span class="picker-hub-eyebrow">Iconography</span>
<span class="picker-hub-title">Icon set
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
<button class="picker-hub-card" type="button" data-hub-target="09" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Corners
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
</div>
<div class="picker-hub-col">
<button class="picker-hub-card" type="button" data-hub-target="10" data-hub-category="material">
<span class="picker-hub-eyebrow">Material</span>
<span class="picker-hub-title">Depth
<svg class="picker-hub-arrow" viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square" aria-hidden="true"><path d="M0 4h14M10 0l4 4-4 4"></path></svg>
<span class="picker-hub-mark" data-hub-mark hidden>Edited</span>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
</div>
</div>
<div class="picker-actions-stack">
<button class="ks-button ks-button-primary" type="button" data-hub-finish data-hub-target="12">
Create my design context
<span class="ks-button-arrow" aria-hidden="true">
<svg viewBox="0 0 16 8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square">
<path d="M0 4h14M10 0l4 4-4 4"></path>
</svg>
</span>
</button>
<button class="picker-back" type="button" data-advance="prev">Back</button>
</div>
</div>
</div>
</section>
{questions.filter((question) => question.id !== '07').map((question) => <QuestionScreen {...question} />)}
<section class="picker-screen" data-screen="11" data-step="Icons" aria-hidden="true" aria-labelledby="picker-icons-title">
<div class="picker-container">
@@ -1278,17 +1264,6 @@ const questions = [
templates start empty and are filled from the interview by
scripts/design-context.js. */}
<section class="dcx-shell" data-dcx-shell hidden aria-label="Design context categories">
<header class="dcx-masthead">
<span class="dcx-masthead-brand">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 2.5 L13.5 2.5 L5.5 21.5 L5 21.5 Q2.5 21.5 2.5 19 L2.5 5 Q2.5 2.5 5 2.5 Z"></path>
<path d="M16.5 2.5 L19 2.5 Q21.5 2.5 21.5 5 L21.5 19 Q21.5 21.5 19 21.5 L8.5 21.5 Z"></path>
</svg>
<span>Impeccable</span>
</span>
<span class="dcx-masthead-title" data-dcx-product>Design context</span>
<span class="dcx-masthead-note">Assembled from your interview &middot; answers saved</span>
</header>
<div class="dcx-grid">
<div class="dcx-badge" data-reveal style="--reveal-delay: 0.45s" aria-hidden="true">
@@ -1642,13 +1617,15 @@ const questions = [
controls()[0]?.focus();
};
/* Screens 05 through 11 are reachable only from the configure hub, so
both their CTA and their Back return there rather than to the DOM
neighbor. The hub itself and the review screen keep the ordinary
rule: Back on the hub is the font pair, Back on the review's error
box is the icons screen. */
/* Motion, boundaries, corners, depth, and icons are reachable only
from the configure hub, so both their CTA and their Back return
there rather than to the DOM neighbor. Type scale (05) and layout
(07) sit on the linear path ahead of the hub and follow DOM order.
The hub itself and the review screen keep the ordinary rule: Back
on the hub is the layout screen (or type scale when layout is
skipped), Back on the review's error box is the icons screen. */
const HUB_SCREEN = '04b';
const HUB_RETURNS = new Set(['05', '06', '07', '08', '09', '10', '11']);
const HUB_RETURNS = new Set(['06', '08', '09', '10', '11']);
const screenIndex = (id) => screens.findIndex((screen) => screen.dataset.screen === id);
form.onclick = (e) => {
-2
View File
@@ -548,8 +548,6 @@ function renderDocument() {
const template = document.getElementById(`dcx-detail-${id}`);
template.innerHTML = `<article class="dcx-article">${build(snapshot, name)}</article>`;
}
const masthead = $('[data-dcx-product]');
if (masthead && name) masthead.textContent = `Design context — ${name}`;
if (name) document.title = `Design context — ${name}`;
}
+13 -12
View File
@@ -2224,7 +2224,7 @@ const paintStage = () => {
/* ============================================================
Screen 04b: the configure hub.
Seven questions remain after the font pair, and every one of
Five questions remain after the layout screen, and every one of
them already holds an answer: sync() pre-fills each per-surface
field with its surface's default the moment the tile is checked,
and the flat radios ship with a default checked. The hub reads
@@ -2237,21 +2237,19 @@ const paintStage = () => {
const hubScreen = document.querySelector('.picker-screen[data-screen="04b"]');
/* Card target screen mapped to the radio group that screen answers. */
const HUB_GROUPS = {
'05': 'type-scale',
'06': 'motion-energy',
'07': 'layout-structure',
'08': 'boundary-style',
'09': 'corner-style',
'10': 'depth-style',
'11': 'icon-pack',
};
/* Type scale and icon set render no per-surface fields, so they have
no dataset.chosen. A change event on their group is the one signal
/* The icon set renders no per-surface fields, so it has no
dataset.chosen. A change event on its group is the one signal
that a person picked rather than the markup default: programmatic
checks never fire it. */
const hubEdited = new Set();
document.addEventListener('change', ({ target }) => {
if (target?.name === 'type-scale' || target?.name === 'icon-pack') hubEdited.add(target.name);
if (target?.name === 'icon-pack') hubEdited.add(target.name);
});
const hubSurfaceLabel = (mode) => modeInputs.find((input) => input.value === mode)?.dataset.surfaceLabel ?? mode;
@@ -2305,12 +2303,15 @@ function hubRow(surfaceLabel, valueTitle, group, value) {
dot.setAttribute('aria-hidden', 'true');
val.append(dot, valueTitle);
pill.append(label, val);
const note = document.createElement('span');
note.className = 'picker-hub-note';
const why = value ? hubOptionDesc(group, value) : '';
note.textContent = why;
note.hidden = !why;
entry.append(pill, note);
entry.append(pill);
if (group !== 'icon-pack') {
const note = document.createElement('span');
note.className = 'picker-hub-note';
const why = value ? hubOptionDesc(group, value) : '';
note.textContent = why;
note.hidden = !why;
entry.append(note);
}
return entry;
}
+9 -60
View File
@@ -35,65 +35,13 @@ body.is-locked { overflow: hidden; }
but the document has its own stacked layout below 920px. */
body.dcx-open .picker-shell { display: none; }
body.dcx-open .picker-width-gate { display: none; }
body.dcx-open { background: linear-gradient(180deg, var(--ks-lacquer), var(--ks-lacquer-deep)); }
body.dcx-open { overflow: hidden; background: linear-gradient(180deg, var(--ks-lacquer), var(--ks-lacquer-deep)); }
.dcx-shell {
min-height: 100svh;
background: linear-gradient(180deg, var(--ks-lacquer), var(--ks-lacquer-deep));
}
/* Masthead — the slot the prototype gave its site header, reduced to what
the picker owes: the mark, what this page is, and that the answers are
already on disk. */
.dcx-masthead {
display: flex;
align-items: center;
gap: 18px;
padding: 18px clamp(14px, 2vw, 26px) 6px;
}
.dcx-masthead-brand {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--ks-kinpaku);
}
.dcx-masthead-brand svg { width: 22px; height: 22px; fill: currentColor; flex-shrink: 0; }
.dcx-masthead-brand span {
font-family: var(--ks-font-display);
font-weight: 400;
font-size: 1.02rem;
letter-spacing: 0.15em;
text-transform: uppercase;
line-height: 1;
}
.dcx-masthead-title {
flex: 1;
min-width: 0;
height: 100svh;
max-height: 100svh;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--ks-mono);
font-size: 0.7rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ks-text-muted);
}
.dcx-masthead-note {
font-family: var(--ks-mono);
font-size: 0.7rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--ks-text-faint);
white-space: nowrap;
}
@media (max-width: 920px) {
.dcx-masthead-note { display: none; }
background: linear-gradient(180deg, var(--ks-lacquer), var(--ks-lacquer-deep));
}
.dcx-expander code {
@@ -111,15 +59,16 @@ body.dcx-open { background: linear-gradient(180deg, var(--ks-lacquer), var(--ks-
============================================================ */
.dcx-shell {
--dcx-gap: clamp(10px, 0.9vw, 14px);
box-sizing: border-box;
display: flex;
flex-direction: column;
padding: var(--dcx-gap) clamp(14px, 2vw, 26px) clamp(16px, 2.2vw, 28px);
}
.dcx-grid {
position: relative;
/* The prototype budgeted 130px for the site header; the picker's masthead
is slimmer, so the grid keeps more of the viewport. */
height: calc(100svh - 96px);
min-height: 560px;
flex: 1 1 0;
min-height: 0;
display: grid;
grid-template-columns: repeat(18, minmax(0, 1fr));
grid-template-rows: repeat(12, minmax(0, 1fr));
+23 -1
View File
@@ -838,6 +838,28 @@ body.picker-page {
background: var(--ks-lacquer-raised);
}
/* Tips on grips and tools sit outside their band's box. Grid paint order
otherwise buries them under the next swatch; the deck's stacked cards
can overlap the panel edge the same way. */
.picker-band-item:has(.picker-band:is(:hover, :focus-within)),
.picker-band-item:has(.picker-band[data-dragging]) {
position: relative;
z-index: 3;
}
.picker-band-item:has(.picker-band[data-dragging]) {
z-index: 6;
}
.picker-screen[data-screen="02"] .picker-palette-subgrid {
position: relative;
z-index: 2;
}
.picker-reset:is(:hover, :focus-visible) {
z-index: 5;
}
.picker-band {
--band-color: var(--ks-graphite);
--band-ink: var(--ks-champagne);
@@ -7556,7 +7578,7 @@ body.picker-page {
/* ============================================================
Screen 04b: the configure hub. Three packed columns of cards;
the tall per-surface cards (motion, layout) anchor the outer
the tall per-surface cards (motion, depth) anchor the outer
tracks. Sharp corners and hairline rules, the same vocabulary
as the option rows. Summaries are written by renderHub().
============================================================ */