ultra11y 5.41.0 → 5.41.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@
10
10
  "name": "ultra11y",
11
11
  "source": "./",
12
12
  "description": "Automatic accessibility review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page WCAG 2.2 AA / RGAA reports in Claude Code.",
13
- "version": "5.41.0",
13
+ "version": "5.41.2",
14
14
  "author": { "name": "maxgfr" },
15
15
  "homepage": "https://github.com/maxgfr/ultra11y",
16
16
  "repository": "https://github.com/maxgfr/ultra11y",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ultra11y",
3
3
  "description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): automatic review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page conformance reports in interactive agent sessions.",
4
- "version": "5.41.0",
4
+ "version": "5.41.2",
5
5
  "author": { "name": "maxgfr" },
6
6
  "homepage": "https://github.com/maxgfr/ultra11y",
7
7
  "repository": "https://github.com/maxgfr/ultra11y",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ultra11y",
3
3
  "description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): automatic review before commit, push and pull request, plus detailed evidenced Markdown, HTML and per-page conformance reports in interactive agent sessions.",
4
- "version": "5.41.0",
4
+ "version": "5.41.2",
5
5
  "author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
6
6
  "homepage": "https://github.com/maxgfr/ultra11y",
7
7
  "repository": "https://github.com/maxgfr/ultra11y",
@@ -27,7 +27,7 @@ import { fileURLToPath } from "node:url";
27
27
  // Ownership marker: `ultra11y install --opencode` refuses to overwrite a plugin file that
28
28
  // does not carry it, so a user's own ultra11y.js is never clobbered.
29
29
  const ULTRA11Y_OPENCODE_PLUGIN = "ULTRA11Y_OPENCODE_PLUGIN";
30
- const ULTRA11Y_PLUGIN_VERSION = "5.41.0";
30
+ const ULTRA11Y_PLUGIN_VERSION = "5.41.2";
31
31
 
32
32
  const here = dirname(fileURLToPath(import.meta.url));
33
33
 
@@ -236,7 +236,22 @@ function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFocusables
236
236
  const rootExpr = scope ? `document.querySelectorAll(${JSON.stringify(scope)})` : `[document.documentElement]`;
237
237
  return `(() => { ${PRELUDE}
238
238
  const sel = 'a[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
239
- const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
239
+ // A BOX THAT IS ANIMATING CANNOT BE COMPARED ACROSS TIME, so it contributes a constant.
240
+ //
241
+ // The snapshot is taken before Tab and re-read after it, and the properties compared include
242
+ // background-color and colour \u2014 so a pseudo-element pulsing on a keyframe animation differs
243
+ // between the two reads for a reason that has nothing to do with focus. That reads as \xAB the
244
+ // focus is visible \xBB on a control that has no indicator at all: a MISSED non-conformity,
245
+ // strictly worse than the false positive the pseudo-element read was added to remove.
246
+ //
247
+ // A transition is not affected and must not be excluded: it only moves on a state change,
248
+ // so its at-rest value is stable and a focus transition is exactly what we want to see.
249
+ //
250
+ // When every part of an element is animated, both sides collapse to the same constant and
251
+ // the probe reports \xAB no visible change \xBB \u2014 a finding rather than a silent pass. That is the
252
+ // safe direction: this tool would rather name something a human can dismiss than clear
253
+ // something nobody will look at again.
254
+ const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
240
255
  const snap = (e) => [__style(e, null), __style(e, '::before'), __style(e, '::after')].join('#');
241
256
  // Visually-hidden radio/checkbox \u2192 measure its visible label/proxy, not the input.
242
257
  const proxyFor = (e) => {
@@ -283,7 +298,9 @@ var FOCUS_CHECK_PROBE = `(() => {
283
298
  // its control in label::before -- DSFR, GOV.UK, USWDS, Bootstrap -- puts the focus ring
284
299
  // there too, and reading only the element would report every one of them as unfocusable.
285
300
  // (No backticks in this comment: it lives inside a template literal.)
286
- const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
301
+ // Same three boxes, same animation exclusion as pass 1 -- an animating box contributes a
302
+ // constant on both sides, so it can never fabricate a difference (nor hide a real one).
303
+ const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
287
304
  const now = [st(null), st('::before'), st('::after')].join('#');
288
305
  return { key: key, changed: now !== rec.rest, selector: rec.sel, html: rec.html };
289
306
  })()`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultra11y",
3
- "version": "5.41.0",
3
+ "version": "5.41.2",
4
4
  "description": "Audit HTML/CSS/JSX against WCAG 2.2 AA and produce a dated compliance report, or author/review accessible markup (native-HTML-first). A skills.sh agent skill: an install-free static engine (bundled, no npm install) plus the agent's judgment, with check/verify gates against hallucinated non-conformities, and pluggable in-repo standards packs (RGAA France first; Section 508 & EN 301 549 next).",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.33.0",
@@ -6,7 +6,7 @@ import { join as join53, relative as relative6, resolve as resolve16, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "5.41.0";
9
+ var VERSION = "5.41.2";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -65276,7 +65276,22 @@ function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFocusables
65276
65276
  const rootExpr = scope ? `document.querySelectorAll(${JSON.stringify(scope)})` : `[document.documentElement]`;
65277
65277
  return `(() => { ${PRELUDE}
65278
65278
  const sel = 'a[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
65279
- const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65279
+ // A BOX THAT IS ANIMATING CANNOT BE COMPARED ACROSS TIME, so it contributes a constant.
65280
+ //
65281
+ // The snapshot is taken before Tab and re-read after it, and the properties compared include
65282
+ // background-color and colour \u2014 so a pseudo-element pulsing on a keyframe animation differs
65283
+ // between the two reads for a reason that has nothing to do with focus. That reads as \xAB the
65284
+ // focus is visible \xBB on a control that has no indicator at all: a MISSED non-conformity,
65285
+ // strictly worse than the false positive the pseudo-element read was added to remove.
65286
+ //
65287
+ // A transition is not affected and must not be excluded: it only moves on a state change,
65288
+ // so its at-rest value is stable and a focus transition is exactly what we want to see.
65289
+ //
65290
+ // When every part of an element is animated, both sides collapse to the same constant and
65291
+ // the probe reports \xAB no visible change \xBB \u2014 a finding rather than a silent pass. That is the
65292
+ // safe direction: this tool would rather name something a human can dismiss than clear
65293
+ // something nobody will look at again.
65294
+ const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65280
65295
  const snap = (e) => [__style(e, null), __style(e, '::before'), __style(e, '::after')].join('#');
65281
65296
  // Visually-hidden radio/checkbox \u2192 measure its visible label/proxy, not the input.
65282
65297
  const proxyFor = (e) => {
@@ -65323,7 +65338,9 @@ var FOCUS_CHECK_PROBE = `(() => {
65323
65338
  // its control in label::before -- DSFR, GOV.UK, USWDS, Bootstrap -- puts the focus ring
65324
65339
  // there too, and reading only the element would report every one of them as unfocusable.
65325
65340
  // (No backticks in this comment: it lives inside a template literal.)
65326
- const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65341
+ // Same three boxes, same animation exclusion as pass 1 -- an animating box contributes a
65342
+ // constant on both sides, so it can never fabricate a difference (nor hide a real one).
65343
+ const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65327
65344
  const now = [st(null), st('::before'), st('::after')].join('#');
65328
65345
  return { key: key, changed: now !== rec.rest, selector: rec.sel, html: rec.html };
65329
65346
  })()`;
@@ -3,7 +3,7 @@ name: review-a11y
3
3
  description: "Use to REVIEW changed frontend code for accessibility: staged files, a working diff, branch, or PR; prompts such as 'review a11y', 'is this accessible?', or 'anything to fix before merge?'; and hook or CI findings on a change. Run the bundled install-free engine with cross-file JSX/TSX analysis, then adjudicate candidates and return a severity-ranked WCAG 2.2 AA review with file:line fixes, residual rendering risks, and a verdict. Use `ultra11y` instead for whole-repo audits, dated conformance reports, country standards, PRD backlogs, or authoring accessible markup."
4
4
  license: MIT
5
5
  metadata:
6
- version: 5.41.0
6
+ version: 5.41.2
7
7
  ---
8
8
 
9
9
  # review-a11y — accessibility review of changed code
@@ -6,7 +6,7 @@ import { join as join53, relative as relative6, resolve as resolve16, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "5.41.0";
9
+ var VERSION = "5.41.2";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -65276,7 +65276,22 @@ function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFocusables
65276
65276
  const rootExpr = scope ? `document.querySelectorAll(${JSON.stringify(scope)})` : `[document.documentElement]`;
65277
65277
  return `(() => { ${PRELUDE}
65278
65278
  const sel = 'a[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
65279
- const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65279
+ // A BOX THAT IS ANIMATING CANNOT BE COMPARED ACROSS TIME, so it contributes a constant.
65280
+ //
65281
+ // The snapshot is taken before Tab and re-read after it, and the properties compared include
65282
+ // background-color and colour \u2014 so a pseudo-element pulsing on a keyframe animation differs
65283
+ // between the two reads for a reason that has nothing to do with focus. That reads as \xAB the
65284
+ // focus is visible \xBB on a control that has no indicator at all: a MISSED non-conformity,
65285
+ // strictly worse than the false positive the pseudo-element read was added to remove.
65286
+ //
65287
+ // A transition is not affected and must not be excluded: it only moves on a state change,
65288
+ // so its at-rest value is stable and a focus transition is exactly what we want to see.
65289
+ //
65290
+ // When every part of an element is animated, both sides collapse to the same constant and
65291
+ // the probe reports \xAB no visible change \xBB \u2014 a finding rather than a silent pass. That is the
65292
+ // safe direction: this tool would rather name something a human can dismiss than clear
65293
+ // something nobody will look at again.
65294
+ const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65280
65295
  const snap = (e) => [__style(e, null), __style(e, '::before'), __style(e, '::after')].join('#');
65281
65296
  // Visually-hidden radio/checkbox \u2192 measure its visible label/proxy, not the input.
65282
65297
  const proxyFor = (e) => {
@@ -65323,7 +65338,9 @@ var FOCUS_CHECK_PROBE = `(() => {
65323
65338
  // its control in label::before -- DSFR, GOV.UK, USWDS, Bootstrap -- puts the focus ring
65324
65339
  // there too, and reading only the element would report every one of them as unfocusable.
65325
65340
  // (No backticks in this comment: it lives inside a template literal.)
65326
- const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65341
+ // Same three boxes, same animation exclusion as pass 1 -- an animating box contributes a
65342
+ // constant on both sides, so it can never fabricate a difference (nor hide a real one).
65343
+ const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65327
65344
  const now = [st(null), st('::before'), st('::after')].join('#');
65328
65345
  return { key: key, changed: now !== rec.rest, selector: rec.sel, html: rec.html };
65329
65346
  })()`;
@@ -3,7 +3,7 @@ name: ultra11y
3
3
  description: "Use to AUDIT a repository, site, or page against WCAG 2.2 AA or a country standard such as RGAA; produce a dated conformance report, criterion grid, PRD backlog, or tickets; AUTHOR accessible markup; fix accessibility; or wire a repo gate and rendered-DOM capture pipeline. The bundled install-free engine runs 93 static checks tied to success criteria, while the agent adjudicates judgment criteria and routes rendering criteria to browser scans; check/verify reject invented non-conformities. Use `review-a11y` instead for a review scoped only to a diff, branch, or PR."
4
4
  license: MIT
5
5
  metadata:
6
- version: 5.41.0
6
+ version: 5.41.2
7
7
  ---
8
8
 
9
9
  # ultra11y — audit WCAG 2.2 AA and write accessible markup
@@ -6,7 +6,7 @@ import { join as join53, relative as relative6, resolve as resolve16, sep as sep
6
6
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL3 } from "url";
7
7
 
8
8
  // src/types.ts
9
- var VERSION = "5.41.0";
9
+ var VERSION = "5.41.2";
10
10
  var SCHEMA_VERSION = 2;
11
11
  var INAPPLICABLE_STATUS = "C";
12
12
 
@@ -65276,7 +65276,22 @@ function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFocusables
65276
65276
  const rootExpr = scope ? `document.querySelectorAll(${JSON.stringify(scope)})` : `[document.documentElement]`;
65277
65277
  return `(() => { ${PRELUDE}
65278
65278
  const sel = 'a[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
65279
- const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65279
+ // A BOX THAT IS ANIMATING CANNOT BE COMPARED ACROSS TIME, so it contributes a constant.
65280
+ //
65281
+ // The snapshot is taken before Tab and re-read after it, and the properties compared include
65282
+ // background-color and colour \u2014 so a pseudo-element pulsing on a keyframe animation differs
65283
+ // between the two reads for a reason that has nothing to do with focus. That reads as \xAB the
65284
+ // focus is visible \xBB on a control that has no indicator at all: a MISSED non-conformity,
65285
+ // strictly worse than the false positive the pseudo-element read was added to remove.
65286
+ //
65287
+ // A transition is not affected and must not be excluded: it only moves on a state change,
65288
+ // so its at-rest value is stable and a focus transition is exactly what we want to see.
65289
+ //
65290
+ // When every part of an element is animated, both sides collapse to the same constant and
65291
+ // the probe reports \xAB no visible change \xBB \u2014 a finding rather than a silent pass. That is the
65292
+ // safe direction: this tool would rather name something a human can dismiss than clear
65293
+ // something nobody will look at again.
65294
+ const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65280
65295
  const snap = (e) => [__style(e, null), __style(e, '::before'), __style(e, '::after')].join('#');
65281
65296
  // Visually-hidden radio/checkbox \u2192 measure its visible label/proxy, not the input.
65282
65297
  const proxyFor = (e) => {
@@ -65323,7 +65338,9 @@ var FOCUS_CHECK_PROBE = `(() => {
65323
65338
  // its control in label::before -- DSFR, GOV.UK, USWDS, Bootstrap -- puts the focus ring
65324
65339
  // there too, and reading only the element would report every one of them as unfocusable.
65325
65340
  // (No backticks in this comment: it lives inside a template literal.)
65326
- const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65341
+ // Same three boxes, same animation exclusion as pass 1 -- an animating box contributes a
65342
+ // constant on both sides, so it can never fabricate a difference (nor hide a real one).
65343
+ const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
65327
65344
  const now = [st(null), st('::before'), st('::after')].join('#');
65328
65345
  return { key: key, changed: now !== rec.rest, selector: rec.sel, html: rec.html };
65329
65346
  })()`;
@@ -27,7 +27,7 @@ variables:
27
27
  ULTRA11Y_LANG: en
28
28
  # Exact by default: a CI report must say which engine produced it. The release script keeps
29
29
  # this pin aligned with the package version; override only for a deliberate upgrade test.
30
- ULTRA11Y_VERSION: '5.41.0'
30
+ ULTRA11Y_VERSION: '5.41.2'
31
31
  # Batches of 8 amortise the prompt; each accepted verdict is checkpointed before the next
32
32
  # batch, and later passes receive only the residue.
33
33
  ULTRA11Y_GRAIN: batch
package/src/probes.ts CHANGED
@@ -206,7 +206,22 @@ export function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFoc
206
206
  const rootExpr = scope ? `document.querySelectorAll(${JSON.stringify(scope)})` : `[document.documentElement]`;
207
207
  return `(() => { ${PRELUDE}
208
208
  const sel = 'a[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
209
- const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
209
+ // A BOX THAT IS ANIMATING CANNOT BE COMPARED ACROSS TIME, so it contributes a constant.
210
+ //
211
+ // The snapshot is taken before Tab and re-read after it, and the properties compared include
212
+ // background-color and colour — so a pseudo-element pulsing on a keyframe animation differs
213
+ // between the two reads for a reason that has nothing to do with focus. That reads as « the
214
+ // focus is visible » on a control that has no indicator at all: a MISSED non-conformity,
215
+ // strictly worse than the false positive the pseudo-element read was added to remove.
216
+ //
217
+ // A transition is not affected and must not be excluded: it only moves on a state change,
218
+ // so its at-rest value is stable and a focus transition is exactly what we want to see.
219
+ //
220
+ // When every part of an element is animated, both sides collapse to the same constant and
221
+ // the probe reports « no visible change » — a finding rather than a silent pass. That is the
222
+ // safe direction: this tool would rather name something a human can dismiss than clear
223
+ // something nobody will look at again.
224
+ const __style = (e, pseudo) => { const s = getComputedStyle(e, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
210
225
  const snap = (e) => [__style(e, null), __style(e, '::before'), __style(e, '::after')].join('#');
211
226
  // Visually-hidden radio/checkbox → measure its visible label/proxy, not the input.
212
227
  const proxyFor = (e) => {
@@ -257,7 +272,9 @@ export const FOCUS_CHECK_PROBE = `(() => {
257
272
  // its control in label::before -- DSFR, GOV.UK, USWDS, Bootstrap -- puts the focus ring
258
273
  // there too, and reading only the element would report every one of them as unfocusable.
259
274
  // (No backticks in this comment: it lives inside a template literal.)
260
- const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
275
+ // Same three boxes, same animation exclusion as pass 1 -- an animating box contributes a
276
+ // constant on both sides, so it can never fabricate a difference (nor hide a real one).
277
+ const st = (pseudo) => { const s = getComputedStyle(proxy, pseudo); if (s.animationName && s.animationName !== 'none') return 'animated'; return [s.outlineStyle, s.outlineWidth, s.outlineColor, s.boxShadow, s.borderColor, s.borderTopWidth, s.borderBottomWidth, s.backgroundColor, s.color, s.textDecorationLine].join('|'); };
261
278
  const now = [st(null), st('::before'), st('::after')].join('#');
262
279
  return { key: key, changed: now !== rec.rest, selector: rec.sel, html: rec.html };
263
280
  })()`;
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // Single source of truth for shared types + the embedded version. sync-version.mjs
2
2
  // rewrites VERSION at release time (kept in lockstep with package.json + SKILL.md).
3
- export const VERSION = "5.41.0";
3
+ export const VERSION = "5.41.2";
4
4
  export const SCHEMA_VERSION = 2;
5
5
 
6
6
  // Lang = the UI FRAME's language — the `L` localization tables each module (report,