diff --git a/components/public-form-runner.test.tsx b/components/public-form-runner.test.tsx index 5c9e3bc..afd2c38 100644 --- a/components/public-form-runner.test.tsx +++ b/components/public-form-runner.test.tsx @@ -126,8 +126,6 @@ function renderRunner(form: PublicForm) { submit: "Submit", progressRestored: "Progress restored", savedProgressDiscarded: "Saved progress discarded", - chooseOne: "Choose one option.", - selectAll: "Select all that apply.", defaultTextTitle: "Take a breath before the next step.", openCalendar: "Open calendar", }, diff --git a/components/public-form-runner.tsx b/components/public-form-runner.tsx index d35c82a..012f72d 100644 --- a/components/public-form-runner.tsx +++ b/components/public-form-runner.tsx @@ -823,18 +823,6 @@ export function PublicFormRunner({ form }: { form: PublicForm }) { ) : null}
- {currentBlock.type === "SINGLE_CHOICE" ? ( -

- {t("publicRunner.chooseOne")} -

- ) : null} - - {currentBlock.type === "MULTIPLE_CHOICE" ? ( -

- {t("publicRunner.selectAll")} -

- ) : null} - {currentBlock.type === "SHORT_TEXT" ? ( +
{(currentBlock.config as ChoiceBlockConfig).options.map( (option, index) => { const selected = @@ -976,7 +964,7 @@ export function PublicFormRunner({ form }: { form: PublicForm }) { aria-pressed={selected} aria-keyshortcuts={shortcut ?? undefined} className={cn( - "flex items-center justify-between gap-3 rounded-[16px] border px-4 py-3 text-left transition", + "flex w-full max-w-full items-center justify-between gap-3 rounded-[16px] border px-4 py-3 text-left transition", selected ? "border-[var(--accent)] bg-[var(--accent-soft)] text-[var(--ink)] shadow-[var(--shadow-accent)]" : "border-[color:var(--line)] bg-[var(--surface-strong)] text-[var(--ink)] hover:bg-[var(--surface)]", @@ -1008,7 +996,7 @@ export function PublicFormRunner({ form }: { form: PublicForm }) { ) : null} {currentBlock.type === "MULTIPLE_CHOICE" ? ( -
+
{(currentBlock.config as ChoiceBlockConfig).options.map( (option, index) => { const rawValues = answers[currentBlock.id]; @@ -1028,7 +1016,7 @@ export function PublicFormRunner({ form }: { form: PublicForm }) { aria-pressed={selected} aria-keyshortcuts={shortcut ?? undefined} className={cn( - "flex items-center justify-between gap-3 rounded-[16px] border px-4 py-3 text-left transition", + "flex w-full max-w-full items-center justify-between gap-3 rounded-[16px] border px-4 py-3 text-left transition", selected ? "border-[var(--accent)] bg-[var(--accent-soft)] text-[var(--ink)] shadow-[var(--shadow-accent)]" : "border-[color:var(--line)] bg-[var(--surface-strong)] text-[var(--ink)] hover:bg-[var(--surface)]", @@ -1065,7 +1053,7 @@ export function PublicFormRunner({ form }: { form: PublicForm }) { ) : null} {currentBlock.type === "AGREEMENT" ? ( -
+
{(() => { const selected = answers[currentBlock.id] === @@ -1084,7 +1072,7 @@ export function PublicFormRunner({ form }: { form: PublicForm }) { aria-pressed={selected} aria-keyshortcuts="1" className={cn( - "flex items-center justify-between gap-3 rounded-[16px] border px-4 py-3 text-left transition", + "flex w-full max-w-full items-center justify-between gap-3 rounded-[16px] border px-4 py-3 text-left transition", selected ? "border-[var(--accent)] bg-[var(--accent-soft)] text-[var(--ink)] shadow-[var(--shadow-accent)]" : "border-[color:var(--line)] bg-[var(--surface-strong)] text-[var(--ink)] hover:bg-[var(--surface)]", diff --git a/locales/en.yml b/locales/en.yml index 751d7e9..275489e 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -305,8 +305,6 @@ publicRunner: context: Context defaultTextTitle: Take a breath before the next step. required: Required - chooseOne: Choose one option. - selectAll: Select all that apply. answerRequired: Please answer before continuing. singleChoiceRequired: Choose one option before continuing. multiChoiceRequired: Choose at least one option before continuing. diff --git a/locales/ru.yml b/locales/ru.yml index bf9de44..76ae916 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -305,8 +305,6 @@ publicRunner: context: Контекст defaultTextTitle: Сделайте паузу перед следующим шагом. required: Обязательно - chooseOne: Выберите один вариант. - selectAll: Выберите все подходящие варианты. answerRequired: Пожалуйста, ответьте перед продолжением. singleChoiceRequired: Выберите один вариант перед продолжением. multiChoiceRequired: Выберите хотя бы один вариант перед продолжением. diff --git a/openspec/changes/add-keyboard-form-runner-controls/.openspec.yaml b/openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/.openspec.yaml similarity index 100% rename from openspec/changes/add-keyboard-form-runner-controls/.openspec.yaml rename to openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/.openspec.yaml diff --git a/openspec/changes/add-keyboard-form-runner-controls/design.md b/openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/design.md similarity index 100% rename from openspec/changes/add-keyboard-form-runner-controls/design.md rename to openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/design.md diff --git a/openspec/changes/add-keyboard-form-runner-controls/proposal.md b/openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/proposal.md similarity index 100% rename from openspec/changes/add-keyboard-form-runner-controls/proposal.md rename to openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/proposal.md diff --git a/openspec/changes/add-keyboard-form-runner-controls/specs/anonymous-form-runner/spec.md b/openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/specs/anonymous-form-runner/spec.md similarity index 100% rename from openspec/changes/add-keyboard-form-runner-controls/specs/anonymous-form-runner/spec.md rename to openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/specs/anonymous-form-runner/spec.md diff --git a/openspec/changes/add-keyboard-form-runner-controls/tasks.md b/openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/tasks.md similarity index 100% rename from openspec/changes/add-keyboard-form-runner-controls/tasks.md rename to openspec/changes/archive/2026-04-17-add-keyboard-form-runner-controls/tasks.md diff --git a/openspec/specs/anonymous-form-runner/spec.md b/openspec/specs/anonymous-form-runner/spec.md index 3e199de..07780d6 100644 --- a/openspec/specs/anonymous-form-runner/spec.md +++ b/openspec/specs/anonymous-form-runner/spec.md @@ -30,6 +30,33 @@ The system SHALL present form blocks one at a time starting from the first saved - **WHEN** a respondent advances past a step on a form that disables back navigation - **THEN** the system does not provide runner navigation that returns them to an earlier visited step +### Requirement: Runner supports keyboard-first response entry +The system SHALL support keyboard-first answering for the active step in the public form runner. For single-choice, multiple-choice, and agreement questions, the system SHALL map visible options in display order to digit shortcuts using `1` through `9` and `0` for the tenth visible option when present, and SHALL show respondents that those shortcuts are available. The shortcut affordances for those choice-style actions SHALL remain visible on mobile layouts as well as larger screens. For questions with a primary typed input, the system SHALL focus that input when the step becomes active. Pressing Enter on the active step SHALL advance to the next question or submit on the last step when validation succeeds, and SHALL NOT also toggle or change a choice answer. + +#### Scenario: Respondent selects a single-choice option with a number key +- **WHEN** a respondent is on a single-choice question and presses the digit key for a visible option +- **THEN** the system selects that option as the current answer for the question + +#### Scenario: Respondent toggles a multiple-choice option with a number key +- **WHEN** a respondent is on a multiple-choice question and presses the digit key for a visible option that is already selected +- **THEN** the system removes that option from the current answer for the question + +#### Scenario: Respondent sees that keyboard shortcuts are available +- **WHEN** a respondent views a choice-based question in the public runner +- **THEN** the system shows visible shortcut affordances or helper text indicating which number keys can be used for the available options + +#### Scenario: Respondent sees shortcut hints on mobile +- **WHEN** a respondent views a choice-based question on a mobile layout +- **THEN** the system still shows the shortcut affordances for the available actions instead of hiding them at that viewport size + +#### Scenario: Respondent lands on a text-input question +- **WHEN** a respondent advances to a short text, long text, number, link, or date question +- **THEN** the system focuses the primary input control for that question by default + +#### Scenario: Respondent presses Enter on a choice question +- **WHEN** a respondent presses Enter on an active choice question after making a valid selection +- **THEN** the system advances to the next step or submits on the last step without changing the current choice selection as a side effect + ### Requirement: Restored public runner sessions honor the form's back-navigation mode The system SHALL restore saved public-runner progress only when the saved route remains valid for the current form, and resumed interaction SHALL honor the form's currently configured back-navigation mode.