Digital accessibility · Hands-on practice

Interaction lab

Feel what a useful error message, a well-managed dialog and a keyboard-friendly tab set do. Try each example, then inspect the pattern behind it.

Learn by completing a task

1. Recover from an error

Submit an empty form. Follow a specific error back to its field, correct it and complete the demo.

2. Keep your place

Open a dialog with the keyboard. Explore its content, close it and check where focus returns.

3. Explore, then choose

Move between tab labels without changing the panel. Activate one when you are ready.

These are learning examples, not a complete WCAG assessment. Combine keyboard checks with screen reader testing, zoom, contrast and real task completion.

Example 01 · Forms

A booking form that helps you recover

Fictional workshop booking

Practice only: this does not book a place or send email. Use fictional details. This demo sends and saves none of your entries.

All three fields are required.

Use a made-up name, for example Alex Example.

Use [email protected] for this practice.

Any option works in this fictional booking.

Try this with your keyboard

  1. Reach Check demo booking and press Enter with all fields empty. Focus moves to a summary of three errors.
  2. Press Tab to an error link, then Enter. Its field receives focus and has both its hint and error description.
  3. Enter Alex Example, [email protected] and a workshop. Resubmit. The confirmation appears without moving you away.

What makes this work

Persistent visible labels identify each field. Required fields are marked in words. Errors explain how to recover and remain linked to the affected input. Correcting an error removes its invalid state and keeps the original hint.

Initial validation happens on submit. Corrections update quietly; the demo does not announce every keystroke. A polite status confirms a successful action.

For real personal-information forms, appropriate autocomplete tokens identify input purpose. Keep server validation too: this local demo is not a backend or an email-delivery check.

Pattern references: WAI form labels, WAI error and success notifications, WCAG 2.2 · 1.3.5 input purpose (AA).

Read the form error pattern

A focused excerpt. Use the full example and test its behavior in context.

<label for="email">Email (required)</label>
<input id="email" type="email" autocomplete="email"
  required aria-describedby="email-hint">
<p id="email-hint">For example, [email protected].</p>
<p id="email-error" hidden></p>

// After an unsuccessful submit:
email.setAttribute("aria-invalid", "true");
email.setAttribute("aria-describedby", "email-hint email-error");
error.textContent = "Enter an email such as [email protected].";
error.hidden = false;
// Add a linked error to the summary, then focus the summary.
summary.hidden = false;
summary.focus();

// When the error is corrected, keep the permanent hint:
email.removeAttribute("aria-invalid");
email.setAttribute("aria-describedby", "email-hint");
error.hidden = true;

Example 02 · Focus management

A dialog with a clear way back

Practice notes

Open a short checklist. Notice the starting point and the visible focus indicator as you move through it.

Your keyboard practice notes

Focus starts on this heading so you can read the context before reaching a control.

  1. Tab into the checkbox and toggle it with Space.
  2. Tab through the close button. The page behind this modal cannot receive focus.
  3. Close with Escape or the button, then check that focus returns to the opener.

Keyboard route

Press Enter on the opener. Use Tab and Shift + Tab to cycle through the modal controls. Try Escape, reopen it, and try the close button.

This uses native <dialog> with showModal(). The heading is an intentional initial focus target. The background becomes inert while the modal is open, and dismissal restores focus to its opener.

Choose initial focus for the task: a reading position may suit explanatory content; a safe action may suit a confirmation. Only contain focus while a modal is open.

Pattern reference: W3C APG modal dialog. APG describes interaction patterns; it is not a WCAG conformance certificate.

Read the native dialog pattern

A focused excerpt. Use the full example and test its behavior in context.

<button id="open-notes" type="button">Open notes</button>
<dialog id="notes" aria-labelledby="notes-title">
  <h2 id="notes-title" tabindex="-1">Practice notes</h2>
  <p>Read the notes, then close this dialog.</p>
  <button id="close-notes" type="button">Close dialog</button>
</dialog>

const opener = document.getElementById("open-notes");
const dialog = document.getElementById("notes");
opener.addEventListener("click", () => {
  dialog.showModal();
  document.getElementById("notes-title").focus();
});
document.getElementById("close-notes").addEventListener("click",
  () => dialog.close());
dialog.addEventListener("close", () => opener.focus());
// Native Escape dismisses the modal. The full demo additionally
// wraps Tab from its last control to its first, and Shift+Tab
// from its first control (or initial heading) to its last.
// Scope any wrapping handler to the open dialog only.

Example 03 · Composite widgets

Tabs that wait for your choice

Keyboard perspective

Reach every control without a mouse. Look for a visible focus outline and a logical route. Complete the action, then find your way back.

Practice with the form

Screen reader perspective

Listen for each control's name, role and state. Check whether errors, selected tabs and the dialog title give you enough information to continue.

Record your observations

Zoom perspective

Increase browser zoom and text size. Check that controls and messages remain reachable, and focused items are not hidden by fixed content.

Follow the testing workflow

Move focus, then activate

  1. Tab into the tab list. Use or to move to another label. The panel stays as it was.
  2. Use Home and End to reach the first and last labels.
  3. Press Enter or Space to activate the focused tab. Press Tab to enter the displayed panel.

Only one tab is in the page's Tab sequence. The active tab exposes aria-selected="true"; each panel is labelled by its tab. All panels remain ordinary readable sections when JavaScript is unavailable.

Pattern reference: W3C APG tabs. This example uses manual activation, separating focus from selection.

Read the manual tab pattern

A focused excerpt. Use the full example and test its behavior in context.

// Run after adding tablist, tab and tabpanel roles.
// Each tab's aria-controls points to its labelled panel.
function activate(next) {
  tabs.forEach((tab, index) => {
    const selected = index === next;
    tab.setAttribute("aria-selected", String(selected));
    tab.tabIndex = selected ? 0 : -1;
    panels[index].hidden = !selected;
  });
}

// On Left/Right/Home/End: move focus, not the displayed panel.
function moveFocus(next) {
  tabs.forEach((tab, index) => {
    tab.tabIndex = index === next ? 0 : -1;
  });
  tabs[next].focus();
}
// Activate on click. Native buttons also produce a click for
// Enter and Space. Do not cancel their default key behavior.
tabs.forEach((tab, index) => {
  tab.addEventListener("click", () => activate(index));
});

Optional focus inspector

Inspector off.

The readout shows an element name and ID, never your form entries. It is deliberately not a live region, so it does not interrupt screen reader announcements on every focus change.

Also test at zoom and with sticky headers: WCAG 2.2 · 2.4.11 Focus Not Obscured (Minimum), AA requires a focused component not to be entirely hidden by author-created content.

Take the practice into your own site

Record the task, browser, assistive technology, expected behavior and what actually happened. A working example helps you learn the pattern; testing a complete journey tells you whether your product works for people.