Accessibility conformance report · No Look Typing

What works, what doesn’t, and what we haven’t tested

This is a VPAT-style report against WCAG 2.1 Level AA for the No Look Typing app at nolooktyping.com/play/. It is written to be checked. Where we fall short we have said so and given the measurement, because a report that overclaims is worse than one that admits gaps.

Report published 8 September 2026 · based on testing of the build deployed 8 September 2026 · author: the No Look Typing development team · self-assessment

At a glance

Fifty success criteria, assessed one at a time

The four outright failures are Reflow (the on-screen keyboard inside a lesson loses its outer key columns on a narrow screen), Timing Adjustable (practice runs have a fixed clock that cannot be extended or switched off), Pause, Stop, Hide (a small animation on the reach arrow loops with no way to stop it) and Non-text Contrast (the border colour used throughout the interface is below the required ratio). All four have a fix identified and none of them requires re-architecting anything.

The product does some things unusually well for its size: every control on every screen has a proper accessible name, the whole app is keyboard-driven by nature, its modal dialogs are implemented correctly down to focus restoration, and it ships two themes whose body text clears AA by a wide margin in both.

Method — read this before the table

How this was tested, and the important limit on it

No screen reader has been run against this app

Not NVDA, not JAWS, not VoiceOver, not TalkBack, not Narrator. Everything in this report was established by reading the source code, computing colour contrast arithmetically, and driving the live page through the browser’s DOM and accessibility APIs.

That is enough to prove that the right markup is present and behaves correctly. It is not enough to prove that a blind person can use the practice screen, and we are not going to claim otherwise. Every judgement below that depends on how assistive technology actually behaves is marked needs AT confirmation, and the specific open questions are listed at the end.

Also untested: Windows High Contrast / forced-colors mode, voice control (Dragon, Voice Control), switch access, screen magnification combined with assistive technology, and mobile screen readers.

What was done

  • Source review of the whole app: 1 HTML file, 1 stylesheet, 15 ES modules.
  • Contrast computed for 198 foreground/background pairs in both themes, plus further passes for state boundaries and for the way the on-screen hints fade, using the real sRGB transfer function rather than a browser helper, including alpha compositing and the opacity fades the app applies as hints wear off.
  • Live testing at 1440×900, 720×450 (the 200% zoom equivalent), 500×900 and 320×900.
  • Programmatic checks across all four tabs for unnamed controls, placeholder-only labels, duplicate IDs, positive tabindex, unlabelled graphics and heading structure.
  • A real practice run driven keystroke by keystroke, checking what each live region actually received.
  • Keyboard operation of every dialog: focus placement, focus trapping, focus restoration, and escape.
  • The WCAG 1.4.12 text-spacing override applied and checked for clipping.

Scope

The web application at nolooktyping.com/play/. The marketing page at nolooktyping.com/ has not been separately audited to the same depth; two contrast problems found there in passing are noted at the bottom of this report.

The build tested

index.html1d72d7ab
css/app.css1da9b064
js/app.jsc821fca1
js/keyboard.js1dee85c7
js/keys.js4e8aaa38
js/practice.js8e3780d5

MD5 prefixes, verified against the live server on 8 September 2026 at 02:12 PDT. The app is under active development; a later build may differ.

The problems

What does not work yet

In plain words, worst first. Each one is in the conformance table below with its criterion number.

1. On a narrow screen, part of the on-screen keyboard becomes unreachable

Who this affects: anyone using a small window, a split screen, or high browser zoom — which includes many people with low vision, since zooming is the most common adaptation there is.

What happens: inside the two guided lessons, the picture of the keyboard is a fixed 474 pixels wide and does not shrink below a certain point. At a 320 pixel viewport its outer columns (1 Q A Z on the left, 0 P ’ / on the right) sit outside the visible area, and because the page itself does not scroll sideways there is no way to bring them into view.

Status: fix identified — a smaller key size below 560 pixels plus a horizontal scroll container as a backstop.

What already works: the four main tabs reflow correctly at 320 pixels with nothing clipped, and the practice screen deliberately refuses to run below 700 pixels, showing an explanation and pointing you to the parts of the app that are useful on a phone.

2. Practice runs have a clock you cannot change

Who this affects: anyone who needs more time — people using a screen reader, a switch, an on-screen keyboard, or who simply reads more slowly.

What happens: a practice run lasts 90, 150 or 180 seconds depending on which one you pick, and there is no setting to extend it, adjust it or turn it off. Pressing Escape pauses the clock, which helps, but a pause is not an extension.

Status: fix identified — a practice-length setting offering a normal, a long (three times) and a no-clock option. The engine already supports untimed runs; two of its modes use them.

The one exception we are claiming: the two 60-second speed tests. Words per minute is defined as a rate, so for those the time limit is the measurement — changing it would not produce a longer test, it would produce a different and meaningless number. WCAG allows this under the “Essential” exception, in the same way a stopwatch is essential to a race. That argument covers the two tests and nothing else; the ordinary practice runs have no such defence and will be fixed.

3. Borders and some indicators are too faint

Who this affects: people with low vision or reduced contrast sensitivity, and anyone on a poor screen or in bright light.

What happens: one colour draws most of the boundaries in the app — card edges, button outlines, input outlines, table rules, the tab strip. It measures 1.75–2.61:1 against the surfaces it sits on, where 3:1 is required. Separately, the empty half of every progress bar is 1.12–1.19:1 against the panel behind it, so you can see how far the bar has filled but not how far it could go.

Status: fix identified, with computed replacement values that clear 3:1 on every surface in both themes. This one is a two-line change and is near the top of the queue. Note that the border colour was already darkened once during this audit; the change was a genuine improvement (from 1.36:1) but it did not reach the threshold, which is why we compute these rather than judge them by eye.

Smaller problems, same honesty

The finger-by-finger panel is unreadable in the dark theme

The eight finger names on the PROGRESS tab are printed in a colour set designed for the light page. In the dark theme all nine measure 2.11–4.07:1 against their panel; two of them also miss in the light theme. A second, brighter colour set already exists in the code for exactly this purpose and is simply not being used here.

The spoken hint repeats far too often

The line that tells you which finger to use is announced to screen readers, and it is rewritten on every keystroke — measured at 12 updates a second, each a full sentence. Because these announcements queue rather than interrupt, a screen reader would fall further and further behind. We intend to announce only when the finger changes, and at most once every two seconds.

Four settings buttons do not say what they control

The theme, keyboard, sound and shortcut switches are announced by their current value alone — “MIDNIGHT”, “SHOWN”, “ON”, “MAC”. Their visible labels are next to them but not connected to them in code. Fix: give each an accessible label naming the setting and its state.

The practice calendar has no text version

Ten weeks of practice history are drawn as seventy shaded squares. They now carry graded borders as well as colour, so the intensity no longer depends on telling greens apart — but they still have no text alternative, so a screen reader gets nothing from them. Fix: label each square with its date and minutes, and add a one-sentence summary.

Passwords are collected through a browser prompt

Downloading or deleting an account asks for the password through window.prompt, which shows it in clear text and cannot be labelled or masked. This is on the list to replace with the app’s own dialog, which is already built correctly elsewhere.

The reach arrow animates without a stop control

A small dot travels along a curved arrow from your finger’s home key to the key you owe, looping continuously while that key is on screen. Continuously moving content needs a way to pause or stop it, and there is not one yet. It will be bounded to a few repetitions and switched off for people who ask their system to reduce motion.

Reduced motion is not yet honoured — and we want to correct the record

An earlier internal note claimed this was implemented. It is not. The stylesheet contains two prefers-reduced-motion rules, covering two small decorations; six animations, five transitions and the moving arrow above are not covered. Honouring the setting is a single stylesheet block plus a three-line guard for the arrow, and it is queued. (This is a AAA-level criterion, so it is not a AA conformance failure — but it was claimed, so it belongs here.)

The other side of the ledger

What does work

Every control has a proper name

A sweep of all four tabs found zero unnamed buttons or fields, zero fields relying on placeholder text as their label, no duplicate element IDs, no forced tab order, and no unlabelled graphics. Every input carries an autocomplete value.

The whole app runs from the keyboard

It is a typing tutor, so this was never optional — but it also holds up in the places that usually break: no keyboard traps, focus moves into dialogs and back out to the right place when they close, and when the button you opened a dialog from has been removed by a redraw, focus lands on the main panel rather than at the top of the page.

The dialogs are done properly

Real dialog roles, the page behind made inert so it leaves the tab order and the accessibility tree entirely, Tab cycling inside the dialog, and a safeguard that guarantees the page becomes usable again however a dialog is closed. Focus deliberately lands on the dialog itself rather than on its first button, so Enter and Space cannot fire something you did not mean.

Text contrast is comfortable in both themes

Body text measures 14.17:1 (light) and 16.32:1 (dark). Of the text pairs we computed, the great majority clear AA with room to spare; the failures listed above are specific elements, not a systemic problem with the palette.

The page survives zoom and text spacing

At the 200% zoom equivalent, and again with the WCAG text-spacing override applied, all four tabs render with no horizontal scrolling and nothing clipped or overlapping.

Errors are announced and specific

Sign-in problems are placed in a live alert region rather than only shown. A failed practice run says exactly what was missed — “Needed 34 wpm at 97%. You were 28 at 98% — 6 wpm short” — rather than just “failed”.

The detail

WCAG 2.1 conformance table

Terms are the ones used in the standard VPAT: Supports means the criterion is met; Partially means it is met in most places but there are named exceptions; Does not means it is not met; N/A means the criterion does not apply to this product. The verdict is always written as a word, never signalled by colour alone.

Level A — 30 criteria
CriterionLevel ConformanceRemarks
1.1.1 Non-text ContentA Partially Decorative graphics (the hands, the keyboard) are correctly hidden from assistive technology and have a text equivalent in prose. The progress chart is an image with a label. Exception: the 70-cell practice calendar and the level progress pips are empty elements carrying only a title tooltip — no accessible name, no role, no text alternative (app.js, calendar()).
1.2.1 Audio-only / Video-onlyA N/A No prerecorded media. The only sound is synthesised keystroke feedback generated in the browser.
1.2.2 Captions (Prerecorded)A N/ANo media.
1.2.3 Audio Description or AlternativeA N/ANo media.
1.3.1 Info and RelationshipsA Partially Tabs, panels and dialogs use correct ARIA; the line you are typing is a labelled group with aria-current on the live character; tables use header cells; fields are labelled. Exceptions: there is no h1 anywhere and heading levels do not follow visual hierarchy; the settings toggle buttons are not associated with their adjacent labels; the calendar conveys a data relationship with no programmatic structure.
1.3.2 Meaningful SequenceA Supports Reading order matches visual order on all four tabs and in the practice dialog. No CSS reordering is used.
1.3.3 Sensory CharacteristicsA Supports Instructions name the finger and the key (“Your right index — up from J to U”) rather than relying on shape, position or colour.
1.4.1 Use of ColorA Partially Level pips, earned badges and the calendar now carry non-colour cues (a tick glyph, graded border weights) as well as colour. Exception: in the line you are typing, “typed correctly”, “corrected” and “typed wrong” are distinguished by hue alone (a mistyped space is the one case that also gets a glyph).
1.4.2 Audio ControlA Supports No audio plays automatically. Keystroke sounds are user-initiated, under half a second, and there is an on/off control in Settings.
2.1.1 KeyboardA Supports All functionality is keyboard operable; verified by operating every screen and dialog from the keyboard alone. Needs AT confirmation for behaviour inside a screen reader’s virtual cursor mode.
2.1.2 No Keyboard TrapA Supports Dialogs cycle Tab within themselves and are left with Escape; the practice screen has no focusable elements, so Tab is not captured, and Escape opens the pause screen from which Quit is reachable. Verified.
2.1.4 Character Key ShortcutsA Supports Printable keys are content input inside a focused modal, not global shortcuts, which meets the “active only on focus” exception. There are no single-character shortcuts outside a practice run. Needs AT confirmation that a screen reader’s own single-key commands are not lost.
2.2.1 Timing AdjustableA Does not Practice runs are fixed at 90 s (warm up), 150 s (the three main practices) and 180 s (your own text), with no way to turn off, adjust or extend. Escape pauses the clock, which is not sufficient. The two 60-second speed tests are claimed under the Essential exception, as explained above; the practice runs are not.
2.2.2 Pause, Stop, HideA Does not An SVG animation on the reach arrow is set to repeatCount="indefinite" and runs for as long as a non-home-row key is displayed, with no pause, stop or hide mechanism. All other animation in the app is single-shot and under half a second.
2.3.1 Three Flashes or BelowA Supports Nothing flashes. Every keyframe animation was enumerated: all are single-shot, the longest a 9-second fade.
2.4.1 Bypass BlocksA Supports A main landmark is present (technique ARIA11) and only four tab buttons precede it. A skip link would be an improvement but is not required where a landmark exists.
2.4.2 Page TitledA Supports Descriptive title. As a single-page app the title does not change per tab; the tab panel is labelled by its tab instead.
2.4.3 Focus OrderA Supports Verified: opening a dialog moves focus into it and makes the rest of the page inert; closing restores focus to the element that opened it, or to the main panel if a redraw has removed it.
2.4.4 Link Purpose (In Context)A Supports The app contains almost no links; those present have descriptive text.
2.5.1 Pointer GesturesA Supports Single-pointer activation only; no path-based or multipoint gestures.
2.5.2 Pointer CancellationA Supports Actions fire on the up-event via standard click handling.
2.5.3 Label in NameA Supports Accessible names are taken from visible text throughout. (The settings toggles are named by their visible text too — the problem there is that the text does not describe the control’s purpose, which is reported under 4.1.2 and 2.4.6.)
2.5.4 Motion ActuationA N/A No device-motion or user-motion operation.
3.1.1 Language of PageA Supports <html lang="en">.
3.2.1 On FocusA Supports Focus alone never changes context.
3.2.2 On InputA Supports No field changes context on entry; every state change follows an explicit activation.
3.3.1 Error IdentificationA Supports Sign-in and recovery errors are written into a live alert region and describe the problem in words.
3.3.2 Labels or InstructionsA Partially Every field has a programmatic label and helpful surrounding instructions. Exceptions: the account username and password fields use placeholder text as their only visible label, which disappears on typing; the four settings toggles have no programmatic label at all.
4.1.1 ParsingA Supports No duplicate IDs and no malformed nesting found across all four tabs and every dialog. (This criterion is obsolete in WCAG 2.2; assessed here for completeness against 2.1.)
4.1.2 Name, Role, ValueA Partially Every interactive element has an accessible name; tabs expose selection state, dialogs expose modality, selectable rows expose pressed state. Exception: the four settings toggles expose only their current value as their name (“MIDNIGHT”, “SHOWN”, “ON”, “MAC”) with no indication of what they control and no pressed state.
Level AA — 20 criteria
CriterionLevel ConformanceRemarks
1.2.4 Captions (Live)AA N/ANo live media.
1.2.5 Audio DescriptionAA N/ANo media.
1.3.4 OrientationAA Supports No orientation is locked.
1.3.5 Identify Input PurposeAA Supports Every relevant field carries an appropriate autocomplete token (username, current-password, new-password, given-name).
1.4.3 Contrast (Minimum)AA Partially Body and interface text clear AA comfortably in both themes. Exceptions: the character you owe when you have just mistyped it, in the dark theme (2.85:1, needs 3:1 at that size); the eight finger names on the Progress tab (2.11–4.07:1 dark, and two of nine below 4.5:1 light); and the letters on the on-screen keyboard once the hint fade drops below full strength (4.27:1 at the second difficulty, 1.74:1 at the third, in the light theme). Full figures below.
1.4.4 Resize TextAA Supports Verified at the 200% equivalent: no loss of content or function, no horizontal scrolling.
1.4.5 Images of TextAA Supports The app contains no images at all; everything is live text or drawn vector graphics.
1.4.10 ReflowAA Does not The four main tabs reflow correctly at 320 CSS pixels, and the practice screen substitutes an equivalent message below 700. The two guided lessons fail: the keyboard graphic is fixed at 474 pixels wide, so at 320 pixels its outer key columns fall outside the viewport with no horizontal scrolling available to reach them.
1.4.11 Non-text ContrastAA Does not The focus indicator, filled progress bars, selected rows, the lit key and the lit finger all pass. The general border colour does not (1.75–2.61:1 across both themes), nor do the empty tracks of progress bars and the two faintest calendar levels (1.12–2.87:1), nor the keycap borders (2.22:1), nor the hand silhouettes against the dark theme (1.65:1).
1.4.12 Text SpacingAA Supports Verified with the standard override applied: no clipping, no overlap, no horizontal scrolling.
1.4.13 Content on Hover or FocusAA Supports The only hover content is native browser tooltips, which are provided by the user agent and exempt. (Their information is also needed in text — that is reported under 1.1.1, not here.)
2.4.5 Multiple WaysAA N/A The app is a single page; the tab bar is the only navigation and this criterion does not apply to a single-page process.
2.4.6 Headings and LabelsAA Partially Headings and labels are descriptive where they exist. Exceptions: there is no top-level heading on the page, and heading levels are used for visual weight rather than structure; the four settings toggles are labelled with their value rather than their purpose.
2.4.7 Focus VisibleAA Supports A 3 pixel outline measuring 4.49:1 in the light theme and 13.88:1 in the dark. Text fields and links fall back to the browser’s own indicator.
3.1.2 Language of PartsAA Supports All content is in English.
3.2.3 Consistent NavigationAA Supports The tab bar is identical in order and position on every screen.
3.2.4 Consistent IdentificationAA Supports Repeated components are named and behave identically throughout.
3.3.3 Error SuggestionAA Supports Errors say what to do next, and a failed practice run states the exact shortfall in speed or accuracy.
3.3.4 Error PreventionAA Partially Deleting a player or an account is confirmed, states what will be lost, and offers to export the data first. Exception: both account paths collect the password through a browser prompt, which displays it in clear text and cannot be labelled or masked.
4.1.3 Status MessagesAA Partially Status regions are correctly placed outside the part of the page that becomes inert behind a dialog — verified, and a real defect until recently. Run start, word completion, wrong keys and results are all announced. Exception: the finger-hint region is a polite live region rewritten on every keystroke (measured at 12 updates a second), which will queue up far behind the user. Needs AT confirmation for the practical severity.

Appendix

Contrast, measured

Ratios were computed with the sRGB relative-luminance formula from the specification — each channel linearised as c ≤ 0.03928 ? c/12.92 : ((c+0.055)/1.055)^2.4 — rather than read from a tool, because several of these sit within a fifth of a point of the threshold. Translucent layers were composited first. 198 pairs were checked across both themes, with further passes for state boundaries (a lit key against the keys beside it, rather than against the page) and for the way the hints fade out as you improve. A representative selection follows.

Passing, with the numbers

Body text on the page
14.17 light · 16.32 dark
Secondary and note text on cards
6.40–6.73 light · 6.02–8.00 dark
Small notes on inset panels
5.38 light · 5.38 dark
The finger instruction line under the keyboard
6.73 light · 10.76 dark
The main call-to-action button
7.82 light · 10.77 dark
The character you are about to type
7.82 light · 10.77 dark
Keyboard letters at full hint strength
7.33 both themes
Focus indicator against the page
4.49 light · 13.88 dark
The lit key, against the unlit keys beside it
5.11–9.44 across all nine fingers
The lit finger, against the unlit fingers beside it
4.55–8.40 across all nine fingers

Failing, with the numbers

Mistyped current character, dark theme (needs 3.0)
2.85
Finger names on Progress, dark theme (needs 4.5)
2.11–4.07
Finger names on Progress, light theme — two of nine (needs 4.5)
4.05, 4.09
Keyboard letters at the second difficulty, light theme (needs 4.5)
4.27
Keyboard letters at the third difficulty, light theme (needs 4.5)
1.74
General border colour on cards, buttons, inputs, tables (needs 3.0)
1.75–1.93 light · 2.13–2.61 dark
Empty progress-bar track, lesson dots, empty calendar cells (needs 3.0)
1.19 light · 1.12 dark
Calendar shading, faintest two levels (needs 3.0)
1.68 and 2.87
Keycap borders against the board (needs 3.0)
2.22
Hand silhouettes against the dark theme background (needs 3.0)
1.65

One correction worth recording, since it is an easy mistake to make in the other direction: the lit finger and lit key look like failures when compared with the page background, but the correct comparison under 1.4.11 is with the adjacent unlit fingers and keys, and against those they pass comfortably. We are not going to claim a failure we do not have any more than we will hide one we do.

A note on the marketing page

The page at nolooktyping.com/ was not audited to the same depth, but two contrast problems were found in passing and are on the list: its muted text colour measures 3.60–3.94:1 where 4.5:1 is required, and its rule colour measures 1.38–1.51:1 where 3:1 is required. This report page uses darkened versions of both.

The honest gap

What we still do not know

These questions cannot be answered by reading code or computing ratios. They need a person with a screen reader, and until that has happened we are not going to characterise the app as usable with assistive technology.

  1. Whether the practice screen works at all with a screen reader. The whole activity is a modal that intercepts every printable key. Does the virtual cursor reach the line of text? Does the screen reader switch modes? Do its own single-key commands still function?
  2. Whether the current-character marker is announced. The markup is right; whether it is spoken usefully, and whether a user can find their place without arrowing through the whole line, is a different question.
  3. How bad the repeated finger hint really is — merely irritating, or enough to make the screen unusable.
  4. Whether the first announcement is heard. A live region inserted at the same moment as its content often is not.
  5. Whether hiding the on-screen keyboard from assistive technology is the right call, or whether a summarised alternative would serve better than prose alone.
  6. Windows High Contrast and forced-colors mode. Not tested; the app has no rules for it, and the colour-coded keyboard is the obvious risk.
  7. Voice control — buttons named only by their current value are awkward to address by voice.
  8. Mobile screen readers on the Progress and Coach tabs, which are the two screens we specifically suggest opening on a phone.

We would rather publish this list than a claim we cannot support. If your institution runs its own assistive-technology testing, we would genuinely like the results, whatever they say.

Report a problem

Tell us and we will fix it

Email [ACCESSIBILITY CONTACT — set this up before publishing; a reviewer will test it]. Useful things to include, though none of them are required — a description of the problem on its own is welcome:

  • Which screen you were on, and what you were trying to do.
  • Your browser and operating system.
  • Any assistive technology you were using, and its version.

What to expect. We aim to acknowledge within five working days and to come back with either a fix or a date. Anything that stops somebody using the app goes to the front of the queue.

If we cannot fix it quickly we will say so and describe the workaround, rather than leave the report open. Problems reported this way are added to our test suite, so the same fault cannot come back unnoticed.

Some things worth knowing for a procurement file

  • The app runs entirely in the browser. There is no tracking, no advertising and no third-party analytics.
  • Without an account, nothing about a learner leaves their computer. With an optional account, the only personal data held is a username the learner chooses and a password — no email address, no real name, no age.
  • It works offline once loaded, which matters on a shared or intermittently connected device.
  • This report is a self-assessment. It has not been produced or reviewed by an independent accessibility auditor.