{"_id":"@a11y-pulse/text-spacing-audit","_rev":"5-44e677d184cd6036db330b904a5a3a3f","name":"@a11y-pulse/text-spacing-audit","dist-tags":{"latest":"0.3.0"},"versions":{"0.0.0":{"name":"@a11y-pulse/text-spacing-audit","version":"0.0.0","keywords":["accessibility","a11y","wcag","text-spacing","puppeteer","audit"],"author":{"name":"A11y Pulse Limited"},"license":"SEE LICENSE IN LICENSE.md","_id":"@a11y-pulse/text-spacing-audit@0.0.0","maintainers":[{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"}],"homepage":"https://github.com/A11y-Pulse/audits/tree/main/packages/text-spacing-audit#readme","bugs":{"url":"https://github.com/A11y-Pulse/audits/issues"},"dist":{"shasum":"47c017554af7358cc848beed6111b94be5e11111","tarball":"https://registry.npmjs.org/@a11y-pulse/text-spacing-audit/-/text-spacing-audit-0.0.0.tgz","fileCount":13,"integrity":"sha512-RnloaMhXGlrPLw2o3zDh3MCev/ShmmrvxFN1ScUwYvwMFb4OLxQeD3FOTKi09vrArBSU8KbwlPMQb8SP4UMulA==","signatures":[{"sig":"MEQCIAGFi1p4l0iBJYXKQIOb49BH2Q21dkpX9nrtfNE4ykIRAiADAf5BGncde22bloix0BkMh8JJHj3F3h9PKiCgBtIh5A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":92237},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./puppeteer":{"types":"./dist/adaptors/puppeteer.d.ts","import":"./dist/adaptors/puppeteer.js"}},"gitHead":"42f332636b5d0603bdb5451d77c2d085d0f39023","scripts":{"lint":"biome check","test":"npm run typecheck && npm run test:unit","build":"tsup && tsc -p tsconfig.build.json","lint:fix":"biome check --fix","test:unit":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","test:integration":"vitest run --config vitest.integration.config.ts"},"_npmUser":{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"},"repository":{"url":"git+https://github.com/A11y-Pulse/audits.git","type":"git","directory":"packages/text-spacing-audit"},"_npmVersion":"11.11.0","description":"WCAG 1.4.12 Text Spacing audit that injects the SC spacing overrides and detects clipped or overlapping text.","directories":{},"_nodeVersion":"24.13.0","dependencies":{"@a11y-pulse/browser-adaptor":"*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"happy-dom":"^20.11.2","puppeteer":"^25.5.0"},"peerDependencies":{"puppeteer":"^25.5.0"},"peerDependenciesMeta":{"puppeteer":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/text-spacing-audit_0.0.0_1787210794207_0.011349083591814857","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@a11y-pulse/text-spacing-audit","version":"0.1.0","keywords":["accessibility","a11y","wcag","text-spacing","puppeteer","audit"],"author":{"name":"A11y Pulse Limited"},"license":"SEE LICENSE IN LICENSE.md","_id":"@a11y-pulse/text-spacing-audit@0.1.0","maintainers":[{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"}],"homepage":"https://github.com/A11y-Pulse/audits/tree/main/packages/text-spacing-audit#readme","bugs":{"url":"https://github.com/A11y-Pulse/audits/issues"},"dist":{"shasum":"143097612add44a2b9b22980c4f9e191099e36d7","tarball":"https://registry.npmjs.org/@a11y-pulse/text-spacing-audit/-/text-spacing-audit-0.1.0.tgz","fileCount":13,"integrity":"sha512-K3GoNuIPrOIAf7E+ViMD8R93oCGiZpJwuHBQd+8T9WDA5gAt27fmbhuvzjVTUrHKjSuuUC82vVNAxD1hMfqZqA==","signatures":[{"sig":"MEUCIA5sCbKcaEOrGRKExlDMTt1NQNdgrb2AoXDz9AueC76iAiEA61UOv7fm+hsUg/8mKhfkSnkwsoawVFPuvZ+lvXndCwc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a11y-pulse%2ftext-spacing-audit@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":92237},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./puppeteer":{"types":"./dist/adaptors/puppeteer.d.ts","import":"./dist/adaptors/puppeteer.js"}},"gitHead":"f05e0097f3dc9b26151bb00db20bf4ba98707bda","scripts":{"lint":"biome check","test":"npm run typecheck && npm run test:unit","build":"tsup && tsc -p tsconfig.build.json","lint:fix":"biome check --fix","test:unit":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","test:integration":"vitest run --config vitest.integration.config.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fa0ebe32-2e5e-4931-a894-d10101525ce3"}},"repository":{"url":"git+https://github.com/A11y-Pulse/audits.git","type":"git","directory":"packages/text-spacing-audit"},"_npmVersion":"12.0.2","description":"WCAG 1.4.12 Text Spacing audit that injects the SC spacing overrides and detects clipped or overlapping text.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@a11y-pulse/browser-adaptor":"*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"happy-dom":"^20.11.2","puppeteer":"^25.5.0"},"peerDependencies":{"puppeteer":"^25.5.0"},"peerDependenciesMeta":{"puppeteer":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/text-spacing-audit_0.1.0_1787212757863_0.33171294315594224","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@a11y-pulse/text-spacing-audit","version":"0.2.0","keywords":["accessibility","a11y","wcag","text-spacing","puppeteer","audit"],"author":{"name":"A11y Pulse Limited"},"license":"SEE LICENSE IN LICENSE.md","_id":"@a11y-pulse/text-spacing-audit@0.2.0","maintainers":[{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"}],"homepage":"https://github.com/A11y-Pulse/audits/tree/main/packages/text-spacing-audit#readme","bugs":{"url":"https://github.com/A11y-Pulse/audits/issues"},"dist":{"shasum":"cd5a118047528f58fd8b07844245746960dd6949","tarball":"https://registry.npmjs.org/@a11y-pulse/text-spacing-audit/-/text-spacing-audit-0.2.0.tgz","fileCount":13,"integrity":"sha512-w3iX/xVf1buKRUIOoY3nUjDv3Jcj4JbYtKCoahh89gghkwA3Z8rHn10tgbXiKT+UUtw+X+gHYCTVLdi5US9Xcg==","signatures":[{"sig":"MEQCIDdbjm8QYm+PVV1MGCIzvIDOqrgOQMhA3/EIS2WADiRKAiBkedtv/EdplwznroNKrkI6/q1gljm6nKl3g4VKY19vAg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a11y-pulse%2ftext-spacing-audit@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":99436},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./puppeteer":{"types":"./dist/adaptors/puppeteer.d.ts","import":"./dist/adaptors/puppeteer.js"}},"gitHead":"cdf225620b08438311d55916e8c946f45bd61adc","scripts":{"lint":"biome check","test":"npm run typecheck && npm run test:unit","build":"tsup && tsc -p tsconfig.build.json","lint:fix":"biome check --fix","test:unit":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","test:integration":"vitest run --config vitest.integration.config.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fa0ebe32-2e5e-4931-a894-d10101525ce3"}},"repository":{"url":"git+https://github.com/A11y-Pulse/audits.git","type":"git","directory":"packages/text-spacing-audit"},"_npmVersion":"12.0.2","description":"WCAG 1.4.12 Text Spacing audit that injects the SC spacing overrides and detects clipped or overlapping text.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@a11y-pulse/browser-adaptor":"*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"happy-dom":"^20.11.2","puppeteer":"^25.5.0"},"peerDependencies":{"puppeteer":"^25.5.0"},"peerDependenciesMeta":{"puppeteer":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/text-spacing-audit_0.2.0_1787554134734_0.07019605616569491","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@a11y-pulse/text-spacing-audit","version":"0.2.1","keywords":["accessibility","a11y","wcag","text-spacing","puppeteer","audit"],"author":{"name":"A11y Pulse"},"license":"SEE LICENSE IN LICENSE.md","_id":"@a11y-pulse/text-spacing-audit@0.2.1","maintainers":[{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"}],"homepage":"https://github.com/A11y-Pulse/audits/tree/main/packages/text-spacing-audit#readme","bugs":{"url":"https://github.com/A11y-Pulse/audits/issues"},"dist":{"shasum":"e148d7d2c3e7d4c7aef829051942d097a103a91e","tarball":"https://registry.npmjs.org/@a11y-pulse/text-spacing-audit/-/text-spacing-audit-0.2.1.tgz","fileCount":13,"integrity":"sha512-w8X4EZ3f+MIX0vhNoOKyhgrDlJu21puwekLXkXgI5I3mlZBwbFcT5fG2u5XuN0tju+XxHBtpcqd0CSrRBUVdTg==","signatures":[{"sig":"MEUCIQDxenmsSGgCVnXWqefcqUQwXnL8MoJF1ZLZjBe4gZ9SvwIgTLj7Z9QrV+uPLE/MaH/czvNDZPlp0MXJkyDWaV148+E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQC0uKP1Fg56XBPFCYmT5Uz/2bYDZins3H5+j4NzLoPDxQIhAL9HfTYTTpObEP66lnYtFJt9wQ7DbiYHb04r/nFAB7wU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a11y-pulse%2ftext-spacing-audit@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":98557},"type":"module","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts"},"./puppeteer":{"types":"./dist/adaptors/puppeteer.d.ts","import":"./dist/adaptors/puppeteer.js","source":"./src/adaptors/puppeteer.ts"}},"gitHead":"8431177b6098e09064306dc08bddfb59796b444d","scripts":{"lint":"biome check","test":"npm run typecheck && npm run test:unit","build":"tsup && tsc -p tsconfig.build.json","lint:fix":"biome check --fix","test:unit":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","test:integration":"vitest run --config vitest.integration.config.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fa0ebe32-2e5e-4931-a894-d10101525ce3"}},"repository":{"url":"git+https://github.com/A11y-Pulse/audits.git","type":"git","directory":"packages/text-spacing-audit"},"_npmVersion":"12.0.2","description":"WCAG 1.4.12 Text Spacing audit that injects the SC spacing overrides and detects clipped or overlapping text.","directories":{},"_nodeVersion":"22.23.2","dependencies":{"@a11y-pulse/browser-adaptor":"*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"happy-dom":"^20.12.0","puppeteer":"^25.9.0"},"peerDependencies":{"puppeteer":"^25.9.0"},"peerDependenciesMeta":{"puppeteer":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/text-spacing-audit_0.2.1_1788834896970_0.5903794576505201","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@a11y-pulse/text-spacing-audit@0.3.0","bugs":{"url":"https://github.com/A11y-Pulse/audits/issues"},"dist":{"shasum":"b83417e274954e1fe902eacf551f44e75dc48926","tarball":"https://registry.npmjs.org/@a11y-pulse/text-spacing-audit/-/text-spacing-audit-0.3.0.tgz","fileCount":16,"integrity":"sha512-dV2mTom29FWQTEf0hcFzzSdJ0oN1YM1lQQM4/FWSecW+8lHTtq+bcpSB1dBMtwT/KwHe0XpPoLuN1V79LkVIHw==","signatures":[{"sig":"MEUCIQCj3Bzehc85Z5qL0L/YnXJ3eRIVjAjyrLxNQwP9ZUAV6gIgfo9MUrqHTpcAsK3fnSIuGBV82HNkgzQ7EefJzIDALws=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCTqA7NpttEMtT6XdIu8f6VB2r//gN4uzqsMXEKXo3xmwIgTj7J1txFzMJ2qPvl/dZfIViiS0ZiMFbJwellxxjn2zI="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a11y-pulse%2ftext-spacing-audit@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":103598},"name":"@a11y-pulse/text-spacing-audit","type":"module","author":{"name":"A11y Pulse"},"engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","source":"./src/index.ts"},"./puppeteer":{"types":"./dist/adaptors/puppeteer.d.ts","import":"./dist/adaptors/puppeteer.js","source":"./src/adaptors/puppeteer.ts"},"./playwright":{"types":"./dist/adaptors/playwright.d.ts","import":"./dist/adaptors/playwright.js","source":"./src/adaptors/playwright.ts"}},"gitHead":"facc34c09fbb3982d29a1dc04944b5d035af7780","license":"SEE LICENSE IN LICENSE.md","scripts":{"lint":"biome check","test":"npm run typecheck && npm run test:unit","build":"tsup && tsc -p tsconfig.build.json","lint:fix":"biome check --fix","test:unit":"vitest run","typecheck":"tsc --noEmit","prepublishOnly":"npm run build","test:integration":"vitest run --config vitest.integration.config.ts"},"version":"0.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:fa0ebe32-2e5e-4931-a894-d10101525ce3"}},"homepage":"https://github.com/A11y-Pulse/audits/tree/main/packages/text-spacing-audit#readme","keywords":["accessibility","a11y","wcag","text-spacing","puppeteer","audit"],"repository":{"url":"git+https://github.com/A11y-Pulse/audits.git","type":"git","directory":"packages/text-spacing-audit"},"_npmVersion":"12.0.2","description":"WCAG 1.4.12 Text Spacing audit that injects the SC spacing overrides and detects clipped or overlapping text.","directories":{},"maintainers":[{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"}],"_nodeVersion":"22.23.2","dependencies":{"@a11y-pulse/browser-adaptor":"*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"happy-dom":"^20.12.0","puppeteer":"^25.10.0","playwright-core":"^1.63.0"},"peerDependencies":{"puppeteer":"^25.10.0","playwright-core":">=1.40"},"peerDependenciesMeta":{"puppeteer":{"optional":true},"playwright-core":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/text-spacing-audit_0.3.0_1789286322844_0.6180344474786301"}}},"time":{"created":"2026-08-20T07:26:34.050Z","modified":"2026-09-13T07:58:43.248Z","0.0.0":"2026-08-20T07:26:34.355Z","0.1.0":"2026-08-20T07:59:17.997Z","0.2.0":"2026-08-24T06:48:54.874Z","0.2.1":"2026-09-08T02:34:57.070Z","0.3.0":"2026-09-13T07:58:42.930Z"},"bugs":{"url":"https://github.com/A11y-Pulse/audits/issues"},"author":{"name":"A11y Pulse"},"license":"SEE LICENSE IN LICENSE.md","homepage":"https://github.com/A11y-Pulse/audits/tree/main/packages/text-spacing-audit#readme","keywords":["accessibility","a11y","wcag","text-spacing","puppeteer","audit"],"repository":{"url":"git+https://github.com/A11y-Pulse/audits.git","type":"git","directory":"packages/text-spacing-audit"},"description":"WCAG 1.4.12 Text Spacing audit that injects the SC spacing overrides and detects clipped or overlapping text.","maintainers":[{"name":"wildlyinaccurate","email":"joseph@wildlyinaccurate.com"}],"readme":"# @a11y-pulse/text-spacing-audit\n\n[![npm version](https://img.shields.io/npm/v/@a11y-pulse/text-spacing-audit)](https://www.npmjs.com/package/@a11y-pulse/text-spacing-audit)\n[![CI](https://github.com/A11y-Pulse/audits/actions/workflows/ci.yml/badge.svg)](https://github.com/A11y-Pulse/audits/actions/workflows/ci.yml)\n[![License: PolyForm Shield 1.0.0](https://img.shields.io/badge/license-PolyForm%20Shield%201.0.0-blue)](./LICENSE.md)\n\nAn accessibility audit for [**WCAG 1.4.12 Text Spacing**](https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html). It injects the success criterion's spacing overrides (the same stylesheet used by Steve Faulkner's [text spacing bookmarklet](https://codepen.io/stevef/full/YLMqbo)), measures candidate text containers, and reports text that is newly clipped or newly overlapping. It is framework-agnostic and can run in any environment that can evaluate JavaScript in a page, such as Puppeteer, Playwright, or Selenium.\n\nThis audit was developed by [A11y Pulse](https://www.a11ypulse.com/) for its accessibility monitoring service. It is released as source-available under the [PolyForm Shield License 1.0.0](#license).\n\n## Install\n\n```bash\nnpm install @a11y-pulse/text-spacing-audit puppeteer\n```\n\n`puppeteer` is an optional peer dependency. It is only required if you use the bundled [Puppeteer adaptor](#adaptors). Other frameworks can supply their own adaptor without installing Puppeteer at all.\n\n## Quickstart\n\n```js\nimport { runTextSpacingAudit } from \"@a11y-pulse/text-spacing-audit\";\nimport { PuppeteerAdaptor } from \"@a11y-pulse/text-spacing-audit/puppeteer\";\nimport puppeteer from \"puppeteer\";\n\nconst browser = await puppeteer.launch();\nconst page = await browser.newPage();\nawait page.goto(\"https://who.likesdogs.nz/\");\n\nconst result = await runTextSpacingAudit(new PuppeteerAdaptor(page));\n\nconsole.log(result.summary);\n// { clipped: 0, truncationIncreased: 0, overlaps: 0 }\n\nconsole.log(result.findings);\n// []\n\nawait browser.close();\n```\n\nSee [`examples/puppeteer`](./examples/puppeteer) for a complete, runnable example.\n\n## What it checks\n\nWCAG 1.4.12 requires that content and functionality survive when a user sets:\n\n- line height to at least 1.5 times the font size\n- spacing following paragraphs to at least 2 times the font size\n- letter spacing to at least 0.12 times the font size\n- word spacing to at least 0.16 times the font size\n\nThe audit does not require authors to *use* those values. It checks that the page still shows its text when they are applied.\n\nStatic tools such as axe-core's `avoid-inline-spacing` only flag inline `!important` spacing declarations that would *block* a user override. This audit applies the override and looks at the outcome. Leave axe's rule enabled: the two checks are complementary, and inline `!important` spacing is left to axe because a stylesheet `!important` cannot override it.\n\n## Bookmarklet methodology\n\nThe procedure matches the bookmarklet used by the WCAG community:\n\n1. Wait for `document.fonts.ready` and freeze CSS animations/transitions so motion does not look like clipping.\n2. Collect visible text containers (elements with a non-whitespace direct text node), capped at `candidateLimit`.\n3. Inject:\n\n```css\n* { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }\np { margin-bottom: 2em !important; }\n```\n\n4. Re-measure the same elements and classify:\n   - **clipped** (the only violation class): overflow `hidden`/`clip`, content fitted at baseline, then exceeded the clip box by more than `clipTolerancePx`.\n   - **truncation-increased** (incomplete): already truncated (ellipsis / line-clamp) and overflow grew.\n   - **overlap** (incomplete): nearby text rects that did not intersect at baseline and now do. Sticky and fixed elements are skipped.\n5. Remove the injected styles and verify a sample of rects match the baseline. A mismatch sets `restored: false` but never throws.\n\nGrowth and reflow without clipping are not findings. That is the correct response to a spacing override.\n\n## Browser support\n\n| Adaptor | Browser | Supported |\n| --- | --- | --- |\n| Puppeteer | Chrome | Yes |\n| Playwright | Chromium | Yes |\n| Playwright | WebKit | Yes |\n| Playwright | Firefox | Yes |\n\nVerified by this repo's integration suites, which run every audit against each of these engines. Unsupported and partial cases are skipped there with the reason printed alongside them.\n\n## Options\n\nThe following options can be passed to `runTextSpacingAudit` as `TextSpacingOptions`:\n\n| Option             | Type     | Default | Description                                                                                          |\n| ------------------ | -------- | ------- | ---------------------------------------------------------------------------------------------------- |\n| `candidateLimit`   | `number` | `500`   | Max visible text containers to measure, in document order.                                           |\n| `clipTolerancePx`  | `number` | `2`     | Overflow in px that must be exceeded before a clip or truncation increase counts.                    |\n| `settleMs`         | `number` | `200`   | Extra wait after two animation frames for layout to settle after injecting or removing styles.       |\n\n## Result shape\n\n`runTextSpacingAudit` resolves to a `TextSpacingResult`:\n\n```ts\ntype TextSpacingResult = {\n  findings: Array<{\n    selector: string;\n    html: string;\n    kind: \"clipped\" | \"truncation-increased\" | \"overlap\";\n    metrics: { beforeOverflowPx: number; afterOverflowPx: number };\n    overlapsWith?: string;\n  }>;\n  candidateCount: number;\n  restored: boolean;\n  summary: {\n    clipped: number;\n    truncationIncreased: number;\n    overlaps: number;\n  };\n};\n```\n\n`candidateCount` is the number of stable (non-moving) text containers that were measured. `clipped` findings are the confident loss-of-content signal. `truncation-increased` and `overlap` are heuristic and should be treated as incomplete, not violations.\n\n## Adaptors\n\nThe audit itself is framework-agnostic: it drives a page through an **adaptor**, a small interface of primitives that the audit calls without knowing which browser automation library is behind it.\n\nThe package ships two implementations: `PuppeteerAdaptor` from the `./puppeteer` subpath, backed by a Puppeteer `Page`, and `PlaywrightAdaptor` from `./playwright`, backed by a Playwright `Page`. Other environments (Selenium, WebDriver) can be supported by implementing the same interface, exported as `TextSpacingAuditAdaptor` (aliased as `BrowserAdaptor` from the package root).\n\n### `TextSpacingAuditAdaptor` / `BrowserAdaptor`\n\n| Method                  | Description                                                                                               |\n| ----------------------- | --------------------------------------------------------------------------------------------------------- |\n| `evaluate(fn, ...args)` | Runs `fn` in the page context, passing in any serialisable `args`, and returns its result.               |\n\n### Writing a new adaptor\n\nImplement `TextSpacingAuditAdaptor` from `@a11y-pulse/text-spacing-audit` (or its `BrowserAdaptor` alias) against your automation library's page/session object, then pass an instance to `runTextSpacingAudit`:\n\n```ts\nimport type { TextSpacingAuditAdaptor } from \"@a11y-pulse/text-spacing-audit\";\n\nclass MyFrameworkAdaptor implements TextSpacingAuditAdaptor {\n  // ...implement evaluate for your framework\n}\n```\n\nUse [`src/adaptors/puppeteer.ts`](./src/adaptors/puppeteer.ts) or [`src/adaptors/playwright.ts`](./src/adaptors/playwright.ts) as a reference implementation. Each is a thin `page.evaluate` wrapper.\n\n## Limitations\n\n- **Overlap is incomplete, never a violation.** Overlap detection is heuristic (nearby pairs only; sticky/fixed skipped). Decorative overlaps and stacking contexts can still produce noise.\n- **Inline `!important` spacing is left to axe.** A user stylesheet cannot beat an inline `!important` declaration. axe-core `avoid-inline-spacing` covers that blocker; this audit cannot restyle those elements.\n- **Already-clipped content is not attributed to the override.** Only new overflow inside a clipping box is reported as `clipped`.\n- **Intentional truncation.** Single-line ellipsis and line-clamp that were already truncating route to `truncation-increased` (incomplete), not `clipped`.\n- **Motion.** CSS animations are frozen before the baseline. Elements whose rects move between two samples (JS-driven motion) are excluded.\n- **Main frame only.** Text in canvas, images, or cross-origin iframes is invisible to the audit. Closed shadow roots are opaque; open shadow roots are walked.\n- **Paragraph spacing is `p` only.** That matches the bookmarklet. Pages that use `div`s as paragraphs are under-tested.\n- **Loss of functionality** without a geometric symptom (for example a click target covered by a transparent sibling) is not detected.\n- **Forcing `line-height: 1.5`** can reduce spacing on a page that already exceeds the minimum. That is faithful to the bookmarklet. Clipping under 1.5 still implies clipping under anything larger.\n\n## Releasing\n\nReleases are managed in the [A11y-Pulse/audits](https://github.com/A11y-Pulse/audits) monorepo with [Changesets](https://github.com/changesets/changesets). Publishing uses [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) (OIDC). There is no long-lived `NPM_TOKEN`.\n\n### Ship a change\n\n1. Open a PR against `main` that includes a changeset (`npx changeset`) naming `@a11y-pulse/text-spacing-audit`.\n2. After merge, the Release workflow opens a Version PR. Merging that PR publishes this package to npm and tags `@a11y-pulse/text-spacing-audit@<version>`.\n\nTrusted Publisher on npm must stay configured for:\n\n| Field | Value |\n| --- | --- |\n| Organization or user | `A11y-Pulse` |\n| Repository | `audits` |\n| Workflow filename | `release.yml` |\n\n### Consumers (e.g. the A11y Pulse runner)\n\nBumping the published version in downstream apps is a separate change. Update the dependency range / lockfile there after the npm release lands.\n\n## License\n\nReleased under the [PolyForm Shield License 1.0.0](./LICENSE.md), in plain language:\n\n- **Source-available.** The source is public and you can read, fork, and modify it.\n- **Permitted for non-competing use.** You can use this package freely in your own products and services, as long as they don't compete with A11y Pulse.\n- **Competing products are forbidden.** You may not use this software (or a modified version of it) to build a product or service that competes with A11y Pulse's accessibility monitoring offering.\n\nSee [LICENSE.md](./LICENSE.md) for the full, binding terms.\n","readmeFilename":"README.md"}