What we are buying, and why
Can someone verify they own a domain, run a security scan, understand what it found, decide what to do about it, and get a report out?
We are commissioning this because our own testing cannot answer that question, and because we would rather find out from you than from a client who signed up and gave up.
There is no compliance deadline driving this. If you find that a workflow is technically conformant and still unusable, say so — that finding is worth more to us than a criterion number.
Already covered — please do not bill for this
An automated pass runs on every commit: axe-core 4.10.2 in Chromium, across 60 client-facing pages, in both the light and dark palettes, both languages (English and Lithuanian), and at both 1280 px and 320 px. It is green, and it is enforced, so regressions in this class cannot reach production. The narrow pass is worth knowing about before you plan your time: whether some rules fire at all depends on the layout — a region is only scrollable when it is too narrow for its content — so running axe at the desktop width alone had been hiding real failures on the marketing pages.
Settled
- Skip link and a
<main>landmark on every page; nav is<nav aria-label="Main">. - No page reloads itself. Live scan pages poll a JSON endpoint and update text in place inside
aria-live="polite"regions, with a Pause updates control. When a scan finishes, the page says so in that region and offers a Load the results control — it does not navigate on its own. - Form errors carry
role="alert"; confirmations carryrole="status". - Tool-status glyphs are
aria-hiddenwith real text beside them. - The decorative pulsing dot is disabled under
prefers-reduced-motion. - All 21 semantic colour pairs meet 4.5:1 in both palettes, verified arithmetically as well as by axe.
That last one was not a formality. It caught two real defects: table headers at 2.77:1 against a 4.5:1 requirement, and prose links distinguished only by colour. Both are fixed.
So contrast, missing alternative text, unlabelled inputs, landmark structure, ARIA validity and auto-updating content are covered. If you find one of these it is a genuine escape and we want it — but please do not plan hours against them.
Scope
In scope
The client-facing product, signed out and signed in: sign-in and registration, the dashboard, targets and target detail, findings, scan detail both while running and once finished, compare, billing, team, activity, integrations, all of settings, the public status page, a shared report link, and the generated PDF report.
Out of scope, unless budget allows
- Admin pages — staff-only, no external users.
- Stripe's hosted checkout — a third party's page. Do tell us if the handoff to it is broken, because that part is ours.
- The API and its docs — consumed by programs, not people.
Environment
We will provide, before the engagement starts:
- A staging account, pre-loaded with verified targets, a completed scan with findings at every severity, a generated report, and a scan you can start yourself to exercise the live-progress path. You will not need to run any code or wait on real infrastructure.
- A second account for the team-invite flow, and credentials for a member as well as an owner — the roles see different pages.
- A staging URL, throwaway TOTP enrolment, and a scratch payment method if you want to walk the billing handoff.
Tell us what else you need. If something is awkward to reach, that is our problem to solve, not yours to work around.
We care most about NVDA + Firefox and VoiceOver + Safari, in that order, because that is the bulk of real usage. JAWS if it is in your kit. Please also spend time in keyboard-only, no screen reader — sighted keyboard users hit different problems, mostly focus visibility and focus order.
The workflows
Each of these is a task a client actually performs.
For each, we care about the same four questions:
- Can it be completed at all?
- Is it obvious what state you are in, and what just happened?
- How much longer does it take than it would with a mouse?
- Is anything actively misleading — announced wrongly, or silently not announced?
-
5.1
Sign up, sign in, and turn on two-factor
Register, sign out, sign in. Then enrol in two-factor authentication.
This one has a structural problem we already know about: enrolment is presented as a QR code, which is meaningless to a screen-reader user. There is a text secret beside it — "Can't scan it? Enter this key by hand".
Tell us whether that alternative is discoverable and usable in practice: whether it is announced, whether the key is readable character by character, and whether the "can't scan it" framing hides the only path some people have. Then the recovery codes screen — are they readable, and can they be saved? Also the 2FA challenge at sign-in, and a failed sign-in: the error must be announced, not merely drawn.
-
5.2
Add a domain and prove you own it
Add a target, then verify it by publishing a DNS TXT record. The page gives you a token to copy into your DNS provider.
Copying an exact opaque string is a known screen-reader pain point. Is the token announced unambiguously? Is it clear where it ends and the surrounding prose begins? Is there a copy control, and does it confirm it worked?
Then the bulk import flow — a textarea of many domains at once, and the confirmation screen that follows.
-
5.3
Start a scan and wait for it
This is the workflow we are least confident about, and the most important. A scan takes minutes to hours. The page updates itself in place via
aria-live="polite", and there is a Pause updates button.- There are several live regions on a scan page at once — a service message, a "this page updates automatically" note, a progress line, and a report-generation notice. We suspect they interact badly: announcements queueing, interrupting each other, or being dropped. Please characterise what actually happens.
- Is the pause control discoverable before the updates become a problem, or only after?
- When the scan finishes the page reloads once. Where does focus land? Is it announced?
- Is the progress information useful, or just noise repeated every few seconds?
-
5.4
Read what the scan found
Tables of findings with severity badges, expandable evidence and remediation panels, filters, and grouping.
- Are the tables navigable by table commands — do headers associate with cells correctly?
- Is severity conveyed without colour, and announced in a sensible order relative to the finding's name?
- Do the disclosure widgets announce their state, and does content appear where you expect it?
- With 50+ findings, is there a way to move through them that is not linear?
-
5.5
Act on a finding
Mute a finding, override its severity, mark one as being worked on. Each is a form that changes a row in place.
Does the row's new state get announced? After submitting, where is focus? Is it clear the change was saved — and, for the severity override, that the client's value now takes precedence over ours?
-
5.6
Get a report out
Generate a report, download the CSV and JSON exports, create a shared report link, and open that link signed out.
-
5.7
The PDF report
Separate problem, separate rules. Our PDFs are produced by WeasyPrint from HTML.
Is the PDF tagged at all? Is reading order correct? Are tables tables? Do headings carry structure? If it is untagged, tell us plainly — we would rather offer an accessible HTML report and say the PDF is not accessible than imply something we have not earned.
-
5.8
Settings, sessions, and the team
Settings and its sub-pages, especially sessions: ending an individual session is destructive and the rows are near-identical, so identifying which one you are ending matters. Plus SSO configuration, notification preferences, and the team invite and accept flow.
-
5.9
Export your data, then close the account
Unrecoverable
Account closure hard-deletes everything immediately, with no grace period and nothing kept for recovery. This is a deliberate product decision, which makes the confirmation flow safety-critical.
Confirmation requires typing the account email address. Please check hard: is the irreversibility announced before the field, not only shown near it? Could someone reach and complete this without understanding what happened?
This is the one place in the product where a screen-reader failure is unrecoverable, so we would rather over-invest here.
-
5.10
The status page, and the outage page
The public status page, signed out. Also the static fallback that our web server returns when the application itself is down — it is what a client meets during an outage, so it is worth a few minutes even though it is one short page.
Criteria we expect automation to have missed
Offered as a starting point, not a checklist to fill in. Anything you find outside it is welcome.
| Criterion | Why a machine cannot judge it here |
|---|---|
| 1.3.1 | Info and relationships — whether findings tables and grouped results convey their real structure |
| 1.3.2 | Meaningful sequence — whether a finding's severity, name and evidence arrive in a usable order |
| 2.4.3 | Focus order — especially after in-place updates and form submissions |
| 2.4.6 | Headings and labels — whether labels describe the thing or merely exist |
| 2.4.7 | Focus visible — custom controls and the disclosure widgets |
| 3.2.2 | On input — whether anything changes context unexpectedly |
| 3.3.2 | Labels or instructions — the DNS token and the deletion confirmation in particular |
| 3.3.3 | Error suggestion — whether errors say how to fix the problem |
| 4.1.3 | Status messages — the multiple live regions on the scan page (see 5.3) |
Where we would look first
Our own suspicions, so you can confirm or dismiss them early rather than discovering them on the last day.
Do not let this list bound the audit. It is what we can guess, which is exactly the thing that has already proven insufficient.
- The scan page's live regions (5.3). Partly resolved since this was written. There were five, plus one per finding — a fifty-finding scan carried fifty polite regions, all announced on load. The per-finding ones and two wrapping text that never changes are gone; three remain and each genuinely changes. Whether three still compete badly is the part we cannot answer.
- Two-factor enrolment via QR code (5.1) — a graphic as the primary path.
- The QR code is now
aria-hidden. We asked you to decide this and then decided it: the code encodes exactly the secret printed beside it, so announcing it offers a graphic and no information. The wording changed too — the key was framed as "Can't scan it?", a consolation for having failed at something, which is the wrong thing to say to somebody who was never going to scan a picture. Both are now offered plainly. Tell us if that was the wrong call. - The DNS verification token (5.2) — an opaque string that must be copied exactly. Changed since this was written. It, the HTTPS-file token, the 2FA key and the cloud External ID were inline
<code>inside a sentence; they are now the focusable block the rest of the product already used for the API token, the webhook secret, the recovery codes and a share URL. Whether that is enough for a string somebody has to reproduce exactly is still the question we want answered. - The risk-score sparkline. It is
role="img"with a label naming what it is and the number of scans, plus adjacent text giving direction and delta. It does not expose the underlying numbers. Is the adjacent text enough, or does it need a table alternative? - The account-deletion confirmation (5.9).
- Focus after in-place updates, throughout. One case changed since this was written: a finished scan used to reload the page under whoever was reading it, and now announces itself in the existing polite region and offers a control that takes focus. The general question stands.
- PDF tagging (5.7). Resolved since this was written — please do not bill for it. The suspicion was right: the report carried a
/Titleand nothing else, so a screen reader got one flat run of text. It is now generated as PDF/UA-1 with a declared language, a structure tree and real headings. Confirm it if it is cheap; do not plan hours against it.
What we would like back
-
A findings report
Each item with: what happens and where; how to reproduce it, including the assistive technology and browser; the WCAG criterion where one applies — and say so plainly when something is a real usability failure that maps to no criterion, rather than forcing a fit; severity in terms of whether it blocks the task, not just conformance level; and a suggested fix where you have one. We write the code.
-
A short summary we can act on in priority order
If we can only fix five things this month, we want to know which five.
-
A VPAT or ACR, if you produce them
Not needed now. A client's procurement will eventually ask, and doing it while the product is fresh in your mind is cheaper than doing it later.
-
What you did not get to
A gap we know about is manageable. One we do not know about is how we got here.
Shape of the engagement
Roughly two to four days: one to walk the workflows across two assistive-technology and browser combinations, one to write it up, and the remainder for the PDF and a re-check after we fix things.
We would rather have depth on 5.3, 5.4 and 5.9 than thin coverage of everything. If the budget forces a choice, cut breadth first and tell us what you cut.
We would like a re-check after we act on the report. A fix that was never verified by the person who found the problem is a fix we merely believe in.
What happens after
Findings become planned work and get fixed with tests, the same as everything else here. Where a finding can be expressed as an automated check, it joins the suite so it cannot come back.
Where it cannot — and most of the interesting ones cannot — we record it with your reasoning intact, so the next person to touch that code understands why it is the way it is rather than tidying it back to broken.