Skip to content
Back to skills

Ci Cd

ASecurity

Load this skill when configuring or reviewing CI/CD pipelines, GitHub Actions workflows, or automated testing setups. Ensures accessibility regressions are caught before code reaches production by enforcing quality gates, structured reporting, and a zero-debt strategy across all pages and user preferences.

  • 47 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 26, 2026
devopsjavascripttypescriptpythonrustgojavarubybashnodetesting

Works with

  • cli

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

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

Scanned May 27, 2026

npx -y skills add mgifford/accessibility-skills --skill ci-cd --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ci Cd?

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

Security grade badge for Ci Cd
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mgifford-ci-cd/badge)](https://www.skillsdirectory.com/skills/mgifford-ci-cd)

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: ci-cd
description: >
  Load this skill when configuring or reviewing CI/CD pipelines, GitHub Actions
  workflows, or automated testing setups. Ensures accessibility regressions are
  caught before code reaches production by enforcing quality gates, structured
  reporting, and a zero-debt strategy across all pages and user preferences.
---

# CI/CD Accessibility Skill

> **Canonical source**: `examples/CI_CD_ACCESSIBILITY_BEST_PRACTICES.md` in `mgifford/ACCESSIBILITY.md`
> This skill is derived from that file. When in doubt, the example is authoritative.

Apply these rules when adding, reviewing, or maintaining CI/CD accessibility checks.

---

## Core Mandate

Every CI/CD pipeline must prevent accessibility regressions from reaching production.
Automated checks are the baseline, not the ceiling — combine rule-based scanning with
accessibility tree testing and, where practical, virtual screen reader testing.

**Zero-Debt strategy:** target 100 % Lighthouse Accessibility and Performance scores
on all pages across all devices and user preferences.

---

## Severity Scale (this skill)

| Level | Meaning |
| --- | --- |
| **Critical** | Blocks task completion entirely for one or more disability groups |
| **Serious** | Significantly impairs access; workaround unreasonable to expect |
| **Moderate** | Creates friction; workaround exists and is not too burdensome |
| **Minor** | Best-practice gap; marginal impact on access |

---

## Critical: Lighthouse CI Quality Gate

Enforce a strict score threshold. A drop to 99 % accessibility or performance **must
fail the build**.

**`.lighthouserc.js` (strict gate — use once baseline is clean):**
```javascript
module.exports = {
  ci: {
    collect: {
      staticDistDir: './_site',
      numberOfRuns: 1,
      settings: { emulatedFormFactor: 'mobile' },
    },
    assert: {
      assertions: {
        'categories:accessibility': ['error', { minScore: 1 }],
        'categories:performance':   ['error', { minScore: 1 }],
      },
    },
  },
};
```

**`.lighthouserc.json` (warn-first — use while resolving existing issues):**
```json
{
  "ci": {
    "collect": { "staticDistDir": "./_site", "numberOfRuns": 1 },
    "assert": {
      "assertions": {
        "categories:accessibility": ["warn", { "minScore": 0.9 }]
      }
    },
    "upload": { "target": "filesystem", "outputDir": ".lighthouseci" }
  }
}
```

> Start with `"warn"` + `minScore: 0.9`, then tighten to `"error"` + `minScore: 1`
> once the existing baseline is clean.

---

## Critical: axe-core on Every PR

Run axe-core via Playwright on every pull request to catch WCAG violations in
dynamic content (menus, modals, theme variants).

```typescript
// tests/a11y.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

const themes = ['light', 'dark'];

for (const theme of themes) {
  test(`A11y: Desktop & Mobile in ${theme} mode`, async ({ page }) => {
    await page.emulateMedia({ colorScheme: theme as 'light' | 'dark' });
    await page.goto('/');

    const menuBtn = page.locator('#main-menu-toggle');
    if (await menuBtn.isVisible()) await menuBtn.click();

    const results = await new AxeBuilder({ page })
      .withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
      .analyze();

    expect(results.violations).toEqual([]);
  });
}
```

**Missing axe-core checks on PRs is Critical** — dynamic violations are invisible to
Lighthouse and reach production silently.

---

## Serious: GitHub Actions Workflows

### A. Lighthouse CI on every PR and push to `main`

```yaml
# .github/workflows/lighthouse.yml
name: Lighthouse CI

on:
  pull_request:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with: { ruby-version: "3.2", bundler-cache: true }
      - run: bundle exec jekyll build
      - uses: actions/setup-node@v4
        with: { node-version: "22" }
      - run: npm install -g @lhci/cli
      - run: lhci autorun
```

### B. Scheduled accessibility scan with alert-fatigue guard

Run monthly; skip the scan when open `accessibility` issues already exist so
developers are not flooded with duplicate noise.

```yaml
# .github/workflows/accessibility-scan.yml
name: Accessibility Scan (Scheduled)

on:
  schedule:
    - cron: "0 0 1 * *"   # first day of every month
  workflow_dispatch:

permissions:
  contents: write
  issues: write
  pull-requests: write

jobs:
  accessibility-scanner:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Check for existing open accessibility issues
        id: check_issues
        env:
          GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
        run: |
          COUNT=$(gh issue list --label "accessibility" --state open --json number --jq '. | length')
          echo "count=$COUNT" >> $GITHUB_OUTPUT

      - name: Run GitHub Accessibility Scanner
        if: steps.check_issues.outputs.count == '0'
        uses: github/accessibility-scanner@v3
        with:
          urls: ${{ vars.ACCESSIBILITY_SCAN_URL || format('https://{0}.github.io/{1}/', github.repository_owner, github.event.repository.name) }}
          repository: ${{ github.repository }}
          token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
          cache_key: accessibility-scan-results
```

> Set the `ACCESSIBILITY_SCAN_URL` repository variable to override the default
> GitHub Pages URL. Multiple URLs can be provided as a newline-separated list.

### C. Full deep-crawl for AI-ready audit (manual trigger)

```yaml
name: Deep Site Audit
on: workflow_dispatch

jobs:
  crawl:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx playwright test --reporter=json > audit-report.json
      - uses: actions/upload-artifact@v4
        with:
          name: a11y-json-report
          path: audit-report.json
```

---

## Serious: Accessibility Tree Testing

Automated WCAG rule checks verify markup compliance; they cannot verify what a
screen reader **actually announces**. Add accessibility tree tests for complex
components: SVG diagrams, custom widgets, live regions, navigation landmarks.

### Playwright aria snapshots (Playwright ≥ v1.46)

Assert the exact accessible name, role, and structure that assistive technologies
consume — distinct from axe-core rule checks.

```typescript
// tests/a11y-tree.spec.ts
import { test, expect } from '@playwright/test';

test('main navigation is correctly announced', async ({ page }) => {
  await page.goto('/');
  await expect(page.locator('nav[aria-label="Main navigation"]'))
    .toMatchAriaSnapshot(`
      - navigation "Main navigation":
          - list:
              - listitem:
                  - link "Home"
              - listitem:
                  - link "About"
    `);
});

test('SVG diagram is exposed as a labelled image', async ({ page }) => {
  await page.goto('/diagrams');
  await expect(page.locator('svg[role="img"]').first())
    .toMatchAriaSnapshot(`
      - img "User Authentication Flowchart":
    `);
});
```

Generate baseline snapshots once with `--update-snapshots`; treat subsequent
diffs the same as visual regression diffs.

### Semantic role queries (any Playwright version)

```typescript
test('form controls have meaningful accessible names', async ({ page }) => {
  await page.goto('/contact');
  await expect(page.getByRole('textbox', { name: 'Email address' })).toBeVisible();
  await expect(page.getByRole('button',  { name: 'Send message'  })).toBeEnabled();
});
```

`getByRole()` fails immediately when accessible names are missing or wrong —
earlier feedback than a manual screen reader audit.

### Guidepup virtual screen reader (unit-level)

For asserting exact spoken output without a real screen reader installed:

```typescript
import { virtual } from '@guidepup/virtual-screen-reader';

it('announces the dialog title and action buttons', async () => {
  document.body.innerHTML = `
    <dialog open aria-labelledby="dlg-title">
      <h2 id="dlg-title">Confirm deletion</h2>
      <button>Delete</button>
      <button>Cancel</button>
    </dialog>
  `;
  await virtual.start({ container: document.body });
  const spoken = await virtual.spokenPhraseLog();
  expect(spoken).toContain('Confirm deletion');
  expect(spoken).toContain('Delete, button');
  await virtual.stop();
});
```

GitHub Actions setup for Guidepup:
```yaml
- uses: guidepup/setup-action@v2
- run: npx jest tests/sr.test.ts
```

---

## Moderate: Local-First Developer Workflow

Run audits locally before pushing — fastest feedback loop, keeps CI noise low.

**`package.json` scripts:**
```json
{
  "scripts": {
    "test:a11y":       "lhci autorun && npx playwright test",
    "test:a11y:local": "lhci collect --url=http://localhost:3000 && lhci assert"
  }
}
```

**Install once:**
```bash
npm install -g @lhci/cli
npm install -D @playwright/test @axe-core/playwright
```

---

## Moderate: Shift-Left Accessibility Strategy

Prevent issues from entering commits in the first place. Catch problems in order
from fastest to slowest feedback:

1. **In-editor / local lint** — framework-specific a11y lint rules run as you type.
2. **Pre-commit gate** — run checks on changed files only; block commit when checks
   fail. Use `pre-commit` (Python) or `husky` + `lint-staged` (Node). Keep total
   runtime ≤ 30–60 seconds.
3. **PR gate** — re-run in CI for trust and consistency; fail PR on blocking
   regressions; publish artifact links.
4. **Scheduled depth scans** — nightly/weekly deeper scans; auto-label findings;
   trend metrics over time in `ACCESSIBILITY.md`.

**Suggested Definition of Done addition:**
> No UI-impacting commit is accepted unless local/pre-commit accessibility checks
> pass and PR CI accessibility checks are green.

**Policy model:**
* **Fast fail locally** — contributors get immediate feedback before push.
* **Strict PR enforcement** — no merge with blocking accessibility failures.
* **Transparent metrics** — show pass rate, open defects, and remediation trend in `ACCESSIBILITY.md`.
* **Waiver discipline** — only time-bound waivers with explicit owner and expiry.

---

## Moderate: AI-Assisted Remediation Loop

Close the detect → fix gap using GitHub's accessibility scanner and the Copilot
coding agent:

1. The scheduled scan (workflow B above) uses `github/accessibility-scanner@v3`
   and creates issues labelled `accessibility`.
2. A companion remediation workflow watches for that label event and passes the
   issue to a Copilot coding agent.
3. The agent locates the offending code, applies a minimal fix, and opens a
   **draft pull request** linked to the original issue.
4. A human reviews and merges the draft PR.

Copy [`examples/AGENT_REMEDIATION_WORKFLOW.yml`](https://github.com/mgifford/ACCESSIBILITY.md/blob/main/examples/AGENT_REMEDIATION_WORKFLOW.yml)
to `.github/workflows/accessibility-remediation.yml` to enable this loop.

Covered violation types: `image-alt`, `label`, `link-name`, `heading-order`,
`color-contrast`, `aria-required-attr`.

> Requires a GitHub Copilot subscription with the coding agent feature enabled
> and **Settings → Copilot → "Allow Copilot to create and approve pull requests"**
> turned on.

---

## Tool Comparison

| Approach | Finds WCAG rule violations | Finds announcement quality issues | Works for SVG / canvas | CI-friendly |
|:---|:---:|:---:|:---:|:---:|
| axe-core | ✅ | ❌ | Limited | ✅ |
| Lighthouse | ✅ | ❌ | ❌ | ✅ |
| Playwright aria snapshots | Partial | ✅ | ✅ | ✅ |
| Guidepup virtual screen reader | ❌ | ✅ | ✅ | ✅ |
| Manual screen reader testing | Partial | ✅ | ✅ | ❌ |

No single tool catches everything — use the approaches together.

---

## Governance

* **Critical failures:** any page below 100 % Lighthouse Accessibility blocks the build.
* **Performance budget:** any page below 100 % Lighthouse Performance blocks the build.
* **Triage:** scheduled scan failures must be converted to GitHub Issues.
  If an issue remains open, subsequent scheduled scans are paused (alert-fatigue guard).
* **SLA:** triage critical failures within one business day; serious within one sprint.

---

## Definition of Done Checklist

* [ ] Lighthouse CI workflow runs on every PR and push to `main`
* [ ] axe-core (via Playwright) runs on every PR covering WCAG 2.x AA tags
* [ ] Both light and dark colour schemes tested
* [ ] Mobile emulation enabled in Playwright tests
* [ ] `.lighthouserc` score threshold set to `warn ≥ 0.9` or `error ≥ 1.0`
* [ ] Scheduled scan workflow present with alert-fatigue guard
* [ ] Scan failures auto-labelled `accessibility` and converted to issues
* [ ] Accessibility tree tests in place for SVG, custom widgets, and live regions
* [ ] Local `test:a11y` script documented in contributing guide
* [ ] `audit-report.json` artifact uploaded on manual deep-crawl run
* [ ] Pre-commit / lint-staged accessibility check present and runs on changed files
* [ ] Shift-left metrics (pass rate, open defects) tracked in `ACCESSIBILITY.md` table

---

## Key WCAG Criteria (automation coverage)

* 1.1.1 Non-text Content (A) — caught by axe-core / Lighthouse
* 1.3.1 Info and Relationships (A) — caught by axe-core
* 1.4.3 Contrast Minimum (AA) — caught by Lighthouse / axe-core
* 4.1.2 Name, Role, Value (A) — caught by axe-core + aria snapshots
* 4.1.3 Status Messages (AA) — partially caught by axe-core

> Automation covers ~30–40 % of WCAG issues. Pair with manual and assistive
> technology testing to achieve full conformance.

---

## Alternative Tools

* **[AccessLint](https://github.com/accesslint):** GitHub App for inline PR comments — useful backup for code-review-time catches.
* **[Open-Scans](https://github.com/mgifford/open-scans):** External scans using multiple engines against a live URL.
* **[Lighthouse CI server](https://github.com/GoogleChrome/lighthouse-ci):** Historical tracking and dashboards.
* **[CivicActions: Scaling Automation](https://accessibility.civicactions.com/posts/how-we-scale-inclusive-website-content-with-automated-testing-and-open-source-tools):** Enterprise-scale a11y philosophy.

---

## References

* [Full best practices guide](https://github.com/mgifford/ACCESSIBILITY.md/blob/main/examples/CI_CD_ACCESSIBILITY_BEST_PRACTICES.md)
* [Shift-left automation guide](https://github.com/mgifford/ACCESSIBILITY.md/blob/main/examples/SHIFT_LEFT_ACCESSIBILITY_AUTOMATION.md)
* [GitHub Accessibility Scanner integration](https://github.com/mgifford/ACCESSIBILITY.md/blob/main/examples/GITHUB_ACCESSIBILITY_SCANNER_INTEGRATION.md)
* [Lighthouse CI documentation](https://github.com/GoogleChrome/lighthouse-ci)
* [axe-core/playwright](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/playwright)
* [Playwright aria snapshots](https://playwright.dev/docs/aria-snapshots)
* [Guidepup virtual screen reader](https://www.guidepup.dev)
* [github/accessibility-scanner action](https://github.com/marketplace/actions/accessibility-scanner)
* [WebDriver BiDi accessibility tree spec](https://github.com/w3c/webdriver-bidi/issues/443)

Files in this skill

  • SKILL.md14.9 KB
  • SYNC.md883 B

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.