{"_id":"@asanka-npm/a11y-gate","_rev":"2-6f5e88be9475cebba5a7e20bcff3153e","name":"@asanka-npm/a11y-gate","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@asanka-npm/a11y-gate","version":"0.1.0","_id":"@asanka-npm/a11y-gate@0.1.0","maintainers":[{"name":"asanka-npm","email":"inbox2asanka@gmail.com"}],"dist":{"shasum":"9caabc1d7c72042cbd9e7cd98a1917e6651fac66","tarball":"https://registry.npmjs.org/@asanka-npm/a11y-gate/-/a11y-gate-0.1.0.tgz","fileCount":31,"integrity":"sha512-xC0SOMzNADFrbrEf+vdPtivDRzF7M9+cQ/WuTmIqcgXpYjipeNBr1a5Ah5SS0SAlwOXVWrKLZzg+CAO8rToi+Q==","signatures":[{"sig":"MEUCIQDUOBlPtq7budcSV+My6goAoa3EAHOOgjdnewcQnGLqYAIgHR+ZRdUP0XIKpOF2LQlILh85ig96TU1gJEAk3h/45nM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":110519},"pnpm":{"onlyBuiltDependencies":["esbuild"]},"gitHead":"99092ad45e693c98fe546ddd3f15039f3facfa96","scripts":{"dev":"pnpm -r --parallel dev","lint":"pnpm -r lint","build":"pnpm -r build","release":"pnpm build && changeset publish","changeset":"changeset","typecheck":"pnpm -r typecheck","version-packages":"changeset version"},"_npmUser":{"name":"asanka-npm","email":"inbox2asanka@gmail.com"},"_npmVersion":"10.9.3","description":"> WCAG 2.1 Level AA compliance for legacy React/Next.js applications — without modifying internal component states.","directories":{},"_nodeVersion":"22.20.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.4.5","@changesets/cli":"^2.27.1"},"_npmOperationalInternal":{"tmp":"tmp/a11y-gate_0.1.0_1777815399265_0.10973208024910086","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@asanka-npm/a11y-gate","version":"0.1.1","repository":{"type":"git","url":"git+https://github.com/asankagit/a11y-gate.git"},"homepage":"https://github.com/asankagit/a11y-gate#readme","bugs":{"url":"https://github.com/asankagit/a11y-gate/issues"},"scripts":{"build":"pnpm -r build","dev":"pnpm -r --parallel dev","lint":"pnpm -r lint","typecheck":"pnpm -r typecheck","changeset":"changeset","version-packages":"changeset version","release":"pnpm build && changeset publish"},"devDependencies":{"@changesets/cli":"^2.27.1","typescript":"^5.4.5"},"pnpm":{"onlyBuiltDependencies":["esbuild"]},"_id":"@asanka-npm/a11y-gate@0.1.1","gitHead":"99092ad45e693c98fe546ddd3f15039f3facfa96","description":"> WCAG 2.1 Level AA compliance for legacy React/Next.js applications — without modifying internal component states.","_nodeVersion":"22.20.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-ZCw3oh7slBqSl1IEU3aJSt4REl1Qdk2eUGUxohhVuD79TAkvcmC7JIAYAEaKxNTNthY1CvAjImlE5IUuRLXPNg==","shasum":"e7a71c57109414a0a3eaea7242efd6bb57b4b318","tarball":"https://registry.npmjs.org/@asanka-npm/a11y-gate/-/a11y-gate-0.1.1.tgz","fileCount":31,"unpackedSize":110760,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIElFRR+DKtt3dt7Qks4xvpuHjgu8WmNsjTbulYYAZpSeAiA0Wg+7KjHQ86lb3oZMDzoeuhCkUbLms6f2VXuPLdntRQ=="}]},"_npmUser":{"name":"asanka-npm","email":"inbox2asanka@gmail.com"},"directories":{},"maintainers":[{"name":"asanka-npm","email":"inbox2asanka@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/a11y-gate_0.1.1_1777815755202_0.08826669837379808"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-03T13:36:39.144Z","modified":"2026-05-03T13:42:35.450Z","0.1.0":"2026-05-03T13:36:39.441Z","0.1.1":"2026-05-03T13:42:35.341Z"},"description":"> WCAG 2.1 Level AA compliance for legacy React/Next.js applications — without modifying internal component states.","maintainers":[{"name":"asanka-npm","email":"inbox2asanka@gmail.com"}],"readme":"# A11y Gate\n\n> WCAG 2.1 Level AA compliance for legacy React/Next.js applications — without modifying internal component states.\n\nA11y Gate is a pnpm monorepo containing two packages:\n\n- **`@a11y-gate/cli`** — dev dependency. AST scanner + interactive config generator.\n- **`@a11y-gate/core`** — runtime dependency. React Provider + Shield wrapper components.\n\nFor a full breakdown of which WCAG 2.1 criteria are currently supported, partially supported, or pending, see [ACCESSIBILITY.md](./ACCESSIBILITY.md).\n\n---\n\n## Architecture\n\n```mermaid\nflowchart TD\n    subgraph devTime [Dev Time]\n        A[\"npx a11y-gate scan\"] -->|\"TS-Morph AST\"| B[\"raw-metadata.json\"]\n        B -->|\"Developer annotates intent\\n(LLM via IDE or manual)\"| C[\"proposed-manifest.json\"]\n        C -->|\"npx a11y-gate review\"| D[\"a11y-gate.config.json\"]\n    end\n\n    subgraph runtime [Runtime]\n        D -->|\"imported at app root\"| E[\"A11yGateProvider config={...}\"]\n        E --> F[\"Context + Escape listener + aria-hidden manager\"]\n        G[\"Shield component='LegacyDrawer'\"] -->|\"reads config from context\"| F\n        G -->|\"cloneElement + prop spy\"| H[\"LegacyDrawer isOpen={...} onClose={...}\"]\n    end\n```\n\n---\n\n## Monorepo Structure\n\n```\n/a11y-gate\n├── packages/\n│   ├── cli/                     # @a11y-gate/cli (devDependency)\n│   │   ├── src/\n│   │   │   ├── scan.ts          # TS-Morph scanner → raw-metadata.json\n│   │   │   └── review.ts        # Commander.js interactive review → a11y-gate.config.json\n│   │   ├── package.json\n│   │   └── tsconfig.json\n│   └── core/                    # @a11y-gate/core (runtime dependency)\n│       ├── src/\n│       │   ├── Provider.tsx     # A11yGateProvider (app root wrapper)\n│       │   ├── Shield.tsx       # Shield HOC (per-component wrapper)\n│       │   ├── types.ts         # A11yGateConfig, ComponentManifest interfaces\n│       │   └── utils/\n│       │       ├── trap.ts              # Sentinel-node focus trap\n│       │       ├── inert.ts             # aria-hidden + inert background management\n│       │       ├── tab-order.ts         # Tab-order patching (tabindex, role, aria-label)\n│       │       ├── tab-order-preview.ts # Dev-mode numbered tab-order badges\n│       │       ├── css-inject.ts        # Scoped CSS overrides (WCAG 1.4.4, 1.4.12)\n│       │       └── contrast.ts          # Per-element contrast enforcement (WCAG 1.4.3)\n│       ├── package.json\n│       └── tsconfig.json\n├── pnpm-workspace.yaml\n├── tsconfig.base.json\n├── package.json\n└── a11y-gate.config.json        # Source of truth (human-verified)\n```\n\n---\n\n## Config Schema\n\n### `raw-metadata.json` — CLI output, developer/LLM input\n\n```json\n[\n  {\n    \"componentName\": \"LegacyDrawer\",\n    \"path\": \"src/components/layout/Drawer.tsx\",\n    \"props\": [\"isOpen\", \"title\", \"onClose\", \"width\"]\n  }\n]\n```\n\n### `a11y-gate.config.json` — human-verified source of truth\n\n```json\n{\n  \"version\": \"1\",\n  \"tab_order_preview\": true,\n  \"tab_order_preview_production\": false,\n  \"tab_order_preview_scope\": \"both\",\n  \"components\": [\n    {\n      \"componentName\": \"LegacyDrawer\",\n      \"componentType\": \"side-tray\",\n      \"triggerProp\": \"isOpen\",\n      \"closeHandler\": \"onClose\",\n      \"rootSelector\": \"#root\",\n      \"tabOrder\": {\n        \"auto\": true,\n        \"patches\": [\n          {\n            \"selector\": \"[data-action='close']\",\n            \"role\": \"button\",\n            \"label\": \"Close\"\n          }\n        ]\n      }\n    }\n  ]\n}\n```\n\nValid `componentType` values: `\"modal\"`, `\"side-tray\"`, `\"tooltip\"`, `\"popover\"`.\n\n`tab_order_preview` shows numbered tab-order tooltips in development. It is disabled\nin production unless `tab_order_preview_production` is also true.\n\n`tab_order_preview_scope` controls where badges appear:\n- `\"overlay\"` (default): only inside active `Shield` overlays\n- `\"global\"`: across the whole page\n- `\"both\"`: whole page plus active overlays\n\n`tabOrder.auto` conservatively patches legacy interactive elements by adding\n`tabIndex=0` in DOM order. `tabOrder.patches` lets you explicitly target legacy\nelements that need keyboard reachability. A11y Gate does not use positive\n`tabindex` values.\n\n---\n\n## Consumer API\n\n```tsx\n// main.tsx / _app.tsx — app root\nimport config from './a11y-gate.config.json';\nimport { A11yGateProvider } from '@a11y-gate/core';\n\n<A11yGateProvider config={config}>\n  <App />\n</A11yGateProvider>\n```\n\n```tsx\n// At each legacy component call site — per-component wrapper\nimport { Shield } from '@a11y-gate/core';\n\n<Shield component=\"LegacyDrawer\">\n  <LegacyDrawer isOpen={open} onClose={close} title=\"Settings\" />\n</Shield>\n```\n\n---\n\n## Integration\n\nThe CLI can create `a11y-gate.config.json` inside a separate or legacy project. Run the commands from the target project root.\n\n### 1. Install Packages\n\nFor local `.tgz` testing:\n\n```bash\nnpm install --save <local-path>/a11y-gate/packages/core/a11y-gate-core-0.1.0.tgz\nnpm install --save-dev <local-path>/a11y-gate/packages/cli/a11y-gate-cli-0.1.0.tgz\n```\n\nAfter publishing to npm:\n\n```bash\nnpm install @a11y-gate/core\nnpm install -D @a11y-gate/cli\n```\n\n### 2. Scan The Project\n\n```bash\nnpx a11y-gate scan --src components\n```\n\nUse the directory where the legacy React components live, such as `src`, `app`, or `components`. This creates `raw-metadata.json`.\n\n### 3. Generate Config\n\n```bash\nnpx a11y-gate review\n```\n\nThis reads `raw-metadata.json`, asks which components should be managed, and writes `a11y-gate.config.json` in the target project.\n\n### 4. Add The Root Provider\n\nIn a Next.js App Router project, create a client wrapper:\n\n```tsx\n\"use client\"\n\nimport { A11yGateProvider } from \"@a11y-gate/core\"\nimport config from \"@/a11y-gate.config.json\"\nimport type { ReactNode } from \"react\"\n\nexport function A11yGate({ children }: { children: ReactNode }) {\n  return <A11yGateProvider config={config}>{children}</A11yGateProvider>\n}\n```\n\nThen wrap the app in `app/layout.tsx`:\n\n```tsx\n<A11yGate>\n  {children}\n</A11yGate>\n```\n\n### 5. Wrap Legacy Overlays\n\n```tsx\nimport { Shield } from \"@a11y-gate/core\"\n\n<Shield component=\"DowngradePopup\">\n  <DowngradePopup\n    isOpen={showDowngradePopup}\n    onClose={handleDowngradeClose}\n  />\n</Shield>\n```\n\nThe `component` value must match `componentName` in `a11y-gate.config.json`.\n\n### 6. Enable Tab-Order Preview\n\n```json\n{\n  \"version\": \"1\",\n  \"tab_order_preview\": true,\n  \"tab_order_preview_production\": false,\n  \"tab_order_preview_scope\": \"both\",\n  \"components\": []\n}\n```\n\nScopes:\n\n- `\"overlay\"`: show badges only in active `Shield` overlays\n- `\"global\"`: show badges across the whole page\n- `\"both\"`: show global page badges and overlay badges\n\n### 7. Run And Verify\n\n```bash\nnpm run dev\n```\n\nVerify that numbered tab-order badges appear in development, wrapped dialogs trap focus, Escape closes wrapped overlays, and background content receives `aria-hidden + inert` while overlays are open.\n\n---\n\n## Tooling\n\n| Concern | Tool |\n|---|---|\n| Monorepo | pnpm workspaces |\n| CLI bundler | tsup (CJS for Node) |\n| Core bundler | tsup (ESM + CJS, <5kb gzipped) |\n| AST parsing | ts-morph |\n| CLI framework | commander.js |\n| TypeScript | shared `tsconfig.base.json` |\n| Versioning | Changesets |\n\n---\n\n## Key Implementation Details\n\n- `Shield.tsx` uses `React.cloneElement` to spy on `triggerProp`; when `true`, activates focus trap and registers its `closeHandler` callback with Provider context\n- `trap.ts` injects two invisible sentinel `<span tabIndex={0}>` nodes around the child subtree; cycles focus on Tab/Shift+Tab. Uses `useId()` for ARIA IDs to prevent Next.js hydration mismatches\n- `tab-order.ts` can patch legacy interactive elements with missing tab stops by adding reversible `tabIndex=0` attributes before focus trapping runs\n- `tab-order-preview.ts` renders numbered development-only tooltips that show the effective tab order while an overlay is open\n- `inert.ts` applies `aria-hidden=\"true\"` + `inert` to the `rootSelector` element when any overlay is active; removes both on deactivation\n- `css-inject.ts` injects a scoped `<style>` tag targeting `[data-a11y-gate=\"ComponentName\"] *` with `!important` overrides for minimum font size (WCAG 1.4.4) and text spacing (WCAG 1.4.12); cleaned up on overlay close\n- `contrast.ts` traverses visible text-bearing elements at overlay-open time, resolves effective background by walking the DOM upward past transparent ancestors, and applies the minimum inline color adjustment needed to reach the WCAG 1.4.3 contrast ratio target via binary search; restores originals on close\n- `Provider.tsx` manages a stack of active overlays; global `keydown` listener maps Escape to the top-of-stack registered `closeHandler` callback\n- LLM integration is intentionally left to the developer/IDE — the CLI is LLM-agnostic\n- The library is read-only with respect to the legacy project — it never modifies source files\n\n---\n\n## Build Phases\n\n### Phase 1 — Monorepo Scaffolding\n- Init pnpm workspace, root `package.json`, `pnpm-workspace.yaml`, `tsconfig.base.json`\n\n### Phase 2 — CLI Package (`@a11y-gate/cli`)\n- `scan.ts`: walk `src/`, identify React components (functional + class), extract prop interface keys → `raw-metadata.json`\n- `review.ts`: interactive CLI to annotate raw metadata → `a11y-gate.config.json`\n\n### Phase 3 — Core Package (`@a11y-gate/core`)\n- `types.ts`: `A11yGateConfig`, `ComponentManifest` TypeScript interfaces\n- `Provider.tsx`: context, overlay stack, global Escape handler, Shield registration API\n- `Shield.tsx`: config lookup by name, prop spy via `cloneElement`, activates trap + inert on trigger\n- `utils/trap.ts`: sentinel-node focus trap with `useId()`\n- `utils/inert.ts`: background inertness management\n\n### Phase 4 — Build & Package\n- Configure tsup for both packages\n- `peerDependencies` for React in `core/package.json`\n- `exports` map, `files` field, `publishConfig` for npm publishing\n- Changesets for monorepo version management\n- Verify `core` bundle size target (<5kb gzipped)\n- README with install + usage instructions for both packages\n","readmeFilename":"README.md","homepage":"https://github.com/asankagit/a11y-gate#readme","repository":{"type":"git","url":"git+https://github.com/asankagit/a11y-gate.git"},"bugs":{"url":"https://github.com/asankagit/a11y-gate/issues"}}