Refine configure hub cards with pills, notes, and layout polish.

Add the intro lede, per-row patina answer marks with DOM-sourced notes, mode-tile hover, and tighter card typography including inline pills and title arrows.

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 80211f3364
commit b629daef01
3 changed files with 143 additions and 42 deletions
+13 -8
View File
@@ -916,23 +916,28 @@ const questions = [
<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">
<h2 id="picker-hub-title" class="picker-question-title">What else would you like to configure?</h2>
<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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
@@ -942,24 +947,24 @@ const questions = [
<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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
@@ -969,16 +974,16 @@ const questions = [
<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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</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>
<svg class="picker-hub-chevron" viewBox="0 0 20 20" aria-hidden="true"><path d="m7.5 4.5 5 5.5-5 5.5"></path></svg>
</span>
<span class="picker-hub-summary" data-hub-summary></span>
</button>
+55 -11
View File
@@ -2279,18 +2279,52 @@ function hubSurfaceRows(group) {
}));
}
function hubLine(text, surface) {
/* A summary row is a surface pill on the left and the value docked
right behind a patina dot, the same signal the surface tabs and the
option rows spend on a committed choice. The plain line survives
only for the skipped-card message. */
function hubPlainLine(text) {
const line = document.createElement('span');
line.className = 'picker-hub-line';
if (surface) {
const name = document.createElement('b');
name.textContent = surface;
line.append(name);
}
line.append(text);
return line;
}
function hubRow(surfaceLabel, valueTitle, group, value) {
const entry = document.createElement('span');
entry.className = 'picker-hub-entry';
const pill = document.createElement('span');
pill.className = 'picker-hub-pill';
const label = document.createElement('span');
label.className = 'picker-hub-pill-label';
label.textContent = surfaceLabel;
const val = document.createElement('span');
val.className = 'picker-hub-value';
const dot = document.createElement('i');
dot.className = 'picker-hub-dot';
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);
return entry;
}
/* The one-line reason the current answer is a sound default, read off
the option row's own description so the hub never restates copy.
dataset.copy is read first because applyAllows swaps textContent for
a blocked reason on surfaces that rule the option out. */
function hubOptionDesc(group, value) {
const input = document.querySelector(`input[name="${group}"][value="${value}"]`);
const desc = input?.closest('.picker-strategy-option')?.querySelector('.picker-strategy-desc');
if (!desc) return '';
return (desc.dataset.copy ?? desc.textContent).trim();
}
function renderHub() {
if (!hubScreen) return;
for (const cardNode of hubScreen.querySelectorAll('.picker-hub-card')) {
@@ -2304,7 +2338,7 @@ function renderHub() {
cardNode.disabled = skipped;
cardNode.setAttribute('aria-disabled', skipped ? 'true' : 'false');
if (skipped) {
summary.replaceChildren(hubLine('Not asked of these surfaces'));
summary.replaceChildren(hubPlainLine('Not asked of these surfaces'));
cardNode.classList.remove('is-edited');
mark.hidden = true;
continue;
@@ -2315,15 +2349,25 @@ function renderHub() {
if (rows.length === 0) {
const checked = document.querySelector(`input[name="${group}"]:checked`);
edited = hubEdited.has(group);
lines = [hubLine(checked ? hubOptionTitle(group, checked.value) : '')];
lines = [hubRow(
'All surfaces',
checked ? hubOptionTitle(group, checked.value) : '',
group,
checked?.value,
)];
} else {
edited = rows.some((row) => row.chosen);
if (rows.length > 1 && rows.every((row) => row.value === rows[0].value)) {
lines = [hubLine(`${hubOptionTitle(group, rows[0].value)} · all`)];
lines = [hubRow('All surfaces', hubOptionTitle(group, rows[0].value), group, rows[0].value)];
} else if (rows.length === 1) {
lines = [hubLine(hubOptionTitle(group, rows[0].value))];
lines = [hubRow(hubSurfaceLabel(rows[0].mode), hubOptionTitle(group, rows[0].value), group, rows[0].value)];
} else {
lines = rows.map((row) => hubLine(hubOptionTitle(group, row.value), hubSurfaceLabel(row.mode)));
lines = rows.map((row) => hubRow(
hubSurfaceLabel(row.mode),
hubOptionTitle(group, row.value),
group,
row.value,
));
}
}
summary.replaceChildren(...lines);
+75 -23
View File
@@ -329,6 +329,7 @@ body.picker-page {
.picker-shell:has(#picker-form[data-current="02"]) .picker-hero-art,
.picker-shell:has(#picker-form[data-current="03"]) .picker-hero-art,
.picker-shell:has(#picker-form[data-current="04"]) .picker-hero-art,
.picker-shell:has(#picker-form[data-current="04b"]) .picker-hero-art,
.picker-shell:has(#picker-form[data-current="05"]) .picker-hero-art,
.picker-shell:has(#picker-form[data-current="06"]) .picker-hero-art,
.picker-shell:has(#picker-form[data-current="07"]) .picker-hero-art,
@@ -4109,7 +4110,7 @@ body.picker-page {
gap: clamp(12px, 2svh, 18px);
}
/* Screen 05 is the only question that carries a lede, and one sentence at
/* Screen 05 and the configure hub carry a lede, and one sentence at
caption size under a display headline reads as a footnote. Set above the
body size instead, with the measure pulled in so the sentence breaks into
two balanced lines rather than one long ruler. */
@@ -7566,6 +7567,11 @@ body.picker-page {
padding-block: clamp(32px, 5svh, 48px) clamp(24px, 3.5svh, 48px);
}
.picker-hub .picker-question-lede {
max-width: 800px;
font-weight: 300;
}
.picker-hub-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -7581,10 +7587,11 @@ body.picker-page {
}
.picker-hub-card {
position: relative;
display: grid;
gap: 10px;
gap: 16px;
width: 100%;
padding: 20px 24px;
padding: 24px 28px;
text-align: left;
background: var(--ks-lacquer-raised);
border: 1px solid var(--ks-rule);
@@ -7596,16 +7603,19 @@ body.picker-page {
}
.picker-hub-card:hover {
background: var(--ks-graphite);
border-color: var(--ks-kinpaku-deep);
border-color: color-mix(in oklab, var(--ks-patina) 45%, var(--ks-rule));
}
.picker-hub-card:focus-visible {
outline: 1px solid var(--ks-kinpaku);
outline-offset: 3px;
outline: 2px solid var(--ks-patina-deep);
outline-offset: -2px;
z-index: 1;
}
.picker-hub-eyebrow {
position: absolute;
top: 24px;
right: 28px;
color: var(--ks-text-faint);
font-size: var(--ks-type-eyebrow-size);
font-weight: 500;
@@ -7616,31 +7626,29 @@ body.picker-page {
.picker-hub-title {
display: flex;
align-items: center;
gap: 10px;
gap: 8px;
padding-right: 72px;
color: var(--ks-champagne);
font-size: var(--ks-type-title-size);
font-weight: var(--ks-type-title-weight);
line-height: var(--ks-type-title-line);
}
.picker-hub-chevron {
width: 16px;
height: 16px;
margin-left: auto;
.picker-hub-arrow {
width: 14px;
height: 8px;
flex: 0 0 auto;
fill: none;
stroke: currentColor;
stroke-width: 1.5;
color: var(--ks-text-faint);
transition: color 180ms var(--ks-ease), transform 180ms var(--ks-ease);
}
.picker-hub-card:hover .picker-hub-chevron {
color: var(--ks-kinpaku);
.picker-hub-card:hover .picker-hub-arrow {
color: var(--ks-patina);
transform: translateX(2px);
}
.picker-hub-mark {
margin-left: auto;
padding: 2px 8px;
border: 1px solid var(--ks-kinpaku-deep);
color: var(--ks-kinpaku);
@@ -7652,20 +7660,64 @@ body.picker-page {
.picker-hub-summary {
display: grid;
gap: 6px;
gap: 14px;
color: var(--ks-text-muted);
font-size: 0.86rem;
line-height: 1.5;
}
.picker-hub-line {
.picker-hub-entry {
display: grid;
gap: 2px;
gap: 8px;
}
.picker-hub-line b {
.picker-hub-line {
display: block;
}
/* Surface pill wraps the label and the committed value; patina dot
plus the text mix mirror the surface tabs' answered state. */
.picker-hub-pill {
display: inline-flex;
align-items: center;
justify-content: space-between;
gap: 12px;
max-width: 100%;
padding: 4px 9px;
color: var(--ks-text-faint);
font-weight: 500;
font-size: 12.5px;
letter-spacing: 0.015em;
border: 1px solid var(--ks-rule);
border-radius: 2px;
}
.picker-hub-pill-label {
flex: 0 0 auto;
}
.picker-hub-value {
display: inline-flex;
align-items: center;
gap: 8px;
color: color-mix(in oklab, var(--ks-patina) 85%, var(--ks-champagne));
text-align: right;
}
.picker-hub-dot {
flex: 0 0 auto;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ks-patina);
}
/* Why this answer is a sound default, read off the option row's own
description by renderHub(). One note per answer row. */
.picker-hub-note {
padding-inline: 2px;
color: var(--ks-text-faint);
font-size: 0.84rem;
line-height: 1.5;
}
/* A question no chosen surface takes: present so the menu keeps its
@@ -7681,7 +7733,7 @@ body.picker-page {
.picker-hub-card.is-skipped .picker-hub-title,
.picker-hub-card.is-skipped .picker-hub-eyebrow,
.picker-hub-card.is-skipped .picker-hub-summary,
.picker-hub-card.is-skipped .picker-hub-chevron {
.picker-hub-card.is-skipped .picker-hub-arrow {
color: var(--ks-text-mute-deep);
}