Skip to content

Accessibility

Astrolabe targets WCAG 2.2 AA in every part a student or an author touches. This page lists what you get without doing anything, and what to keep when you write your own component or theme.

Keyboard. Everything works with the keyboard alone: Tab through gaps, options and buttons, arrow keys inside a group of radio buttons, Space to tick a checkbox, Enter on buttons. Escape closes the "Hand in anyway?" question. The end-to-end tests solve every example with the keyboard only.

Native controls. Gaps are <input> elements, closed questions are a <fieldset> with a <legend> and native radio buttons or checkboxes, actions are <button> elements. A "Check" button that cannot be used right now is aria-disabled and stays focusable, so screen reader users can still find it.

Verdicts never by colour alone. Every verdict has an icon, a word and a border style: solid for correct, double for partly correct, dashed for not yet, dotted for no answer. In gaps the word is linked to the field with aria-describedby; incorrect fields also get aria-invalid.

One live region. Each player has one polite live region. It announces the result after a check or a hand-in (score, counts per verdict, pass or not), the minutes left at 5 and 1 minute before a deadline, the time running out, and loading or grading problems.

Focus management. Going to another page moves the focus to the page heading. Handing in moves it to the result. The confirmation before a hand-in with empty answers takes the focus, and Escape returns it to the button.

Structure. Page headings are <h2>; the page progress is a <nav> with a list, the current page marked with aria-current="step" and each page's state in words ("Page 2, partly answered"). The countdown is a labelled role="timer". While loading, the player is aria-busy.

Labels. Each gap has a name such as "Gap 3", in the UI language. A question's legend is its prompt plus a hint ("Choose one answer", "Choose all correct answers").

Sizes and focus. Targets are at least 44 by 44 pixels (--astl-target-min). The focus ring is 3 pixels wide in --astl-focus, with a 2 pixel offset, and reaches 3:1 against every background.

Contrast and themes. Every text pair reaches 4.5:1, and 7:1 in the high-contrast theme; borders, focus rings and verdict colours reach 3:1. The token reference lists every pair with its ratio. Under forced-colors: active the player uses system colours; under prefers-reduced-motion: reduce it does not animate.

Two languages. The chrome (buttons, counters, announcements) carries the UI locale in lang and dir; the exercise keeps its own lang and dir. A verdict word inside an English exercise with a Polish UI is marked as Polish, so screen readers read each part with the right voice.

Reflow. Nothing overflows at 320 CSS pixels; long answers in gaps wrap when they are shown read-only.

How it is checked: the component tests render every dataset in every state, theme (light, dark, high contrast), locale and width (320, 768, 1280) and run axe (0 serious or critical violations), target size, focus and overflow checks; the end-to-end tests drive the examples with the keyboard and run axe on every page of this site.

The player gives every element a context and a set of helpers. Use them and your component inherits the guarantees above.

  • Native controls first. An <input>, a <select>, radio buttons in a <fieldset> give keyboard behaviour, focus and screen reader support for free. Put controls that belong together (a radio group) in one shadow root.
  • Name every control. Use the context's number for numbered labels ("Gap 3") and messages from your catalog, never text in code.
  • Verdicts: icon, word and border style. Render them with renderVerdict(verdict, t, options) and verdictStyles from @astrolabe/player; pass lang and dir of the UI locale (the element's uiLanguage) when the word sits inside content.
  • Announce through the player. Call context.announce(message) (the element's announce) instead of adding a live region; the player has exactly one.
  • Respect disabled. After a hand-in or in review the player sets disabled: show the answer, read only and in full, and keep it focusable when it can be long.
  • Targets and focus. Use --astl-target-min for every target and keep the focus ring visible (--astl-focus, --astl-focus-width, --astl-focus-offset).
  • Logical CSS and tokens only. margin-inline-start, not margin-left, so the layout follows dir; colours from tokens, so themes and forced colours work.
  • Describe the pattern. Fill a11y in the manifest with the ARIA pattern and the keys that operate it; the generated reference shows them to everyone who uses your component.
  • Test it like the built-ins. Add fixtures in several languages, render them in every state and theme, and run axe, a keyboard walk-through and a 320-pixel overflow check.

The custom component guide has a complete element that follows these rules.

  • Keep the player's region free of fixed overlays and keep the page zoomable.
  • When you change tokens, keep the contrast pairs (see Looks).
  • Set locale on <astl-player> to the reader's language, and lang on <astl-doc> to the exercise's language.