Files
pbakaus_impeccable/picker/components/QuestionScreen.astro
T
Abdul WahabandCursor a34c9828aa Add the layout screen's portfolio board and default color to full palette
The index board grows the parts only a portfolio has: a project rail, a
support row, and a pagination rail restated at the foot of the handset
stack. They mount behind a prop rather than for every board, because the
motion screen reads the same index body through :nth-of-type and a stray
sibling would shift its scenes. Their sheet loads after the boundary,
corner, and depth sheets so it wins ties on source order.

The color strategy question now opens on Full palette.

Prepared with AI assistance (Cursor).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 10:02:40 +05:00

136 lines
5.1 KiB
Plaintext

---
/*
One question, one artboard: the shape screens 03 onward all share. A screen
that only remaps artboard slots needs nothing of its own here, so it arrives
as data and its design lives in its own stylesheet.
*/
import Artboard from './Artboard.astro';
import { SURFACE_ANSWERS, surfacesAsked, surfaceTabsAttrs } from '../data/surfaces.js';
interface Option {
value: string;
title: string;
desc: string;
/* The sentence that stands in for the description on a surface this option
does not suit. Present only on options some surface rules out. */
blocked?: string;
}
/* A screen whose answer reads differently on each surface draws a board per
surface instead of repainting one, the arrangement the font screen already
uses. `class` is the modifier that board's own scene keys on. */
interface Board {
surface: 'persuade' | 'operate' | 'read' | 'experience';
class?: string;
}
interface Props {
id: string;
step: string;
slug: string;
name: string;
title: string;
legend: string;
cta: string;
options: Option[];
previewClass: string;
phone?: boolean;
boards?: Board[];
}
const { id, step, slug, name, title, legend, cta, options, previewClass, phone = true, boards } = Astro.props;
const titleId = `picker-${slug}-title`;
const artboardClass = phone ? previewClass : `${previewClass} picker-artboard--solo`;
// A question listed in the matrix is answered once per chosen surface, which
// buys it a tab strip on the frame and a field per surface to answer into.
// Everything else about the screen is unchanged by opting in.
const perSurface = SURFACE_ANSWERS[name];
// Only the surfaces the question is put to. A surface with no field here is a
// surface that was never asked, and that absence is the whole of how the script
// and the final document know it.
const asked = perSurface ? surfacesAsked(name) : [];
const tabsAttrs = perSurface ? surfaceTabsAttrs(name) : null;
---
<section class="picker-screen" data-screen={id} data-step={step} aria-hidden="true" aria-labelledby={titleId}>
<div class="picker-container">
<div class="picker-strategy" data-question={slug}>
<h2 id={titleId} class="picker-question-title">{title}</h2>
<div class="picker-strategy-grid">
<div class="picker-type-rail">
<fieldset
class="picker-strategy-options picker-strategy-choices picker-type-options"
style={`--pk-choices:${options.length}`}
>
<legend>{legend}</legend>
{options.map((option, index) => (
<label class="picker-strategy-option" data-blocked-reason={option.blocked}>
<input type="radio" name={name} value={option.value} checked={index === 0} />
<span class="picker-strategy-copy">
<span class="picker-strategy-title">{option.title}</span>
<span class="picker-strategy-desc">{option.desc}</span>
</span>
</label>
))}
</fieldset>
{/* One field per surface, disabled until its tile is chosen, so a
surface nobody asked for stays out of the answers and a surface
nobody opened still leaves one. A flat question keeps the same
fields for the same reading and withholds the name, which is the
whole of what a form submits. */}
{asked.map((mode) => (
<input
type="hidden"
data-surface-field={`${name}-${mode}`}
name={perSurface.flat ? undefined : `${name}-${mode}`}
disabled
/>
))}
</div>
{boards || tabsAttrs ? (
/* All of them mounted and one shown, so a tab switch costs a hidden
attribute rather than a rebuild. The stage is the box the strip takes
its row on, above the frame, which is why a screen with one board and
a strip is wrapped in it too. */
<div class="picker-board-stage">
{tabsAttrs && <div class="picker-surface-tabs" {...tabsAttrs} hidden></div>}
{(boards ?? [{} as Board]).map((board) => (
<Artboard
class={[artboardClass, board.class].filter(Boolean).join(' ')}
surface={board.surface}
phone={phone}
cursor={slug === 'motion'}
carry={slug !== 'motion'}
portfolioExtras={slug === 'layout'}
/>
))}
</div>
) : (
<Artboard
class={artboardClass}
phone={phone}
cursor={slug === 'motion'}
carry={slug !== 'motion'}
/>
)}
</div>
<div class="picker-actions-stack">
<button class="ks-button ks-button-primary" type="button" data-advance="next">
{cta}
<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>