Accessibility statement
Last updated 15 October 2026
Kodukeel is for people learning Estonian, and plenty of them are learning it because they have to. This page says what standard we’re aiming for, what we’ve checked and how, where we fall short today, and how to tell us.
The standard
The target is WCAG 2.2 level AA, and through it EN 301 549, which is the European harmonized standard that public bodies and their suppliers are measured against and which adopts WCAG for web content.
Conformance status
Partially conformant with WCAG 2.2 level AA. Partially conformant means most of the app meets the standard, and the parts named below do not.
It isn’t fully conformant, because nobody who relies on assistive technology has yet been paid to test it. Everything below rests on automated checks and on the people who wrote the app testing their own work.
How the claim was tested
Four suites run in the build on every change, against the real app in a real browser rather than against a component in isolation.
- axe over every route. a11y-check.mjs loads every page the app has, not a chosen sample, and runs axe over each one, including its best-practice rules. Two screens are the exception so far, a marked paper and a scanned page, because each needs a row made before it exists; the containment suite below makes both and measures them, and axe does not reach them yet. It runs the whole sweep again in the dark theme, because light and dark are two palettes and a color that clears the bar in one says nothing about the other. It runs it twice more at 390 pixels wide, which is a phone, because a phone here is different markup rather than the same markup narrower: the navigation rail is not drawn at all and a bar with a sheet behind it is drawn instead. That sheet is opened and swept too, since no address reaches it. It counts a one-character run of text as text, which is how a check mark measured at 2.52 against a bar of 4.5 was found. Beyond axe it asserts exactly one main landmark and one heading per screen, a page title that is not the landing page’s, and Estonian marked so a screen reader does not read it with English phonics.
- Contrast measured rather than reasoned about. test-design.mjs reads the colors the browser actually painted and works out the ratio, in both themes, including states a page does not arrive in such as a row under a pointer.
- Every target measured against 44px. test-mobile.mjs drives the app at 360, 390, 430, 768 and 1280 pixels wide with a coarse pointer, which is the only condition under which that rule is real, and fails on a control below the floor.
- Reflow at 320 pixels. test-mobile.mjs opens every route 320 CSS pixels wide, which is what 400% zoom means in a 1280 window, and fails on anything drawn past the edge of the screen outside a scroller of its own, such as a table of forms.
- Text staying inside its box. test-containment.mjs walks every route at 360, 768 and 1280, in both themes, and asks whether anything is cut off, drawn outside its border or drawn on top of something else. Then it asks again with every run of text replaced by unbreakable text of the same length, since Estonian has long words.
Alongside those, the app is built out of real buttons and links with a visible focus ring. Animation is turned off for anybody whose system asks for reduced motion, and no part of it needs a dragging movement. Color is never the only thing carrying a distinction, so a correct answer says so in words as well as in color.
The timed practice round can be set to run longer (WCAG 2.2.1, Timing Adjustable). The daily quest is two minutes to start with, and one setting stretches it up to ten times as long, which is the figure the criterion asks for. It is chosen before the round starts, in Settings, and the start screen links to it.
No audit by a person with a disability using assistive technology has been commissioned. No screen reader user has been paid to test this, and there has been no third-party accessibility audit. Automated tools find perhaps a third of what is wrong with a page, and the third they find is the mechanical third.
What is known not to conform
These were found by reading the code rather than assumed from a template, and each one is a real screen.
- The mock examination parts run to a clock nobody can change (WCAG 2.2.1, Timing Adjustable), and it closes the part when it goes. The paper is an imitation of a timed state examination and untimed practice of a timed paper measures something else, which is why it is the one clock left fixed, but a candidate who needs extra time in the real examination has no way to ask for it here.
- The listening, dictation and minimal-pair rounds require hearing (WCAG 1.2.1). There is no transcript before the answer, because the transcript is the answer, so a text alternative would remove the exercise. The text is shown as soon as the answer is in. Somebody who cannot use audio can use every other round, and the app does not require any of these three to make progress, but three rounds are closed to them.
- Speaking is heard by a machine on the device, which says which words came across and never how they sounded. It is new and has not been measured on learners' own voices yet. Every step of the Speaking round can be answered by typing, so a learner with no microphone, or who would rather not use one, still has a way through it.
- axe is swept at two widths, not at every width. It runs at 1280 and at 390, which is either side of the one breakpoint that swaps the navigation, so the phone bar and the sheet behind it are covered now. The widths between them, 360, 430 and 768, are measured for targets, overflow and containment and are not swept by axe. A fault in markup that appears at one of those and at neither of these would be found by a person rather than by the build.
- Screens behind data are less covered. The automated sweep sees each page as it loads, plus one state a learner has to reach by doing something. A class roster with students in it, an examination part in progress and the puzzle grids in play are all reachable and all less tested than the pages around them.
- Single-key shortcuts cannot be turned off (WCAG 2.1.4, Character Key Shortcuts). A round answers a number to grade a card or B to look back, which helps on a keyboard and can be set off by speech input saying one letter. There is no setting yet to turn them off.
- Some decorative letters keep moving (WCAG 2.2.2, Pause, Stop, Hide). The four letters on the landing page’s case card wander for as long as the page is open. They stop for anybody whose system asks for reduced motion, and there is no control on the page itself.
- A word plays on its own when a card appears (WCAG 1.4.2, Audio Control). Settings turns that off, and a clip is one word or one sentence, but nothing checks that every clip is under three seconds.
Where a limitation above is ours rather than one the exercise requires, it is on the list to fix. The practice clocks were the first and are adjustable now, and the phone sweep was the second: it used to be on this list and it runs in the build. The examination clock stays, for the reason beside it.
Criterion by criterion
Every WCAG 2.2 criterion at levels A and AA, and where the app stands on it. EN 301 549 adopts them as its chapter 9, one clause each. A criterion is marked as supported only where a check in the build asserts it, and the check is named. Everywhere else the row says why not.
| Criterion | EN 301 549 | Where it stands | Checked by, or why not |
|---|---|---|---|
| 1.1.1 Non-text Content (A) | 9.1.1.1 | Supports | scripts/a11y-check.mjs |
| 1.2.1 Audio-only and Video-only (Prerecorded) (A) | 9.1.2.1 | Partially supports | The listening, dictation and minimal-pair rounds need hearing, as the statement above explains. |
| 1.2.2 Captions (Prerecorded) (A) | 9.1.2.2 | Not applicable | The app has no video. |
| 1.2.3 Audio Description or Media Alternative (Prerecorded) (A) | 9.1.2.3 | Not applicable | The app has no video. |
| 1.2.4 Captions (Live) (AA) | 9.1.2.4 | Not applicable | The app has no video. |
| 1.2.5 Audio Description (Prerecorded) (AA) | 9.1.2.5 | Not applicable | The app has no video. |
| 1.3.1 Info and Relationships (A) | 9.1.3.1 | Supports | scripts/a11y-check.mjs |
| 1.3.2 Meaningful Sequence (A) | 9.1.3.2 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.3.3 Sensory Characteristics (A) | 9.1.3.3 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.3.4 Orientation (AA) | 9.1.3.4 | Supports | scripts/invariants/no-orientation-lock.ts |
| 1.3.5 Identify Input Purpose (AA) | 9.1.3.5 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.4.1 Use of Color (A) | 9.1.4.1 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.4.2 Audio Control (A) | 9.1.4.2 | Partially supports | A word plays on its own when a card appears. Settings turns that off, and nothing checks how long a clip is. |
| 1.4.3 Contrast (Minimum) (AA) | 9.1.4.3 | Supports | scripts/test-design.mjs |
| 1.4.4 Resize Text (AA) | 9.1.4.4 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.4.5 Images of Text (AA) | 9.1.4.5 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.4.10 Reflow (AA) | 9.1.4.10 | Supports | scripts/test-mobile.mjs |
| 1.4.11 Non-text Contrast (AA) | 9.1.4.11 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.4.12 Text Spacing (AA) | 9.1.4.12 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 1.4.13 Content on Hover or Focus (AA) | 9.1.4.13 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.1.1 Keyboard (A) | 9.2.1.1 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.1.2 No Keyboard Trap (A) | 9.2.1.2 | Supports | scripts/invariants/dialogs-hold-focus.ts |
| 2.1.4 Character Key Shortcuts (A) | 9.2.1.4 | Partially supports | Some rounds answer a single key, such as a number to grade or B to look back, and these cannot yet be turned off. |
| 2.2.1 Timing Adjustable (A) | 9.2.2.1 | Partially supports | The mock examination keeps a fixed clock, as the statement above explains. Every practice clock can be set longer. |
| 2.2.2 Pause, Stop, Hide (A) | 9.2.2.2 | Partially supports | Some decorative letters keep moving and stop only when the system asks for reduced motion. |
| 2.3.1 Three Flashes or Below Threshold (A) | 9.2.3.1 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.4.1 Bypass Blocks (A) | 9.2.4.1 | Supports | scripts/a11y-check.mjs |
| 2.4.2 Page Titled (A) | 9.2.4.2 | Supports | scripts/a11y-check.mjs |
| 2.4.3 Focus Order (A) | 9.2.4.3 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.4.4 Link Purpose (In Context) (A) | 9.2.4.4 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.4.5 Multiple Ways (AA) | 9.2.4.5 | Supports | lib/ux/nav.test.ts |
| 2.4.6 Headings and Labels (AA) | 9.2.4.6 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.4.7 Focus Visible (AA) | 9.2.4.7 | Partially supports | The visible focus ring is checked for text fields only. |
| 2.4.11 Focus Not Obscured (Minimum) (AA) | Not yet a clause | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.5.1 Pointer Gestures (A) | 9.2.5.1 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.5.2 Pointer Cancellation (A) | 9.2.5.2 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.5.3 Label in Name (A) | 9.2.5.3 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.5.4 Motion Actuation (A) | 9.2.5.4 | Not applicable | Nothing is operated by moving or shaking the device. |
| 2.5.7 Dragging Movements (AA) | Not yet a clause | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 2.5.8 Target Size (Minimum) (AA) | Not yet a clause | Supports | scripts/test-mobile.mjs |
| 3.1.1 Language of Page (A) | 9.3.1.1 | Supports | scripts/a11y-check.mjs |
| 3.1.2 Language of Parts (AA) | 9.3.1.2 | Supports | scripts/a11y-check.mjs |
| 3.2.1 On Focus (A) | 9.3.2.1 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.2.2 On Input (A) | 9.3.2.2 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.2.3 Consistent Navigation (AA) | 9.3.2.3 | Supports | lib/ux/nav.test.ts |
| 3.2.4 Consistent Identification (AA) | 9.3.2.4 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.2.6 Consistent Help (A) | Not yet a clause | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.3.1 Error Identification (A) | 9.3.3.1 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.3.2 Labels or Instructions (A) | 9.3.3.2 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.3.3 Error Suggestion (AA) | 9.3.3.3 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.3.4 Error Prevention (Legal, Financial, Data) (AA) | 9.3.3.4 | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.3.7 Redundant Entry (A) | Not yet a clause | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 3.3.8 Accessible Authentication (Minimum) (AA) | Not yet a clause | Partially supports | Nothing in the build checks this yet, so it is not claimed. |
| 4.1.2 Name, Role, Value (A) | 9.4.1.2 | Supports | scripts/a11y-check.mjs |
| 4.1.3 Status Messages (AA) | 9.4.1.3 | Supports | scripts/invariants/verdicts-are-announced.ts |
Six of these criteria are new in WCAG 2.2 and are not yet clauses of EN 301 549 version 3.2.1, which adopts WCAG 2.1. Chapter 11 of the standard is for software that is not a web page, so it does not apply to an app used in a browser.
When this was prepared
Prepared on 5 September 2026 by the people who wrote the app, from the automated suites described above and from reading the source, and the table of criteria was added on 15 October 2026. It is reviewed whenever a screen changes enough to move one of the claims on it. There has been no external review.
Telling us about a problem
Write to privacy@upthink.ee. Say what you were trying to do, what happened, and what you were using to do it with, if you can. A message that just says “this screen doesn’t work with my screen reader” is still worth sending: the person reading it can go and look.
Every screen in the app also has a way to report something that is wrong with it, beside the thing that went wrong, and those reports reach the same people.
If that gets you nowhere
The enforcement route for accessibility in Estonia runs through the state authority responsible for the Web Accessibility Directive, and a complaint about how personal data is handled goes to the Estonian Data Protection Inspectorate (Andmekaitse Inspektsioon), Tatari 39, 10134 Tallinn, Estonia, info@aki.ee. Do write to us first, though. A real person reads it, and a fix is usually quicker than a complaint.
See also Trust and security and Privacy.
Privacy, Terms, Funding, Trust, For teachers, Running a pilot, Accessibility, The state examination, Help, Sign in, Contact