Skip to content
Back to skills

Accessibility Audit

ASecurity

Audit the app's UI/UX for accessibility against a maintained criteria list, and periodically refresh that list from current standards. Use when asked for an accessibility or a11y audit/review, to check WCAG conformance, or to find keyboard, screen-reader, contrast, focus or target-size problems in the UI. Drives the real app in a browser against generated data — never production data. Produces a ranked report; files issues only with your approval.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 18, 2026
developmentjavascriptrustgojavashellnodegitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 18, 2026

npx -y skills add ChulioZ/spielwirbel --skill accessibility-audit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Accessibility Audit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Accessibility Audit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/chulioz-accessibility-audit/badge)](https://www.skillsdirectory.com/skills/chulioz-accessibility-audit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: accessibility-audit
description: >-
  Audit the app's UI/UX for accessibility against a maintained criteria list, and
  periodically refresh that list from current standards. Use when asked for an
  accessibility or a11y audit/review, to check WCAG conformance, or to find
  keyboard, screen-reader, contrast, focus or target-size problems in the UI.
  Drives the real app in a browser against generated data — never production
  data. Produces a ranked report; files issues only with your approval.
---

# Accessibility audit

Two jobs: keep `criteria.md` current with what accessibility actually requires of
*this* app, then audit the running UI against it.

**Read `.claude/skills/audit/audit-loop.md` first** — it owns the loop (research
gating, the critique test, how criteria change, the report format, and the rule
that findings only become issues with the user's approval). This file owns the
domain: where to look and how to probe it without being lied to.

Pass `--research` to force a research pass; otherwise the cadence in
`criteria.md` decides.

## Research sources (phase B)

Ask what changed **since `last-researched`**, not what accessibility is:

- **W3C** — WCAG (2.2 is the current Recommendation; treat 3.0 as a draft, i.e.
  not yet a criterion), WAI-ARIA, and the APG patterns for dialog, tabs, listbox
  and disclosure — the four patterns this UI actually uses.
- **EN 301 549** and the EAA/BFSG applicability question — but only far enough to
  hand the *legal* question to `legal-audit` (see A-R05). Do not decide a legal
  duty here.
- **Browser/platform changes** that invalidate a held technique: `:focus-visible`
  behaviour, `inert`, dialog element semantics, `prefers-reduced-motion`.

Then run the critique in `audit-loop.md` §C. Settled conflicts are pre-recorded as
the `A-R*` entries in `criteria.md` (target size, `aria-modal`, among others) — if
research proposes one again, that is the ledger working, not a new finding.

## Setting up a session you can trust (phase E)

Three things will each independently ruin an audit. Do all three before probing.

### 1. Never audit against production data

A bare `npm start` — and the `production-data` launch config — uses the real
`data/` folder: the group's private rounds, members and ratings. A screenshot or
`read_page` of that is the same leak as reading the file
(`.claude/rules/no-reading-production-data.md`).

Launch the committed **`dev-temp-data`** config instead, which runs on port 3100
against a gitignored `.devdata/` folder with accounts and the admin panel on:

```
preview_start {name: "dev-temp-data"}
```

Then seed it through the API with the `test-data` skill. Don't add your own
throwaway `launch.json` entry — reverting one is what previously left the file
with a single production config, and a `preview_start` naming a config that
doesn't exist then **silently starts that lone entry** instead of failing
(`no-reading-production-data.md`). If you seed a *separate* folder by hand, start
the server yourself with `DATA_DIR` set and open the port with
`preview_start {url}`.

The dataset must cover every screen you intend to measure: an empty Pokale tab
renders an empty state and measures nothing, which reads exactly like a passing
check (`responsive-content-width.md`). Seed at least one round with ~12 games
across several tags, some archived and completed games, a finished session with
ratings, and one abandoned draw.

### 2. Clear the service worker after every CSS edit

The shell is served cache-first, so `styles.css` changes are invisible until you
unregister the SW and clear its caches — snippet in
`.claude/rules/pwa-service-worker.md` ("Verifying a shell-asset change"). An audit
that measures stale bytes reports fixed problems as live ones.

### 3. Assume the preview pane is lying, and probe with JS

The Browser pane misreports in ways that mimic real defects. All of these are
documented and none of them are app bugs:

- `window.innerWidth`/`innerHeight` can be `0` after a navigation, so every
  element measures `width: 0`. `resize_window` to a real size and re-probe.
- Screenshots go blank after any programmatic scroll; capture only right after a
  fresh `navigate`.
- `computer` scroll/input actions can time out for a whole session while the page
  stays fully responsive to `javascript_tool`.
- Lazy covers never load: the pane fires no IntersectionObserver at any
  viewport size.

So: **measure with `javascript_tool` probes** (`getComputedStyle`, element rects,
`getAttribute`, `document.activeElement`), not pixels. See
`.claude/rules/preview-pane-paint-artifacts.md` and
`.claude/rules/provider-cover-sizing.md`.

Two probing traps specific to this domain:

- **`el.focus()` does not set `:focus-visible`** in Chrome. Checking focus
  indicators requires real Tab keypresses (`computer {action:"key", text:"Tab"}`),
  not scripted focus. Scripted focus produces phantom A-007 findings.
- **A synthetic click ignores modifier keys** for an anchor's default action, so
  it navigates instead of opening a tab and tears the page down. To check A-009,
  probe the *decision* (`e.defaultPrevented` on a document-level listener that
  blocks the browser), and probe every modified click **before** any plain one —
  the full recipe is in `.claude/rules/in-app-nav-links.md` §1.

## What to walk

Every screen, at **390px and 1280px** — the dock/strip switch is at 860px and the
rail takes over at 1280px, so the three presentations are genuinely different UIs
(`responsive-hub-tabs.md`, `responsive-content-width.md`).

Derive the screens from `resolveRoute` in `public/js/router.js` and the pages
from `public/*.html` + the server-rendered routes in `lib/app.js`, not from this
list; on 2026-10-04 it was:

- **Lobby & entry:** home, new round, the first-start design chooser.
- **Round hub:** Start, Regal, Chronik, Pokale — plus the dock (<860), the strip
  (860–1279) and the rail (≥1280).
- **Round sub-screens:** game detail, member, tags, the colour-marker picker,
  Einstellungen, move games, both archives (retired, completed), the wishlist,
  recommendations, the shelf profile.
- **Account & social:** Konto, profile, Freunde, inbox, „Was ist neu", Entdecken.
- **Session flow:** setup → vote steps → finale → results, and the shared-vote
  page (`/vote/<token>`). Highest risk: it pushes
  history per step, traps nothing, and the rating faces are the main colour-state
  offenders. Exercise Back at a vote step, including the decline branch of the
  confirm (`session-flow-history.md`).
- **Sheets:** add game, link provider, support, provider image. Each one gets the
  A-004 Tab test.
- **Auth:** login, register, forgot, verify/reset landings — reachable only with
  `ACCOUNTS_ENABLED` + `SESSION_SECRET` set on your throwaway instance.
- **Standalone pages** (outside the SPA and its i18n): `login.html`,
  `kontakt.html` (feedback lands here too), `admin.html` (needs `ADMIN_PASSWORD`;
  its login also needs a passkey since #1670, which the Browser pane cannot
  provide — on the throwaway instance only, mint the cookie with
  `node -e "const a=require('./lib/admin');console.log(a.mintToken(a.signingSecret()))"`
  run under that instance's env, and set it as `document.cookie = 'aid=…'`),
  `/faq`, the guide pages, and the legal pages, which 404 until
  `IMPRESSUM_ADDRESS` + `IMPRESSUM_EMAIL` are set.

## Prefer a test over a ticket

`test/a11y-contrast.test.js` already computes contrast ratios from the real
design registry (`public/js/designs.js`), so a new design is checked automatically. Any colour finding
belongs there as an assertion, not in an issue. Structural findings that read from
`styles.css` as text can follow `test/content-width.test.js` — parse via
`test/support/css.js` and strip comments first, or the selector regex silently
matches inside a comment (`css-text-assertions-strip-comments.md`).

Whatever you add, **break the production code once on purpose** and watch the
assertion go red (`.claude/rules/break-the-code-on-purpose.md`). A CSS-text or
DOM-shape test gives no other signal that it is wired to anything real.

## Do not report these

They were each decided deliberately; re-reporting them wastes the user's review:

- The 24px-spacing-exception targets (A-R02).
- The scoped `outline: none` pairs that supply replacements (A-R01).
- `index.html`'s static `lang="en"` — `i18n.js` sets the real value at boot.
- Covers lacking `alt` — they are `background-image` decoration with adjacent
  titles (A-010).
- Anything already asserted in `test/a11y-contrast.test.js`; say it is covered and
  move on.

## Cleanup

Stop the preview server and delete the audit dataset (`.devdata/` is gitignored,
so it never shows up in a commit either way). Leave `.claude/launch.json`
untouched — the `dev-temp-data` entry is permanent and must not be removed. If
you did create scaffolding of your own, confirm `git status` is clean of it
before any commit.

Files in this skill

  • SKILL.md8.8 KB
  • criteria.md12.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.