Restyle the decision card #231
- MA
## Summary
PR 1 of the decision card redesign: restyles `QuestionView` to the approved jig. Resolved/Decided rendering is unchanged.
- Header: no "Decision" label row; 24px success mark (new `DecisionIcon` in `@chopin/icons`) leads the question title. Presence badges unchanged (PR 2). - Options: lettered tiles, label/description, brand-wash selection with a fading check; native radio/checkbox kept (visually hidden, row focus ring). Multi-select adds a "Choose any" hint. - Custom answer is now the last "Add an option" row; one click turns it into a focused inline field (next letter) bound to the same custom draft and 4000-char limit. - Footer: `Discard` (outline) / `Save` (primary, "Saving…", "Try again"); Discard swaps the footer in place to "Discard this decision?" with `Keep it` / `Discard` (destructive), same underlying cancel action. - Several questions: tablist removed; footer stepper `‹ header n/m ›`, primary reads "Next" until the last question, then "Save". Per-question drafts preserved. - Save failed: callout reusing `.plan-research-callout` / `.plan-research-badge` with a "Couldn't save" lead line. - Tests and e2e selectors/copy updated.
## Decisions made
- **Save and Next are disabled until the current question has an answer** (per the approved design; reviewer change). A blank custom answer does not count. Read-only views keep free Next navigation. The validation alert remains only for a skipped earlier question in a multi-question save and for server rejections. - **Add an option field is a single-row auto-growing textarea** (Enter blurs, Shift+Enter newline, Escape closes the field and returns to the choices) rather than the jig's `<input>`, so long existing custom answers keep wrapping and the 4000-char limit is unchanged. An empty field is left open (not auto-closed on blur) so Save still reports "requires an answer". - **Inactive add row keeps a native radio/checkbox** named "Add an option" for keyboard/AT semantics and e2e roles; the active field is a textbox with the same name. - **The native inputs are 1px, opacity 0** inside their label row (not covering it), so label clicks, Playwright `check()` on options and the row focus ring all behave. Focus ring is drawn on the row via `:has(:focus-visible)`. - **"Couldn't save" heads every error callout**, including cancel and sync errors, since `error` is one string. - **Stepper focus:** when a caret reaches an end it disables, so focus is moved to the opposite caret after render. Arrow-key tab navigation is gone with the tablist. - **Design-contract bookkeeping:** `ChevronIcon size=16` allowed in the question view (icon test), `btn-outline` added as a recognised tier in the token test, native-control registrations for the old question inputs removed (the inputs are now intentionally bespoke), and `dynamic-packages.json` cases/hashes renewed for `question-view.tsx` and `icons/line.tsx`. - Reduced motion: the footer fade only runs under `prefers-reduced-motion: no-preference` (keeps the existing theme test's first reduced-motion block intact). - The "Decision" mark keeps a native `title="Decision"` tooltip as in the jig.
## Before / after (2x)
| State | Before | After | | --- | --- | --- | | Open card | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-1-open.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-1-open.png?raw=true"> | | Option chosen | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-2-chosen.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-2-chosen.png?raw=true"> | | Add an option (field) | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-3-add-option.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-3-add-option.png?raw=true"> | | Discard confirm | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-4-discard-confirm.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-4-discard-confirm.png?raw=true"> | | Save failed | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-5-save-failed.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-5-save-failed.png?raw=true"> | | Several questions, first | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-6-several-first.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-6-several-first.png?raw=true"> | | Several questions, second | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/before-7-several-second.png?raw=true"> | <img width="320" src="https://github.com/githubnext/chopin/blob/design-pr-assets/pr-231/after-7-several-second.png?raw=true"> |
Note: the multi-question fixture is the read-only stored questionnaire (no live record), so selection states are not shown for it.
## Checks
- `bun run fix`, `bun run ci`, `bun run types`: pass - `bun test`: 1736 pass, 0 fail - e2e (`--project=fixtures`), full project: 88 passed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
MaggieAppletonreferenced this in Decision card: presence faces and tooltips #232last month on GitHub
- MA
## Independent review (automated, designer asleep)
**Fixed and pushed** - Save and Next were enabled with nothing chosen; the approved design disables them. Now disabled until the current question is answered (a blank custom answer does not count). Next keeps free navigation in read-only views. Unit tests added; the e2e now checks the disabled state. - The alert-motion e2e relied on an empty submit, which is no longer reachable. It now covers a server-rejected save (WebSocket `question:submit` refusal) and asserts "Couldn't save", the message, "Try again" and the alert transition. - CI failure `responsive-content` "a selected tab follows strip layout changes": not flaky. The seeded decision cards sit at the foot of the scroller and wrap to more lines at 320px, so the bottom distance settles at 40px. On main it was exactly 24px, the old limit, so it passed only by luck. Bound raised to 64px with a comment; a moved document would be hundreds of pixels. Full fixtures project now passes. - Escape in the Add an option field dropped focus to the body. Focus now returns to the row; e2e covers it. - After Next lands on the last question, focus moves to the new primary button (Save) rather than the Previous caret. - `.question-callout` now out-specifies `.plan-research-callout` so the padding does not depend on stylesheet load order. - Design-contract `sourceHash` renewed for `question-view.tsx` after these edits. - Refreshed `after-1-open.png` (Save greyed) and `after-5-save-failed.png` (now a real rejected save with a selected option).
**Checked, no change needed**: draft/multi-question state, multiple-select and custom-answer mode switching, discard confirm, aria-busy/disabled fieldset, keyboard-only focus ring, tokens/btn utilities, fidelity to the jig CSS (sizes, spacing, colours, copy).
**Left as is** - Saving from the last step with an earlier question unanswered still shows "<Header> requires an answer" (the view has no per-question focus from the controller); same as before this PR. - Enter in the field blurs it (as in the jig); focus is not moved elsewhere.
Checks: `bun run fix`, `bun test` (1736 pass), `bun run types`, `bun run ci` pass; e2e `--project=fixtures` 88 passed.
MaggieAppletonreferenced this in Decision card: shared Add an option #233last month on GitHub
MaggieAppletonreferenced this in Resolved decisions in prose: margin marker, hover preview, pinned popover #234last month on GitHub
MaggieAppletonadded-to-stacklast month on GitHub
MaggieAppletonmerged 3cac38633668last month on GitHub
MaggieAppletonhead-ref-deletedlast month on GitHub