pour-engine 1.42.1 → 1.42.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.
package/config/project.config.js
CHANGED
|
@@ -66,19 +66,19 @@ function quarterTurn(style, win) {
|
|
|
66
66
|
/** Walk the sheet in cascade order, keeping the LAST relevant declaration
|
|
67
67
|
* per root, orientation and property, so a later declaration in the same
|
|
68
68
|
* orientation block undoes an earlier lock. */
|
|
69
|
-
function scanRules(rules, orientationContext, active, state, doc) {
|
|
69
|
+
function scanRules(rules, orientationContext, active, state, doc, query) {
|
|
70
70
|
const win = doc.defaultView;
|
|
71
71
|
for (const rule of rules ?? []) {
|
|
72
72
|
if (rule.type === win.CSSRule.SUPPORTS_RULE) {
|
|
73
73
|
// An unsupported branch cannot apply in either orientation.
|
|
74
|
-
if (
|
|
74
|
+
if (query.supports(rule.conditionText)) scanRules(rule.cssRules, orientationContext, active, state, doc, query);
|
|
75
75
|
continue;
|
|
76
76
|
}
|
|
77
77
|
if (rule.type === win.CSSRule.MEDIA_RULE) {
|
|
78
78
|
const condition = rule.conditionText ?? rule.media.mediaText;
|
|
79
79
|
const orientation = /orientation\s*:\s*(portrait|landscape)/i.exec(condition)?.[1];
|
|
80
80
|
const context = orientation ? { condition, orientation } : orientationContext;
|
|
81
|
-
scanRules(rule.cssRules, context, active &&
|
|
81
|
+
scanRules(rule.cssRules, context, active && query.media(condition), state, doc, query);
|
|
82
82
|
continue;
|
|
83
83
|
}
|
|
84
84
|
if (orientationContext && rule.style) {
|
|
@@ -104,7 +104,7 @@ function scanRules(rules, orientationContext, active, state, doc) {
|
|
|
104
104
|
// Layers and other grouping rules retain the surrounding media state.
|
|
105
105
|
// Unknown conditional groups cannot establish an active restriction.
|
|
106
106
|
if (rule.cssRules) scanRules(rule.cssRules, orientationContext,
|
|
107
|
-
active && rule.conditionText === undefined, state, doc);
|
|
107
|
+
active && rule.conditionText === undefined, state, doc, query);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
@@ -121,13 +121,30 @@ export default {
|
|
|
121
121
|
const doc = element.ownerDocument;
|
|
122
122
|
const win = doc.defaultView;
|
|
123
123
|
const state = new Map();
|
|
124
|
+
// Each media and supports condition is asked once per audit. Framework
|
|
125
|
+
// stylesheets repeat a handful of breakpoints thousands of times: ethz.ch
|
|
126
|
+
// carries 10,604 @media blocks over 98 distinct conditions, and asking
|
|
127
|
+
// matchMedia for every block cost this rule about half of its 10 ms there
|
|
128
|
+
// (measured 2026-09-13). The answers cannot change within one pass.
|
|
129
|
+
const mediaAnswers = new Map();
|
|
130
|
+
const supportsAnswers = new Map();
|
|
131
|
+
const query = {
|
|
132
|
+
media: (condition) => {
|
|
133
|
+
if (!mediaAnswers.has(condition)) mediaAnswers.set(condition, win.matchMedia(condition).matches);
|
|
134
|
+
return mediaAnswers.get(condition);
|
|
135
|
+
},
|
|
136
|
+
supports: (condition) => {
|
|
137
|
+
if (!supportsAnswers.has(condition)) supportsAnswers.set(condition, win.CSS.supports(condition));
|
|
138
|
+
return supportsAnswers.get(condition);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
124
141
|
for (const sheet of doc.styleSheets) {
|
|
125
142
|
if (sheet.disabled) continue;
|
|
126
143
|
const media = sheet.media?.mediaText;
|
|
127
|
-
if (media && !
|
|
144
|
+
if (media && !query.media(media)) continue;
|
|
128
145
|
let rules;
|
|
129
146
|
try { rules = sheet.cssRules; } catch { continue; } // cross-origin: unreadable, skip
|
|
130
|
-
scanRules(rules, null, true, state, doc);
|
|
147
|
+
scanRules(rules, null, true, state, doc, query);
|
|
131
148
|
}
|
|
132
149
|
const findings = [];
|
|
133
150
|
for (const [key, entry] of state) {
|
|
@@ -31,7 +31,14 @@ export default {
|
|
|
31
31
|
const candidates = [...element.querySelectorAll(INTERACTIVE)].filter((el) =>
|
|
32
32
|
!el.matches(':disabled') && !isInert(el)
|
|
33
33
|
&& !(el.tagName === 'INPUT' && el.type === 'hidden') && isRendered(el)
|
|
34
|
-
|
|
34
|
+
// aria-hidden content that Tab reaches is aria-hidden-focus's finding.
|
|
35
|
+
// aria-hidden content with a negative tabindex is not, yet a click or a
|
|
36
|
+
// script still moves focus onto it, and ARIA 1.2 says authors SHOULD NOT
|
|
37
|
+
// hide an element that "may receive focus, either directly via
|
|
38
|
+
// interaction with the user or indirectly via programmatic means", so
|
|
39
|
+
// it stays a candidate here.
|
|
40
|
+
// https://www.w3.org/TR/wai-aria-1.2/#aria-hidden
|
|
41
|
+
&& !(el.closest('[aria-hidden="true"]') && !(el.hasAttribute('tabindex') && el.tabIndex < 0))
|
|
35
42
|
&& (el.matches(NATIVE) || el.hasAttribute('tabindex')));
|
|
36
43
|
// A child requiring review must not conceal a later definite finding.
|
|
37
44
|
const nested = candidates.find((el) => !(el.hasAttribute('tabindex') && el.tabIndex < 0)) ?? candidates[0];
|
|
@@ -45,7 +52,9 @@ export default {
|
|
|
45
52
|
&& !element.matches('a[href], button')) {
|
|
46
53
|
return {
|
|
47
54
|
status: 'incomplete',
|
|
48
|
-
message:
|
|
55
|
+
message: nested.closest('[aria-hidden="true"]')
|
|
56
|
+
? `This control contains an element (<${nested.tagName.toLowerCase()}>) that is hidden from assistive technology but can still receive focus from a click or a script. Focus landing there lands on content a screen reader cannot see. Check whether it can take focus, and if it can, remove it from focus or from aria-hidden.`
|
|
57
|
+
: `This control contains an element (<${nested.tagName.toLowerCase()}>) with a negative tabindex. It can still receive focus. Check that both controls expose the intended name and role, and that focusing and activating the child works correctly.`,
|
|
49
58
|
};
|
|
50
59
|
}
|
|
51
60
|
// <summary> is the one outer control HTML does not forbid this on: its
|
package/package.json
CHANGED