Skip to content

holmdigital/a11y-hd

Repository files navigation

Holm Digital Accessibility Ecosystem

WCAG and EN 301 549 accessibility testing that maps every failure to national law and the right enforcement body, across 17 jurisdictions.

npm version npm downloads license types

Most scanners (axe, Pa11y, Lighthouse) tell you what is broken. This ecosystem also tells you which law you are breaking and who enforces it: it maps each WCAG 2.1/2.2 failure to EN 301 549 and the relevant national accessibility law, and routes it to the correct enforcement body, for both public sector (WAD) and private sector (EAA) sites. Open source, MIT licensed, ~11k+ downloads across the package family.

Quick start (10 seconds, no install)

# Scan any URL and get a regulation-mapped report in your terminal:
npx hd-a11y-scan https://example.com --country SE --sector public

Add a PDF report, a JUnit file for CI, or a ready-to-publish accessibility statement:

npx hd-a11y-scan https://example.com \
  --country SE --sector private \
  --pdf report.pdf --junit report.xml --statement statement.html

That is the whole loop: point it at a URL, get back violations, the regulation each one maps to, the enforcing authority, and publishable artifacts.

hd-a11y-scan terminal output: compliance score, category breakdown, legal risk, and violations mapped to WCAG and EN 301 549

What you get

  • Regulatory mapping, not just findings. Every WCAG failure is mapped to EN 301 549 and national law across 12 languages and 16 countries plus the EU (17 jurisdictions). See the full list below.
  • Sector-aware routing. Distinguishes public sector (WAD / ADA Title II) from private sector (EAA / DDA / ADA Title III / HHS Section 504) via --sector public|private, and routes to the correct enforcement body and national law. Encodes the EAA microbusiness exemption (<10 employees AND ≤2M EUR turnover, services only) on all 7 EAA private-sector entries.
  • Risk assessment based on real enforcement practice (Digg, Uutilsynet, Logius, and others).
  • CI/CD-grade reporting. JUnit XML with full metadata, success counts, and failure snippets for DevOps dashboards.
  • Publishable statements. Generates modern, localized accessibility statements from typed JSON templates.
All 17 jurisdictions covered
  • 🇸🇪 DOS-lagen (Sweden)
  • 🇫🇮 Laki digitaalisten palvelujen saavutettavuudesta (Finland)
  • 🇳🇴 Forskrift om universell utforming av IKT (Norway)
  • 🇩🇰 Lov om tilgængelighed (Denmark)
  • 🇳🇱 Digitoegankelijk (Netherlands)
  • 🇩🇪 BITV 2.0 (Germany)
  • 🇫🇷 RGAA (France)
  • 🇪🇸 UNE 139803 (Spain)
  • 🇮🇹 Legge Stanca (Italy)
  • 🇵🇹 DL 83/2018 (Portugal)
  • 🇵🇱 Ustawa o dostępności cyfrowej (Poland)
  • 🇬🇧 PSBAR (UK) / 🇮🇪 S.I. No. 358/2020 (Ireland)
  • 🇺🇸 Section 508 (federal, GSA) / ADA Title II (state+local, DOJ) / ADA Title III (private, DOJ) / HHS Section 504 (HHS-funded healthcare/research, OCR). ADA Title II deadline 2027-04-26 for 50k+ population (extended by DOJ Interim Final Rule 2026-04-20), HHS Section 504 WCAG benchmark from 2027-05-11 for 15+ employees (extended by HHS IFR 2026-09266).
  • 🇨🇦 AODA (Ontario) / ACA (Canada federal)
  • 🇦🇺 DDA 1992 (Australia, public and private sectors)

Packages

This monorepo ships three NPM packages plus a documentation wiki.

Regulatory test engine with virtual-DOM analysis for Shadow DOM and SPA support, i18n, premium V2 accessibility-statement generation, and automatic badge support.

npm install @holmdigital/engine

CLI:

npx hd-a11y-scan <url> [options]

Options:

  • --lang <code> : language (en, sv, no, fi, da, de, fr, es, nl, it, pt, pl, en-gb, en-us, en-ca, en-au)
  • --threshold <level> : severity threshold (critical, high, medium, low). Default: high
  • --ci : CI mode (exit code 1 on failures at or above threshold)
  • --json : output results as JSON
  • --pdf <path> : generate a PDF report
  • --statement <path> : generate an accessibility statement (Premium V2 HTML)
  • --format <type> : statement format (html, md). Default: html
  • --junit <path> : generate JUnit XML for CI dashboards
  • --country <code> : country for enforcement body (e.g. SE, NO, DE, IT, PT, PL, AU)
  • --sector <type> : public (WAD) or private (EAA). Default: public
  • --org <name> / --email <email> / --phone <number> : statement metadata
  • --response-time <val> / --publish-date <date> : statement metadata
  • --viewport <size> : viewport (mobile, tablet, desktop, or 1024x768)
  • --generate-tests : emit pseudo-code automation scripts for verification
  • --invalid-https-cert : allow self-signed certs ⚠️ (trusted environments only)
  • --api-key <key> / --cloud-url <url> : upload to HolmDigital Cloud Dashboard

⚠️ --invalid-https-cert should only be used in trusted environments. (Contributed by @FerdiStro)

29 accessible React components with built-in regulatory compliance, including a 12-locale AccessibilityStatement component (EN, SV, NO, FI, DA, NL, DE, FR, ES, IT, PT, PL, with NB/DK and EN-GB/US/CA/AU aliases).

npm install @holmdigital/components
import { Heading, Button, FormField, ErrorSummary, AccessibilityStatement } from '@holmdigital/components';

<Heading level={1}>Kontakta oss</Heading>

<FormField id="email" label="E-postadress" type="email" required
           helpText="Vi delar aldrig din e-post." error={errors.email} />

<Button variant="primary" isLoading={isSaving}>Skicka</Button>

<AccessibilityStatement
  country="SE"
  sector="public"
  organizationName="Stockholms kommun"
  websiteUrl="https://stockholm.se"
  complianceLevel="partial"
  lastReviewDate={new Date()}
  contactEmail="tillganglighet@stockholm.se"
  locale="sv"
/>

Gotcha: Checkbox fires both onChange (native) and onCheckedChange(checked). Select uses a custom compound pattern (SelectTrigger/SelectContent/SelectItem), no Radix dependency. Full catalog: docs/reference/components.md.

Machine-readable regulatory database: 46 WCAG convergence rules, 12 rule-locale files, national-law metadata for 16 countries plus the EU (17 jurisdictions), and fully typed exports (EnrichedReport, FailingNode, LegalContext).

npm install @holmdigital/standards
import { getEN301549Mapping, getNationalLawByFramework, getEnforcementBody } from '@holmdigital/standards';

// WCAG → EN 301 549 → Swedish legal context
getEN301549Mapping('1.4.3', 'sv');
// { wcagCriteria: "1.4.3", en301549Criteria: "9.1.4.3", dosLagenReference: "Lag 2018:1937 §7..." }

// National law by sector (WAD = public, EAA = private, DDA = Australia)
getNationalLawByFramework('EAA', 'DE');
// { fullName: "Barrierefreiheitsstärkungsgesetz", law: "BFSG", ... }

// Sector-aware enforcement lookup
getEnforcementBody('SE', 'public');   // → "Agency for Digital Government (Digg)"
getEnforcementBody('SE', 'private');  // → "Swedish Post and Telecom Authority (PTS)"

Full API (national laws, sanctions, Nordic authorities, statement tools): packages/standards/README.md.

CI/CD integration

The engine returns exit code 1 when it finds violations at or above your --threshold, so it fails builds cleanly.

GitHub Actions

name: Accessibility Scan
on: [push, pull_request]

jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Start server
        run: npm run dev &
        env:
          CI: true
      - name: Wait for server
        run: npx wait-on http://localhost:3000
      - name: Run scan
        run: npx hd-a11y-scan http://localhost:3000 --ci --lang en --sector public --junit report.xml --pdf report.pdf
        # Use --sector private for e-commerce, fintech, and other private-sector sites (EAA instead of WAD)
      - name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: a11y-reports
          path: report.*

GitLab CI

a11y-check:
  image: node:20
  script:
    - npm install
    - npm run dev &
    - npx wait-on http://localhost:3000
    - npx hd-a11y-scan http://localhost:3000 --ci --junit report.xml --pdf report.pdf
  artifacts:
    when: always
    paths:
      - report.xml
      - report.pdf
    reports:
      junit: report.xml

Azure DevOps

trigger:
  - main
pool:
  vmImage: 'ubuntu-latest'
steps:
  - script: |
      npm install
      npm run dev &
      npx wait-on http://localhost:3000
      npx hd-a11y-scan http://localhost:3000 --ci --junit report.xml
    displayName: 'Run Accessibility Scan'
  - task: PublishTestResults@2
    condition: succeededOrFailed()
    inputs:
      testResultsFormat: 'JUnit'
      testResultsFiles: 'report.xml'

Threshold behaviour: --threshold critical fails only on critical issues, --threshold high (default) fails on critical and high, --threshold low fails on everything.

Documentation

Full English documentation lives on the wiki, with in-repo reference catalogs for each package.

Local development

git clone git@github.com:holmdigital/a11y-hd.git
cd a11y-hd
npm install
npm run build   # build all packages

Architecture

a11y-hd/
├── packages/
│   ├── engine/        # Test engine: headless-browser rendering + virtual-DOM analysis, i18n, cloud support
│   ├── components/    # Accessible React components (Heading, Button, FormField, ...)
│   └── standards/     # Machine-readable regulatory database (17 jurisdictions, 12 rule-locale files)
└── package.json       # Monorepo root

Contributing

Contributions are welcome. See CONTRIBUTING.md.

License

MIT. See LICENSE. Copyright (c) 2026 Holm Digital AB.

Links

About

Accessibility testing that maps WCAG failures to EN 301 549 and national law across 17 jurisdictions: CLI + CI reporting, accessible React components, and a machine-readable regulatory database.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Contributors