Basic single answer
A copy-pasteable radio-button quiz with two attempts and progressive feedback.
Progressive quiz behavior for semantic HTML forms, with retry feedback, single- and multiple-answer scoring, and practice, exam, and review modes.
a11y-quiz-formChoose an answer and check it. A wrong first attempt reveals targeted feedback; a correct or final answer reveals all explanations.
Practice · 2 attempts · Radio buttons
Practice · 2 attempts · Checkboxes
Exam · 1 attempt · No hint
Review · Read-only · All explanations visible
Use these smaller pages when you need one behavior, configuration boundary, or lifecycle question without the full documentation page around it.
A copy-pasteable radio-button quiz with two attempts and progressive feedback.
Matched checkbox quizzes make exact versus partial scoring easy to compare.
Test Spanish validation, retry, progress, result, reveal, and answer-state messages.
Apply mode and option presets from controls kept outside the runtime plugin form.
Inspect cleanup artifacts, exact author-markup equality, restoration, and reinitialization.
Replace answer markup while inspecting selection migration, focus recovery, and refresh event details.
The repository is configured for npm packaging. Confirm registry availability before using the package name in a production install.
npm install a11y-quiz-form
pnpm add a11y-quiz-form
yarn add a11y-quiz-form
Supply the semantic form markup. The plugin adds feedback behavior and live-region relationships; it does not generate the question or answers.
<form class="a11y-quiz" data-a11y-quiz novalidate>
<fieldset class="a11y-quiz__fieldset">
<legend class="a11y-quiz__question">Which element is a button?</legend>
<p class="a11y-quiz__instruction">Choose one answer.</p>
<div class="a11y-quiz__answers">
<div class="a11y-quiz__answer" data-correct="true">
<label class="a11y-quiz__answer-label">
<input class="a11y-quiz__input" type="radio" name="answer" value="button">
<span class="a11y-quiz__answer-text"><button></span>
</label>
<p class="a11y-quiz__explanation" hidden>A button has native semantics.</p>
</div>
</div>
<p class="a11y-quiz__status"></p>
<div class="a11y-quiz__actions">
<button type="submit">Check answer</button>
<button type="button" data-quiz-show-answer hidden>Show answer</button>
<button type="button" data-quiz-reset hidden>Try again</button>
</div>
</fieldset>
</form>
import { createQuizForm } from "a11y-quiz-form";
import "a11y-quiz-form/styles.css";
const form = document.querySelector("[data-a11y-quiz]");
if (form instanceof HTMLFormElement) {
createQuizForm(form, { maxAttempts: 2 });
}
See the focused basic example for a complete standalone file.
Pass options to createQuizForm() or use the matching
data-* attributes. Programmatic options take precedence and invalid values
fall back to the defaults.
| Option | Type | Default | Purpose |
|---|---|---|---|
mode |
"practice" | "exam" | "review" |
"practice" |
|
maxAttempts |
number |
2 |
Sets allowed checks from 1 through 100; exam mode always uses one. |
explanationMode |
QuizFormExplanationMode |
selected-after-wrong |
Controls feedback visibility after a retryable incorrect answer. |
showAnswerMode |
QuizFormShowAnswerMode |
after-first-wrong |
Controls when the optional reveal button becomes available. |
scoringMode |
"exact" | "partial" | "all-or-nothing" |
exact |
|
allowReset |
boolean |
true |
Enables reset() and shows the reset action after completion. |
disableAfterComplete |
boolean |
true |
Disables answer inputs after completion or reveal. |
focusResult |
boolean |
false |
Moves focus to the result summary after completion; default behavior preserves focus. |
announceChanges |
boolean |
true |
Writes feedback to the polite status region. |
shuffleAnswers |
boolean |
false |
Randomizes answer-card DOM order once during initialization. |
showProgress |
boolean |
false |
Adds an attempt progress indicator. |
disableCheckUntilAnswered |
boolean |
true |
Disables the submit button until at least one answer is selected. |
messages |
QuizFormMessages |
English catalog | Partially overrides validation, status, progress, summary, and answer-state text. |
Pass a partial typed message catalog. Strings work for fixed copy; callbacks receive
attempt, maxAttempts, and remainingAttempts for
grammar and pluralization. Missing, empty, invalid, or throwing overrides safely use
the existing English default.
import { createQuizForm, type QuizFormMessages } from "a11y-quiz-form";
const messages = {
validationRequired: "Elige al menos una respuesta.",
correct: ({ attempt }) => `Correcto en el intento ${attempt}.`,
incorrectRetry: ({ remainingAttempts }) =>
remainingAttempts === 1
? "Queda un intento."
: `Quedan ${remainingAttempts} intentos.`,
} satisfies QuizFormMessages;
createQuizForm(form, { messages });
Keep live-region and validation messages concise, meaningful, and non-empty. Include the result or action and any remaining-attempt context needed without relying on nearby visual content. Callbacks intentionally receive attempt counts, not selected values. Do not close over or place private response data, personal information, or secure answer material in messages because the text can be visible and announced by assistive technology.
The exported QuizFormMessages, QuizFormMessageContext, and
QuizFormMessageKey types define the catalog. See the repository README for
the complete message-key table and English default purposes.
| Method | Description |
|---|---|
createQuizForm(form, options?) |
Enhances one form and reuses its existing instance. |
initQuizForms(options?, root?) |
Enhances every declarative form in a document or parent node. |
quizFormEvents |
Provides frozen constants for every public lifecycle event name. |
onQuizFormEvent(target, name, listener, options?) |
Adds a typed lifecycle listener and returns idempotent cleanup. |
check() |
Checks the current answer selection. |
reveal() |
Reveals the correct answers and explanations. |
reset() |
Returns the instance to its initial interaction and checked/disabled input state. |
getState() |
Returns state, attempts, selected values, and correctness. |
destroy() |
Removes listeners and generated feedback, restores author-owned state and answer order, then emits the destroy event. |
Events dispatch synchronously from the form, bubble, do not cross a shadow boundary,
and are non-cancelable. Their detail includes instance and the
compatibility alias quiz, plus the fields below.
| Event | Trigger | Additional detail |
|---|---|---|
a11yquiz:init | Enhancement begins after initial state capture. | — |
a11yquiz:ready | Enhancement and initial synchronization finish. | — |
a11yquiz:change | A managed input changes. | selected |
a11yquiz:check | An active check is accepted. | selected, valid, attempt |
a11yquiz:correct | Correct-result UI finishes. | attempt |
a11yquiz:incorrect | Incorrect-result UI finishes. | attempt, canRetry |
a11yquiz:retry | A retry is available. | attempt |
a11yquiz:reveal | Answer-reveal UI finishes. | — |
a11yquiz:reset | An accepted reset finishes. | — |
a11yquiz:complete | A run first reaches a terminal result. | correct, revealed, attempt |
a11yquiz:destroy | Cleanup and author-DOM restoration finish. | — |
Guaranteed sequences include init → ready,
check → correct → complete,
check → incorrect → retry, and
check → incorrect → complete. When reveal first completes a run it emits
reveal → complete; a later permitted reveal emits only reveal.
| Key | Behavior |
|---|---|
| Tab / Shift+Tab | Moves through the native form controls, hint summary, and action buttons. |
| Arrow keys | Uses the browser’s native radio-group selection behavior. |
| Space | Selects a focused radio button or checkbox, or activates a focused button. |
| Enter | Activates a focused button and submits the quiz through its native form behavior. |
The stylesheet uses the .a11y-quiz block with BEM elements including
__fieldset, __answer, __input,
__actions, __status, and __explanation.
Runtime states use .is-selected, .is-correct, and
.is-incorrect.
Theme the component with prefixed properties such as
--a11y-quiz-color-action-bg,
--a11y-quiz-color-focus-ring,
--a11y-quiz-color-correct-bg,
--a11y-quiz-radius, and
--a11y-quiz-max-width. Properties beginning with --_
are private implementation details.
.a11y-quiz {
--a11y-quiz-color-action-bg: #5b21b6;
--a11y-quiz-color-action-hover: #4c1d95;
--a11y-quiz-color-focus-ring: #fbbf24;
--a11y-quiz-radius: 0.75rem;
}
data-correct attributes. Exam mode changes interaction, but it is not a secure assessment system.Treat this package as an interaction layer. Content design, answer quality, localization, end-to-end assistive-technology testing, and server-side assessment security remain the integrating application’s responsibility.