Example Civic Software Accessibility Findings Report
Example Permit Portal 4.2 (build 4.2.118). Companion to the WCAG Edition Accessibility Conformance Report (ACR), the completed Voluntary Product Accessibility Template (VPAT®).
Version 1.0, issued . Testing: to .
This is a sample. The product, company, findings and code are fictional, and tool versions are placeholders. It shows how SiteAligned reports problems so a team can fix them: what's wrong, where, who it affects, how to reproduce it and how to fix it. Code examples are illustrative; test any change in your own product before release.
Summary for leadership
We tested six flows of Example Permit Portal 4.2 against the Web Content Accessibility Guidelines (WCAG) 2.2, Level A and AA. Most criteria are supported, but one critical problem blocks a required task. Of the 56 criteria in the ACR, 41 are rated Supports and 15 Partially Supports. We found 9 problems and 2 advisories:
Critical 1
High 3
Medium 3
Low 2
Advisory 2
Fix first, in this order:
F1: the inspection date picker. People who use a keyboard can't schedule an inspection at all, and every permit needs one.
F3 and F4: people using screen readers aren't told when an upload fails or when their session is about to end, so they can lose work without knowing.
F2: application status is never spoken, so people using screen readers can't tell where their permit stands.
Each of these fixes is contained to one component. After they ship, a retest will re-evaluate the affected ratings and the ACR will be reissued with a new date.
Meets 3.3.1 Error Identification (A) and 2.4.6 Headings and Labels (AA)
Severity levels
Critical: some people can't finish the task.
High: the task is hard or needs a major workaround.
Medium: a workaround exists.
Low: minor.
Advisory: meets WCAG but still causes problems; not counted in the ACR.
Finding details
The full test environment and scope are in the ACR. Each finding names the screen, the element and what it was tested with, so someone who wasn't there can reproduce it. Evidence IDs refer to recordings and screenshots available on request.
F1 Inspection date picker can't be used without a mouse
Severity: Critical
WCAG
2.1.1 Keyboard (A); 4.1.2 Name, Role, Value (A); 2.5.3 Label in Name (A)
Where
Schedule inspection screen; element #inspection-date .calendar-grid
Tested with
Keyboard only in Chrome 144 on Windows 11; NVDA 2025.3.2 with Firefox 147
Who it affects
People who use a keyboard, switch devices or voice control, and people using screen readers
Evidence
Screen recording E-01 (keyboard), E-02 (NVDA)
Steps to reproduce
Sign in and open an issued permit (status: Approved).
Choose "Schedule inspection."
With no screen reader running, press Tab from the field before the date picker.
Then turn on NVDA and move through the calendar with the down arrow key in browse mode, and press Enter on a day.
What happens
Tab moves from the previous field straight to "Back"; the calendar is never reached and arrow keys do nothing. With NVDA, the day cells are read as plain numbers with no role, full date or selected state, and pressing Enter does nothing because the click handler is on the calendar container, not on each day. The inspection date can only be set with the picker and is required, so the step can't be completed.
What should happen
Every available day can be reached and chosen with the keyboard, and screen readers announce each day as a selectable date with its full date and selected state.
Recommended fix
The simplest fix is a text field that accepts a typed date, with the format shown in the hint and checked on the server. If only some dates are available, a select menu or radio buttons listing those dates is simpler still. If the calendar must stay, rebuild it following the W3C ARIA Authoring Practices "Date Picker Dialog" example.
<label for="insp-date">Inspection date (required)</label>
<p id="insp-hint">For example, 11/14/2026</p>
<input id="insp-date" name="inspection_date" type="text"
inputmode="numeric" autocomplete="off"
aria-describedby="insp-hint" required>
W3C references: G202, F54, F59, F96; ARIA Authoring Practices: Date Picker Dialog example
F2 Application status is not announced
Severity: High
WCAG
1.1.1 Non-text Content (A)
Where
Applications list; element img.status-icon
Tested with
JAWS 2026.2512.50 with Chrome 144; NVDA 2025.3.2 with Firefox 147; VoiceOver with Safari 26.2 on macOS 26.2
Who it affects
People using screen readers
Evidence
Screen recordings E-03 (JAWS), E-04 (VoiceOver)
Steps to reproduce
Open "My applications."
With each screen reader, move through a row with the down arrow key.
What happens
JAWS and NVDA read "Permit 2041, Deck addition" and skip the status icon. VoiceOver reads "Permit 2041, Deck addition, status-approved.png, image." In no case is the status spoken as a word.
What should happen
The status is spoken as text: "Approved," "Pending" or "Rejected."
Recommended fix
Show the status as visible text next to the icon and hide the icon from assistive technology, or give the icon alt text that names the status. Visible text also helps people who don't recognize the icons.
F3 Rejected uploads are shown only by a red border
Severity: High
WCAG
3.3.1 Error Identification (A); 1.4.1 Use of Color (A); 3.3.3 Error Suggestion (AA)
Where
Upload documents step; element li.file-item.is-invalid
Tested with
NVDA 2025.3.2 with Firefox 147; visual check in Chrome 144
Who it affects
People using screen readers, people with low vision or color blindness, and anyone who misses the color change
Evidence
Screenshot E-05; screen recording E-06 (NVDA)
Steps to reproduce
On the Upload documents step, upload a file in an unsupported format, such as a .heic photo.
What happens
The file's border changes from gray to red. No text explains the problem and nothing is announced, so the person can continue without knowing the file was rejected.
What should happen
A text message on the file, such as "Not uploaded: use PDF, JPG or PNG," that is announced when it appears and is tied to the file's Remove button.
Recommended fix
Add a text error to the file row, connect it to the row's Remove button with aria-describedby, and announce it through the step's single status message area (the same one F9 uses).
<!-- status area: on the page from the start, empty -->
<div id="upload-status" role="status"></div>
<li class="file-item is-invalid">
site-plan.heic
<p id="err-3" class="file-error">Not uploaded: use PDF, JPG or PNG.</p>
<button type="button" aria-describedby="err-3">Remove
<span class="visually-hidden">site-plan.heic</span></button>
</li>
// when a file is rejected:
status.textContent = "site-plan.heic was not uploaded. Use PDF, JPG or PNG.";
W3C references: F81, G83, G85, ARIA21, ARIA22
F4 People using screen readers aren't warned before the session times out
Severity: High
WCAG
2.2.1 Timing Adjustable (A)
Where
All signed-in screens; element div.timeout-warning
Tested with
NVDA 2025.3.2 with Firefox 147; JAWS 2026.2512.50 with Chrome 144
Who it affects
People using screen readers
Evidence
Screen recording E-07 (NVDA)
Steps to reproduce
Sign in, start an application, and wait 18 minutes without activity.
What happens
The timeout warning and its Stay signed in button appear on screen, but nothing is announced and focus stays where it was. Someone using a screen reader can be signed out and lose changes made since their last save.
What should happen
The warning is announced as soon as it appears, and the Stay signed in button is easy to reach.
Recommended fix
Show the warning as an alert dialog and move focus to the Stay signed in button. A lighter alternative is to put the warning text in a role="alert" area that is on the page from the start, though people then still have to find the button.
<div role="alertdialog" aria-modal="true"
aria-labelledby="to-title" aria-describedby="to-text">
<h2 id="to-title">You'll be signed out in 2 minutes</h2>
<p id="to-text">Your changes since your last save will be lost.</p>
<button type="button" id="stay">Stay signed in</button>
</div>
// when shown: document.getElementById("stay").focus();
On the Payment step, read the fee summary with NVDA.
Zoom the browser to 400%.
What happens
The fee names sit in one container and the amounts in another, so NVDA reads every fee name first and then every amount. At 400% the summary has a fixed width and the whole page scrolls sideways. The total is read correctly.
What should happen
Each amount is read with its fee name, and the summary fits the screen width.
Recommended fix
Rebuild the summary as a two-column table with a caption and row headers, and remove the fixed width on .fee-summary.
F6 Focus is lost when the Add document dialog closes
Severity: Medium
WCAG
2.4.3 Focus Order (A)
Where
Upload documents step; element div[role="dialog"]#add-document
Tested with
Keyboard only in Chrome 144 and Firefox 147
Who it affects
People who use a keyboard or a screen reader
Evidence
Screen recording E-10 (keyboard)
Steps to reproduce
Press "Add document," choose a file, then press "Done."
What happens
Focus moves to the page itself; the next Tab goes to the "Skip to main content" link, so people have to tab back through the whole page.
What should happen
Focus returns to the Add document button.
Recommended fix
Remember which button opened the dialog and move focus back to it when the dialog closes. If the dialog is rebuilt as a native dialog element opened with showModal(), browsers return focus automatically.
let opener;
function openDialog(e) {
opener = e.currentTarget; // the Add document button
dialog.hidden = false;
dialog.querySelector("h2").focus();
}
function closeDialog() {
dialog.hidden = true;
opener.focus();
}
F7 Uploaded documents can be reordered only by dragging
Severity: Medium
WCAG
2.5.7 Dragging Movements (AA)
Where
Upload documents step; element ul.file-list
Tested with
Mouse and touch in Chrome 144 on Windows 11 and Android 16
Who it affects
People who can't drag precisely, including many people using a mouse with a tremor, a head pointer or a touch screen
Evidence
Screen recording E-11 (touch)
Steps to reproduce
Upload two files and try to change their order without dragging.
What happens
People using a mouse or touch screen can reorder documents only by dragging. People using a keyboard can use Alt+Up and Alt+Down, but that doesn't help someone using a pointer who can't drag.
What should happen
Each document has Move up and Move down buttons, or an order field.
Recommended fix
Add Move up and Move down buttons to each file. After a move, keep focus on the moved file's button, disable Move up on the first file and Move down on the last, and announce the new position in the step's status area.
<button type="button">Move up
<span class="visually-hidden">site-plan.pdf</span></button>
// after moving:
status.textContent = "site-plan.pdf moved to position 1 of 3.";
W3C references: G219, F108
F8 Checkbox borders are too faint to see
Severity: Low
WCAG
1.4.11 Non-text Contrast (AA)
Where
Contacts step; element input[type="checkbox"].form-check (custom-styled)
Tested with
Colour Contrast Analyser 3.5; Chrome 144
Who it affects
People with low vision
Evidence
Contrast measurement E-12
Steps to reproduce
Measure the checkbox border against the white background.
What happens
The custom-styled border is #bec1c8 on white, 1.8:1. The labels can be selected to toggle each checkbox and the checked state shows a dark check mark, which is why this is rated Low.
What should happen
At least 3:1, for example #868a94 (3.46:1).
Recommended fix
Darken the border of the custom checkbox, or use the browser's own checkbox styled with accent-color.
W3C references: Understanding 1.4.11: user interface components
F9 Upload confirmation is not announced
Severity: Low
WCAG
4.1.3 Status Messages (AA)
Where
Upload documents step; element span.upload-done
Tested with
NVDA 2025.3.2 with Firefox 147
Who it affects
People using screen readers
Evidence
Screen recording E-13 (NVDA)
Steps to reproduce
Upload a valid PDF file.
What happens
"File uploaded" appears next to the file but is not announced. The file does appear in the list, so people can check it, which is why this is rated Low.
What should happen
"File uploaded" is announced without moving focus.
Recommended fix
Put the message in the step's status area, which must be on the page from the start and empty; don't hide or remove the area itself.
// when an upload finishes:
status.textContent = "site-plan.pdf uploaded.";
W3C references: ARIA22
Advisories
These meet WCAG but still make tasks harder for some people. They are worth fixing and don't affect the conformance levels in the ACR.
A1 The timeout warning gives only two minutes
Severity: Advisory
WCAG
Meets 2.2.1 Timing Adjustable (A) on length; the missing announcement is F4. Related: 2.2.6 Timeouts (AAA, not in scope)
Where
All signed-in screens; element div.timeout-warning
Tested with
Observation during testing
Who it affects
People who use screen readers, switch devices or voice control, and anyone who reads slowly
Steps to reproduce
Wait 18 minutes without activity.
What happens
Two minutes meets WCAG's 20-second minimum, but it is short for people who need longer to notice the warning and reach the Stay signed in button.
What should happen
A longer warning, or drafts saved automatically so nothing is lost.
Recommended fix
Show the warning at least five minutes before timeout and save drafts automatically.
W3C references: SCR16
A2 The error summary heading doesn't say what to do
Severity: Advisory
WCAG
Meets 3.3.1 Error Identification (A) and 2.4.6 Headings and Labels (AA)
Where
Permit application form; element div.error-summary h2
Tested with
Observation during testing
Who it affects
Everyone, especially people with cognitive disabilities
Steps to reproduce
Submit the form with two required fields empty.
What happens
The summary heading reads "Oops! Something went wrong." The linked list of errors below it is correct.
What should happen
A heading that says what to do, such as "Fix 2 problems to continue."
Recommended fix
Change the heading text.
W3C references: G130
About the visually-hidden class
Several examples use a visually-hidden class for text that screen readers read but that isn't shown on screen. If your product doesn't have one, this is a widely used version: