Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,55 @@ move: the JSON report's `schemaVersion` and the baseline file's. Both are bumped
a field is removed, renamed, or changes meaning — new fields may appear without one, so
consumers must ignore what they do not recognise.

## Unreleased — 0.8.0

### Added

- **`eaa-kit` on its own is now the whole first run.** It used to print the help and exit 2.
With nothing set up (no config, no flags), it finds the site, audits it, prints the
report, writes the HTML report to `.eaa-kit/report.html`, and ends with what it found
out about the project and which command to run next. `.eaa-kit/` gets its own
`.gitignore`. A folder with no site in it is left untouched. The exit codes are
`audit`'s: a first look at a site with critical barriers does not exit 0.
- **A folder of hand-written HTML is found.** No `package.json` and HTML at the top level
means the folder is the site, and `audit` with no arguments audits it where it stands.
- **`init` reads what the built site says about itself.** `<html lang>` becomes the
default language and, where it points at one, the default country: `pl` is Poland,
`fr-BE` is Belgium, `en` and `fr-CA` suggest nothing. The canonical link becomes the
default address. These are only defaults: `init` still asks, because a site's language
does not settle which country's law applies.
- **Four more countries: Belgium, Ireland, Poland and Portugal.** `BE` in French, Dutch and
English, `IE` in English, `PL` in Polish and English, `PT` in Portuguese and English —
fourteen templates to twenty-two, each written under its own country's law rather than
translated from another's. Belgium and Portugal say that supervision is split instead of
naming one body as if it owned the subject. `pl` and `pt` are new `--lang` values.

**Given up, and said so:** the statute, the authority and the enforcement route for these
four were established from regulators' pages, government portals and law firms. The
official gazettes could not be reached from where the templates were written. So they
cite less than the older seven, with no article numbers and no fine amounts. They are
marked in `eaa-kit countries` and in the docs until somebody has checked them against
the primary text, and that check blocks the 0.8.0 tag. Belgium has no German rendering;
that is 0.9.
- **`eaa-kit countries`** lists every country a statement can be written for, with its
languages, statute and the authority its template names. `--json` prints the same list
for other tools.
- **`audit --watch`** audits again whenever the build directory changes. Unchanged pages
are reused from the cache, so a save costs about as much as the pages it touched.
`--watch` with `--url` is an error rather than a poll dressed up as a watch. A watch
exits 0 when stopped, whatever it last found. A change that lands mid-run starts another
run rather than being dropped.

### Changed

- **`init` no longer turns an unrecognised country into Austria without a word.** It asks
again, with the list, and accepts a country's English name as well as its code. After
three unrecognised answers it still writes the file, since a typo should not cost every
other answer, but it says the country is a default and not what was typed.
- What the tool knows about each country lives in one registry, `src/config/countries.ts`.
A test fails when a template exists without its registry entry, or an entry without its
template.

## 0.7.0 — 2026-09-10

### Added
Expand Down
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ Build-time WCAG 2.2 AA auditor and EU accessibility statement generator for stat
built for the freelancers and small agencies who have to comply with the European
Accessibility Act (in force since 28 June 2025) without an accessibility budget. It started
in the DACH region — the BFSG in Germany, the BaFG in Austria — and the statement now names
the statute and supervisory body of **seven countries**: Austria, Germany, Switzerland,
Spain, France, Italy and the Netherlands, each in its own language as well as English.
the statute and supervisory body of **eleven countries**: Austria, Belgium, Germany,
Switzerland, Spain, France, Ireland, Italy, the Netherlands, Poland and Portugal, each in
its own language as well as English.

0.7.0 makes a run cost what it should. A page that has not changed byte for byte is not
audited again, and a run with nothing to re-audit never loads an engine at all: twenty
Expand All @@ -24,10 +25,12 @@ through, `audit --review` reads the answers back beside what the run measured, a
Sites behind a login or a preview protection are auditable too.

```bash
npx eaa-kit # nothing to set up: finds your site, audits it, writes a report
npx eaa-kit audit # WCAG 2.2 AA report; finds your build itself
npx eaa-kit diff a.json b.json # what a change made worse, and what it fixed
npx eaa-kit init # write an eaa.config.json
npx eaa-kit statement # accessibility statement, in one of seven countries
npx eaa-kit statement # accessibility statement, in one of eleven countries
npx eaa-kit countries # which ones, in which languages, under which law
npx eaa-kit checklist # the manual review no engine can do for you
```

Expand Down Expand Up @@ -72,7 +75,7 @@ listing the barriers a real audit found.

```bash
eaa-kit statement --output src/content/a11y.md
eaa-kit statement --country FR --lang fr # or ES, IT, NL, AT, DE, CH
eaa-kit statement --country PL --lang pl # eaa-kit countries lists all eleven
```

Each country's statement is a document under its own law rather than a translation of
Expand Down Expand Up @@ -130,6 +133,14 @@ your site.
**Says what it did not measure.** A crawl that stopped at its page limit, or could not
fetch forty URLs, no longer produces a report that looks like a complete one.

**Keeps up while you work.** `--watch` audits again every time the build changes. Because
of the cache below, only the pages you touched are audited again, so the report is ready
by the time you switch windows.

```bash
eaa-kit audit ./dist --watch
```

**Audits only what changed.** A page whose markup is byte-identical to the last run's keeps
the result it already had, and a run with nothing to re-audit never loads an engine —
twenty pages in ~230 ms instead of ~2,520 ms, which is about what starting the process
Expand Down Expand Up @@ -182,7 +193,7 @@ eaa-kit audit --url https://preview.example.com --basic-auth user:password
| --- | --- |
| [Auditing a build](docs/audit.md) | The `audit` command, both engines, exit codes, and what an automated run can and cannot tell you |
| [Defaults from eaa.config](docs/audit.md#defaults-from-eaaconfig) | Writing the flags down once, and what still overrides them |
| [The statement command](docs/statement.md) | The config file, the seven countries, and filling a statement from audit results |
| [The statement command](docs/statement.md) | The config file, the eleven countries, and filling a statement from audit results |
| [Baselines](docs/baseline.md) | Adopting the tool on a site that already has violations |
| [Comparing two runs](docs/reports.md#comparing-two-runs) | The `diff` command, and what it refuses to call fixed |
| [Coverage of WCAG](docs/audit.md#how-much-of-wcag-a-run-reaches) | What an automated engine can reach at all, and what it cannot |
Expand Down
123 changes: 123 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,129 @@ here is allowed to make a run look more complete than it was.
A section for a released version is left in place with what landed marked on it, so the
record shows what was planned as well as what shipped.

## Towards 1.0

1.0 is the release where somebody who has never read this file can install the tool, answer
a few questions, and end up with an audit in CI and a statement they can publish, without
having to learn the tool first. Three releases get there:

- **0.8.0 — reach.** More of the EU single market, and the loop between editing and seeing
a result made short enough to use while working rather than after.
- **0.9.0 — the first ten minutes.** Everything a new user meets before their first useful
result: `init` that sets up CI and a baseline as well as a config, errors that say what to
type next, and a German rendering for Belgium's third language community. The countries
after this release's four — Sweden, Denmark, Finland, Czechia — go here, with the same rule.
- **1.0.0 — the promise.** The JSON report, the review record, the baseline and the config
file frozen as documented contracts under semver, with a migration note for anything that
changed on the way. No new surface: 1.0 is 0.9 with the guarantees written down.

## 0.8.0 — reach

0.7.0 made a run cost what it should. 0.8.0 spends that on two things: getting the tool to
more of the people the EAA applies to, and getting a result to them while they are still
looking at the code that caused it.

### 1. Four more countries: Belgium, Ireland, Poland, Portugal

On 0.5.0's rule: a statement is written under its country's own law, not translated from
another's, in the language that law is administered in plus English.

| | Statute | Supervision named | Languages |
| --- | --- | --- | --- |
| `BE` | Loi du 5 novembre 2023 / wet van 5 november 2023, amending the Code de droit économique | SPF Économie, Direction générale de l'Inspection économique | `fr`, `nl`, `en` |
| `IE` | European Union (Accessibility Requirements of Products and Services) Regulations 2023 (S.I. No. 636 of 2023) | CCPC for e-commerce, ComReg and the Central Bank for their sectors | `en` |
| `PL` | Ustawa z dnia 26 kwietnia 2024 r. o zapewnianiu spełniania wymagań dostępności niektórych produktów i usług przez podmioty gospodarcze (Dz.U. 2024 poz. 731) | Prezes Zarządu PFRON, who receives every report; the minister for digital affairs supervises e-commerce | `pl`, `en` |
| `PT` | Decreto-Lei n.º 82/2022, de 6 de dezembro | ANACOM for e-commerce; supervisors report to INR | `pt`, `en` |

Belgium is the case the roadmap has been waiting for. Supervision is split, and the
templates say so rather than naming one body as if it owned the subject. Belgium also has
three official languages. The German-speaking community gets its rendering in 0.9: the
federal law is published in French and Dutch, and a German document written without a
German source text would be exactly the translation this rule forbids.

**What this release could not do, written down rather than hidden:** the session that wrote
these templates could not reach the official gazettes (irishstatutebook.ie,
isap.sejm.gov.pl, dre.pt, ejustice.just.fgov.be). Every citation above is corroborated by
several independent secondary sources: regulators' own pages, law firms and government
portals, as indexed. Each one still has to be checked against the primary text before 0.8.0
is tagged. That check is a release blocker, listed under *Done means* below. The templates
also cite less than the older seven do: no article numbers and no fine amounts, because
those are the details a secondary source gets wrong.

### 2. One place a country is defined

A country today is spread across `COUNTRIES`, `STATEMENT_LOCALES`, `init`'s locale table,
the date formats, the docs table and the snapshot list. Adding four more at once would mean
changing all of them eleven times over. The facts move into one registry (name, languages,
statute, authority, default site locale) that everything else reads from. A test fails when
a template exists without its registry entry, or the other way round.

### 3. `eaa-kit countries`

What the statement can be written for, from the terminal: code, name, languages, statute
and the authority each template names. Today that information lives in a docs table, so
nobody finds out that `--country PT` exists until they have read the docs.

### 4. `audit --watch`

[Deferred from 0.7.0](#not-in-070). A run over an unchanged build costs about as much as
starting the process, so it is worth repeating on every save. `--watch` re-audits the
build directory whenever something in it changes. The page cache means only the changed
pages are audited again, and the report is printed again after each run.

The refusals:

- **Directories only.** A running site under `--url` changes without writing anything this
process can watch, so a watch over it would be a poll that looks like a watch. It is an
error, not a quiet fallback.
- **No exit code on the way.** A watch never exits 1 because a run found something. Its
job is to show the result, and CI has the one-shot run for failing a build.
- **A run is never skipped because another one is in progress.** A change that arrives
mid-run starts a new run as soon as the current one ends. A report never shows a build
the files on disk have already moved past.

### 5. `init` stops guessing

`init` turns an unrecognised country into `AT` without saying so. That is an Austrian legal
document for somebody who typed `pl`. An answer it does not recognise is now asked again,
with the list, and the prompt names each country in full.

### 6. The first run needs no setup

`eaa-kit` on its own printed the help and exited 2, and that is the first command anybody
types. Now it is the whole first run. It finds the site the way `audit` already does,
audits it, writes the HTML report to `.eaa-kit/report.html`, and then says what it found
out about the project and which command comes next, depending on what the project already
has: `init` if there is no config, `baseline` if there are findings and no baseline.

Two gaps in the detection close with it. A folder of hand-written HTML with no
`package.json` is audited where it stands. `init` now reads what the built site states
about itself, `<html lang>` and its canonical address, and offers those as defaults. It
still only offers what the site states outright: a language tag suggests a country, it
does not decide one, and `init` still asks.

The refusals: the first run writes nothing into the project outside `.eaa-kit/`, and gives
that directory a `.gitignore` of its own instead of editing the project's. A folder with no
site in it is left exactly as it was found. The first run also keeps `audit`'s exit codes.
Exiting 0 on a site with critical barriers, because this happened to be somebody's first
look, would tell them it was clean.

### Not in 0.8.0

- **Belgium in German, and the Nordic and Czech statements.** 0.9, for the reason above.
- **Watching a running site.** See the refusal above.
- **A score, Level AAA, anything model-generated, a hosted dashboard.** As before, and not
later.

### Done means

- `lint`, `typecheck`, `test`, `smoke` and the packaged-CLI run green across the CI matrix.
- **Every citation in the four new countries' templates checked against the primary
text**, and the checking recorded in the changelog: who checked it, against which
consolidated version. Until then 0.8.0 is not tagged.
- `examples/` regenerated and drift-checked.
- A changelog entry saying what was given up as well as what was added.

## 0.7.0 — the run that costs nothing, and the loop that closes

0.6.0 added the half no engine can do. 0.7.0 is about three things a user feels: how long a
Expand Down
56 changes: 56 additions & 0 deletions docs/audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,41 @@
with axe-core. This page covers the command, both engines, and how to read what it returns.

```bash
eaa-kit # first run: find the site, audit it, write a report
eaa-kit audit # works out what to audit
eaa-kit audit ./dist # or say so
```

## The first run

`eaa-kit` with no command needs no config file, no flags and no setup. It finds the site
the way `audit` does (below), audits it, and prints the console report. It also writes the
HTML report to `.eaa-kit/report.html`, a page you can open, keep, and send to whoever owns
the site. Then it says what it worked out about the project and what to run next:

```
What eaa-kit found about this project
Site dist/ (Astro), 12 pages
Language pl-PL → a statement under Poland's law
Address https://sklep.pl
Report file:///…/.eaa-kit/report.html

Next
eaa-kit init write the config for your statement, with Poland filled in
eaa-kit baseline accept today's findings; later runs fail only on new ones
eaa-kit audit --watch check again on every build while you fix things
eaa-kit checklist the 34 criteria no automated test can check
```

The language and address come from the site itself: `<html lang>` and the canonical link on
its home page. `eaa-kit init` offers them as its defaults. A language tag suggests a country
and does not decide one. `de` could be Germany, Austria or Switzerland, and the country
whose law applies depends on where you sell, so `init` still asks.

Nothing is written into the project except under `.eaa-kit/`, which also gets a `.gitignore`
so the report and the cache stay out of version control. A folder where no site is found is
left untouched. Exit codes are those of `audit`.

## With no arguments

`eaa-kit audit` on its own works out what this project needs, in three steps:
Expand All @@ -24,6 +55,9 @@ eaa-kit audit ./dist # or say so
cannot be exported. It starts `start`, `preview` or `serve`, crawls what that serves,
and stops it again afterwards.

A folder with no `package.json` and HTML files at its top level is a site written by hand,
and is audited where it stands.

Naming a directory or passing `--url` skips all of it, and `--no-build` stops it running
anything, leaving step 1 only.

Expand Down Expand Up @@ -239,6 +273,7 @@ chatter coming along.
| `--review <path>` | — | [What a person checked](review.md), for the criteria no engine reaches |
| `--review-max-age <days>` | — | Stop counting review entries older than this |
| `--config <path>` | searched for | Take defaults from this config file rather than the one found by searching |
| `--watch` | off | [Audit again](#watching-a-build) whenever the build directory changes, until Ctrl-C |

Dot directories such as build caches are skipped by default. `--include` and `--exclude`
replace the defaults rather than adding to them.
Expand Down Expand Up @@ -530,6 +565,27 @@ not be unsafe, but it would be a large directory of facts about somebody else's
baseline is a file somebody commits and then lives with for months, and it is worth the
1.2 seconds to build one from a run that looked at every page itself.

### Watching a build

```bash
eaa-kit audit ./dist --watch
```

Because unchanged pages come from the cache, a run is cheap enough to repeat on every save.
`--watch` audits once, then audits again whenever anything in the build directory changes,
and prints the report each time. Only the pages whose markup changed are audited again.
Keep your build tool's own watch running in another terminal; this watches what it writes.

- **Directories only.** `--watch` with `--url` is an error. A running site changes without
writing anything this process can see, so watching it would really mean polling it.
- **No verdict.** A watch exits 0 when you stop it, whatever the last run found. Failing a
build is what the one-shot run in CI is for.
- **No change is dropped.** A change that lands during a run starts another run as soon
as that one finishes, so the report on screen is never for a build that has already
been replaced.
- A directory that does not exist yet is waited for, and the page cache and an `--output`
inside the build are not treated as changes.

## The Issues section

The console report leads with the violations grouped by the element that causes them:
Expand Down
Loading
Loading