UX second pass: icon-only header, pane-toggle cue, schema columns, facet cleanup

This commit is contained in:
2026-06-13 10:13:40 +03:00
parent e0806bf3db
commit 92bfe888b5
14 changed files with 124 additions and 154 deletions
+5 -2
View File
@@ -210,8 +210,11 @@ UI by swapping one set of values. Borrowed from Carbon's layering model:
action is filled. Worked examples: the **editor toolbar** (Publish is the lone
primary; Extract/Revert are secondary, and collapse to icons when narrow — see
[arch 10 §8](10-interaction-and-feedback.md)), and the **header** (Datasets /
Import / Export / About are ghost; a divider then sets off the soft-accent Donate
and the ghost theme toggle).
Import / Export / About are **icon-only** IconButtons per Carbon's UI-shell
header — global actions as a right-aligned icon row — with accessible names
that carry scope, e.g. "Export workspace" vs the preview's per-chart "Export";
a divider then sets off the soft-accent Donate, which keeps its text label —
a solicitation needs the word — and the ghost theme toggle).
- **Hover is variant-specific:** filled buttons (primary/danger) **darken**
(`--accent-hover` / a slight brightness drop); outlined/ghost buttons **gain a
fill one elevation step above their surface** — on `--bg` → `--layer-01`, on a
@@ -231,6 +231,13 @@ last-focused control on re-entry.
- The **Datasets** control is a plain **command button** (no `aria-pressed`) in the _same_
toolbar — APG permits mixed control types — set off from the toggles by a visual divider (and
optionally a nested `role="group"`), but kept in the roving sequence as its last element.
- The **pressed (pane-visible) state has its own visual cue**: an inset accent edge bar plus the
full-strength glyph on the filled chip (the activity-bar convention). A fill alone cannot be
the state cue — hover uses the same one-step fill, so a fill-only pressed state is
indistinguishable from hovering an off toggle (the collision class arch 09 names for hover);
and a bar, not a border, keeps the strip boxless per the arch 09 box-discipline rule. The
unlabeled glyph rail itself is the established activity-bar convention (NN/g #4) — toggles
carry tooltips per Carbon's icon-button rule; no visible labels.
- **Focus**: show/hide is only ever initiated **from the strip**, so the activating toggle
already holds focus when its pane disappears and **retains it** (the button stays, flips to
not-pressed) — no orphaned focus, no restoration logic. The strip is **never itself hidden**,
@@ -252,7 +259,10 @@ the Draft/Published view) is a **radio group**, never a row of `aria-pressed` to
**toggle switch** was also weighed and rejected: APG defines `role="switch"` as on/off of a
**single** setting, but Draft/Published selects between two **named peer views** with no
natural "on" side — a radio group is the right semantics. Reserve the switch for genuine
on/off settings. Consulted via /council → APG switch / radio-group / tabs.)_
on/off settings. Consulted via /council → APG switch / radio-group / tabs.)_ A per-option
`title` (tooltip for a terse label) doubles as the option's accessible name, so it must
**lead with the visible label** ("Original — the natural size from the spec"), or
speech-input users can't address the control they see (WCAG 2.5.3 label-in-name).
**Resolved — selectable lists.** A row the user selects must be a real `<button>` (or a
proper option), not a click handler on `<li>` (mouse-only, no keyboard, no role). It is
@@ -453,6 +463,14 @@ in the keyboard order, never a heading. **The boundary is set where the list is
module that decides the option order marks the divider-carrying option (e.g.
`chartThemeOptions` stamps the first preset); a consumer must never recompute a group
boundary by index arithmetic, which silently misplaces when the producer's ordering changes.
A value list may carry an **action row** (the VS Code theme-picker pattern — e.g. "Edit
themes…" inside the chart-theme picker): permissible because options are real buttons, not
listbox options (APG's no-interactive-children listbox constraint doesn't apply); the row's
label ends in "…" (the opens-further-UI convention) and sets the option's `hasPopup` so AT
hears `aria-haspopup` — no special visual styling beyond an adjacent group divider. The
default trigger caps its value label at **16ch with ellipsis**, so a long value (a preset or
user-named theme) can't blow out a crowded pane header; the full label remains in the open
list and the trigger's accessible name.
The single-open registry means **disclosures cannot nest**: a SelectControl inside a
settings popover would close — and unmount — its own parent on open. A control that needs
its own popover sits beside the gear in the pane header, never inside the panel.
@@ -494,6 +512,15 @@ Datasets (⌘/Ctrl+K), or check the dataset name in your spec."_ — instead of
must match the actual cause (NN/g #9, GOV.UK error-message). The thrown
`DatasetNotFoundError` carries `datasetName` so the surface can name it.
**Resolved — no affordance for unbuilt features.** A visible placeholder promising future
functionality (the Chart Builder's dashed "+ row facet · LATER" shelf slots, removed
2026-06-13) is roadmap language shipped to users: it speaks our planning vocabulary, not
theirs (NN/g #2), and competes with the working controls around it (NN/g #8). A gated or
later-phase feature gets **no placeholder, tag, or disabled stub** until it ships — disabled
states are for _temporarily unavailable_ actions, not unbuilt ones. Design the layout so the
future control can land without rework (e.g. shelf slots are row-shaped), and keep the
roadmap in `docs/`, not in the UI.
**Resolved — service-worker update prompt & persistent storage (web.dev seat).** The build
uses `registerType: 'prompt'`, so a new service worker waits and never takes over a running
session on its own — the app **must** tell the user, or "ask before updating" silently means
+9 -8
View File
@@ -25,18 +25,19 @@ Behavior:
A fixed header spans the top of the app.
- **Left side**: the app icon, the app title ("Astrolabe"), and a version badge showing the current app version.
- **Right side**: a row of text entry points. Each opens a destination:
- **Right side**: a row of **icon-only** utility entry points (each with a tooltip and an accessible name), then — set off by a divider — the text-labelled Donate button and the theme toggle. Each opens a destination:
| Entry point | Opens |
| ----------- | -------------------------------------------------------------------------------------------------------------------- |
| Import | A file-picker dialog to choose a previously exported file; the chosen file is imported (see _Import & Export_). |
| Export | Immediately produces a downloaded file containing all snippets and datasets (see _Import & Export_). |
| Datasets | The Datasets manager modal (see _Datasets_). |
| About | The About & Help modal (keyboard shortcuts, about, and privacy information). Labelled "About"; privacy lives inside. |
| Donate | The Donate modal. |
| Entry point | Opens |
| ---------------- | --------------------------------------------------------------------------------------------------------------- |
| Datasets | The Datasets manager modal (see _Datasets_). |
| Import workspace | A file-picker dialog to choose a previously exported file; the chosen file is imported (see _Import & Export_). |
| Export workspace | Immediately produces a downloaded file containing all snippets and datasets (see _Import & Export_). |
| About | The About & Help modal (keyboard shortcuts, about, and privacy information); privacy lives inside. |
| Donate | The Donate modal. The one text-labelled, soft-accent button — the solicitation keeps its word. |
Notes:
- The utilities are icon-only so the header reads as quiet chrome (Carbon UI-shell header: global actions are a right-aligned icon row). The accessible names scope the workspace-level Import/Export ("Export workspace") apart from the preview pane's per-chart "Export" — the two were previously both labelled "Export" at once.
- Import and Export act directly (file dialog / file download); they do not open in-app modals.
- The Datasets, About, and Donate entry points each open a modal (see _Modal System_).
- **Settings are not a header entry point.** A design review (see _Settings_) distributed
+8 -59
View File
@@ -8,65 +8,9 @@ record the resolution into the contract (`docs/architecture/09`+`10` and the rel
## Open
- **Two visible "Export" buttons** (`App.tsx` header — workspace export;
`ChartExport.tsx` in the preview header — per-chart export). Same label,
different scope, both visible at once. Candidate fix: rename one ("Export
chart" / "Export workspace") or differentiate via grouping.
- **"LATER" facet placeholders in Chart Builder shelves**
(`ChartBuilderModal.module.css``.facetSlot`/`.facetTag`). Internal roadmap
language shipped to users. Either hide the affordance until faceting works or
write user-facing copy.
- **Default snippet names are timestamps** ("Snippet 2026-06-10 18:04:37") — the
library reads like a downloads folder next to builder-generated names ("Bar
chart of count by Ship Mode"). Candidate: derive a content-based name from the
spec (mark + encoded fields) when the user never renames.
- **Fit-mode tab labels** ("Original / Width / Height / Full", `LivePreview.tsx`)
are terse for a first-time user; tooltips likely suffice.
- **Donate is the most saturated element on screen** (header soft-accent wash) —
visually outranks Publish/Build Chart. Decide whether intentional.
- **Left rail affordance** (`PaneToggleStrip.tsx`): three unlabeled glyphs + a
tall empty strip; pane toggles aren't discoverable without hovering.
- **Pane toggle pressed state = hover state** (`PaneToggleStrip.module.css`).
The pressed chip is a `--layer-02` fill — the same fill the strip's IconButtons
use on hover — so hovering an off toggle looks identical to an on toggle at
rest. (The pressed chip's `--border-strong` border used to disambiguate; it was
dropped in the box-discipline pass.) Candidate cues: a filled/accent glyph,
an inset edge marker, or restoring a border for the on state only.
- **Dataset detail schema density** (`DatasetsModal.tsx`): one field per row
makes a 25-column dataset a long scroll before the preview table; a denser
two-column field list would fit overview + schema + preview on one screen.
- **Chart theme picker placement & header crowding** (`LivePreview.tsx` — ChartThemeControl).
The picker sits in the preview header because nesting a SelectControl inside the
PreviewSettings popover is impossible today (one-open-popover registry: the select would
close/unmount its own parent). Header now holds Fit + theme + export + gear; at narrow
pane widths the long trigger labels ("FiveThirtyEight", "Urban Institute") may crowd it.
Council questions: does the picker deserve header prominence (the "transform your chart"
showcase) or settings-cluster placement (a persistent global pref); should the popover
registry learn nesting; should the preset block also carry a group _heading_ (a
divider now bounds it — 2026-06-12, user-decided; `SelectControl` `dividerBefore`)?
- **"Edit themes…" action row inside the value picker** (`LivePreview.tsx`
ChartThemeControl). A non-value action lives inside a single-select disclosure (the
VS Code theme-picker pattern), placed after the custom-themes block and before the
preset roster (first-use feedback: at the very bottom it was invisible without
scrolling). The ours-vs-presets divider (2026-06-12) now bounds it below; remaining
council questions: should the action row itself be styled distinctly from the value
rows; is a mid-list row that opens a modal instead of selecting surprising to AT
users?
- **Theme Builder config editor is a plain textarea** (`ThemeBuilderModal.tsx`). Monaco
(with the Vega-Lite config schema for completions) would match the main editor but is
heavy inside a modal and untested in that mounting. Revisit whether the builder deserves
a Monaco instance, and whether the gallery's canvas charts need text alternatives
beyond the per-card captions.
_(empty — the 2026-06-13 batch resolved all eleven open items; resolutions live in
spec §01B/§02/§03D, arch 09 §5 (header worked example), and arch 10 §5 (toggle-strip
pressed cue, SelectControl action row + trigger cap, no-affordance-for-unbuilt-features).)_
## Deferred (not design debts, revisit on demand)
@@ -74,3 +18,8 @@ record the resolution into the contract (`docs/architecture/09`+`10` and the rel
be a progressive enhancement on top of the chooser model, not a replacement. Revisit if
real usage asks for it (likely alongside Phase 4 faceting, where drag-to-shelf reads most
naturally).
- **Theme Builder config editor stays a plain textarea** (decided 2026-06-13) — a second
Monaco mount is heavy inside a modal for an occasional surface; the parse error is the
feedback channel that matters. Revisit only if real usage asks for config completions.
The gallery's per-card captions stand as the canvas charts' text alternative.