{
  "componentId": "progress",
  "componentName": "Progress",
  "lastReviewed": "2026-05-04",
  "keyboardWalk": [
    {
      "keys": "Tab",
      "expected": "Progress is non-interactive — the root has no `tabindex`, Tab skips past it. The progress shape stays out of the tab order regardless of variant or state. Adjacent interactive elements (cancel buttons, retry actions) are siblings of progress, not descendants, and carry their own tab stops."
    }
  ],
  "announcements": [
    {
      "trigger": "SR encounters a determinate progress in reading order",
      "expected": "SR announces the accessible name, role, and value — \"Upload progress, progressbar, 43 percent\". The value comes from `aria-valuenow`; the percentage formatting is SR-implementation-specific (some announce raw number, some announce \"percent\")."
    },
    {
      "trigger": "SR encounters an indeterminate progress",
      "expected": "SR announces the accessible name, role, and busy state — \"Loading, progressbar, busy\" or \"Loading, progressbar, indeterminate\". The absence of `aria-valuenow` is the canonical signal. Announcement varies by SR / browser combination."
    },
    {
      "trigger": "Determinate value advances",
      "expected": "Some SR / browser combinations announce on every `aria-valuenow` change (\"44 percent, 45 percent, ...\"), others announce only on focus or only at coarse milestones. The canonical anatomy supports both via the announcement slot — milestone-only opt-in via `aria-live=\"polite\"` text updates at canonical thresholds (25 / 50 / 75 / 100)."
    },
    {
      "trigger": "Progress completes (value reaches max)",
      "expected": "SR announces the final value or, when `aria-valuetext=\"Complete\"` is set, the terminal state (\"Upload progress, progressbar, complete\"). On transition out (component unmounts or replaces with Toast), the progressbar announcement disappears and the post-completion surface takes over."
    }
  ],
  "axeRules": [
    "aria-allowed-attr",
    "aria-required-attr",
    "aria-valid-attr-value",
    "aria-progressbar-name",
    "color-contrast"
  ],
  "_about": "Per-component a11y-acceptance data shaped for direct ingestion into Playwright + @axe-core/playwright or Jest + jest-axe.\n\nSuggested wiring:\n- axeRules → pass to AxeBuilder.options({ runOnly: { type: \"rule\", values: axeRules } }) so the run targets only the rules the canon has pinned for this component (other rules can run in your global pass).\n- keyboardWalk → iterate the entries; each `keys` is a human-readable sequence (e.g. \"Tab → Tab → Esc\"). Translate to page.keyboard.press calls and assert `expected` against the result (focused element, aria-state, visible text, etc.).\n- announcements → assert text content of any aria-live region or capture the accessibility tree at the trigger moment and match against `expected`.\n\nEmpty sub-arrays mean the canon does not yet pin behaviour for that axis on this component, not that none is required."
}