praxis-kit 6.2.3 → 6.5.0
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/dist/_shared/diagnostics.d.ts +4 -0
- package/dist/_shared/diagnostics.js +4 -0
- package/dist/{chunk-H3D2JA3J.js → chunk-AM5J6PY3.js} +486 -310
- package/dist/contract/index.d.ts +1 -0
- package/dist/lit/index.d.ts +1 -0
- package/dist/lit/index.js +486 -310
- package/dist/preact/index.d.ts +1 -0
- package/dist/preact/index.js +486 -310
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/dist/react/legacy.d.ts +2 -2
- package/dist/react/legacy.js +1 -1
- package/dist/{react-options-DQTX4YS_.d.ts → react-options-DoFNqNdY.d.ts} +1 -0
- package/dist/solid/index.d.ts +1 -0
- package/dist/solid/index.js +486 -310
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +486 -310
- package/dist/vite-plugin/index.d.ts +4 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +486 -310
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +486 -310
- package/package.json +5 -5
package/dist/lit/index.js
CHANGED
|
@@ -868,7 +868,11 @@ var ATTRIBUTE_IGNORED_CODES = {
|
|
|
868
868
|
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
869
869
|
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
870
870
|
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
871
|
-
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType
|
|
871
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
|
|
872
|
+
size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
|
|
873
|
+
alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
|
|
874
|
+
height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
|
|
875
|
+
width: DiagnosticCode3.HtmlInputWidthIgnoredForType
|
|
872
876
|
};
|
|
873
877
|
var HtmlDiagnostics = {
|
|
874
878
|
emptyRole(tag) {
|
|
@@ -1056,8 +1060,132 @@ var InvariantBase = class {
|
|
|
1056
1060
|
}
|
|
1057
1061
|
};
|
|
1058
1062
|
|
|
1059
|
-
// ../../lib/contract/src/aria/
|
|
1063
|
+
// ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1064
|
+
var REQUIRED_ARIA_PROPERTIES = {
|
|
1065
|
+
combobox: ["aria-expanded"],
|
|
1066
|
+
option: ["aria-selected"],
|
|
1067
|
+
slider: ["aria-valuenow"],
|
|
1068
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1069
|
+
spinbutton: ["aria-valuenow"]
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
// ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1073
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1074
|
+
|
|
1075
|
+
// ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1060
1076
|
var NO_VIOLATIONS = [{ valid: true }];
|
|
1077
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1078
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1079
|
+
}
|
|
1080
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1081
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1082
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1083
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1084
|
+
const results = [];
|
|
1085
|
+
for (const attribute of requiredAttributes) {
|
|
1086
|
+
if (attribute in props) continue;
|
|
1087
|
+
results.push({
|
|
1088
|
+
valid: false,
|
|
1089
|
+
fixable: false,
|
|
1090
|
+
severity: "warning",
|
|
1091
|
+
attribute,
|
|
1092
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
return results;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1099
|
+
var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1100
|
+
["alert", "assertive"],
|
|
1101
|
+
["status", "polite"],
|
|
1102
|
+
["log", "polite"],
|
|
1103
|
+
["timer", "off"]
|
|
1104
|
+
]);
|
|
1105
|
+
var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1106
|
+
|
|
1107
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1108
|
+
var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1109
|
+
// Boolean (true | false)
|
|
1110
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1111
|
+
["aria-busy", { kind: "boolean" }],
|
|
1112
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1113
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1114
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1115
|
+
["aria-modal", { kind: "boolean" }],
|
|
1116
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1117
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1118
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1119
|
+
["aria-required", { kind: "boolean" }],
|
|
1120
|
+
["aria-selected", { kind: "boolean" }],
|
|
1121
|
+
// Tristate (true | false | mixed)
|
|
1122
|
+
["aria-checked", { kind: "tristate" }],
|
|
1123
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1124
|
+
// Numeric (any finite number)
|
|
1125
|
+
["aria-valuenow", { kind: "number" }],
|
|
1126
|
+
["aria-valuemin", { kind: "number" }],
|
|
1127
|
+
["aria-valuemax", { kind: "number" }],
|
|
1128
|
+
// Integer with optional range
|
|
1129
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1130
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1131
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1132
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1133
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1134
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1135
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1136
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1137
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1138
|
+
// Enum (specific allowed tokens)
|
|
1139
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1140
|
+
[
|
|
1141
|
+
"aria-current",
|
|
1142
|
+
{
|
|
1143
|
+
kind: "enum",
|
|
1144
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1145
|
+
}
|
|
1146
|
+
],
|
|
1147
|
+
[
|
|
1148
|
+
"aria-haspopup",
|
|
1149
|
+
{
|
|
1150
|
+
kind: "enum",
|
|
1151
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1152
|
+
}
|
|
1153
|
+
],
|
|
1154
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1155
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1156
|
+
["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
|
|
1157
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1158
|
+
]);
|
|
1159
|
+
|
|
1160
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1161
|
+
var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1162
|
+
"additions",
|
|
1163
|
+
"removals",
|
|
1164
|
+
"text",
|
|
1165
|
+
"all"
|
|
1166
|
+
]);
|
|
1167
|
+
|
|
1168
|
+
// ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1169
|
+
var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1170
|
+
["h1", 1],
|
|
1171
|
+
["h2", 2],
|
|
1172
|
+
["h3", 3],
|
|
1173
|
+
["h4", 4],
|
|
1174
|
+
["h5", 5],
|
|
1175
|
+
["h6", 6]
|
|
1176
|
+
]);
|
|
1177
|
+
|
|
1178
|
+
// ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
|
|
1179
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1180
|
+
"a",
|
|
1181
|
+
"button",
|
|
1182
|
+
"input",
|
|
1183
|
+
"select",
|
|
1184
|
+
"textarea"
|
|
1185
|
+
]);
|
|
1186
|
+
|
|
1187
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1188
|
+
var NO_VIOLATIONS2 = [{ valid: true }];
|
|
1061
1189
|
function isIntrinsicTag(tag) {
|
|
1062
1190
|
return isString(tag);
|
|
1063
1191
|
}
|
|
@@ -1119,6 +1247,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1119
1247
|
const violations = [];
|
|
1120
1248
|
const fixes = [];
|
|
1121
1249
|
iterate.forEach(rules, (rule) => {
|
|
1250
|
+
if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
|
|
1122
1251
|
iterate.forEach(rule(context), (result) => {
|
|
1123
1252
|
if (result.valid) return;
|
|
1124
1253
|
const {
|
|
@@ -1144,7 +1273,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1144
1273
|
return { violations, fixes };
|
|
1145
1274
|
}
|
|
1146
1275
|
static #getRules(context) {
|
|
1147
|
-
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) &&
|
|
1276
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1148
1277
|
return _AriaPolicyEngine.#pipeline;
|
|
1149
1278
|
}
|
|
1150
1279
|
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
@@ -1346,7 +1475,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1346
1475
|
implicitRole
|
|
1347
1476
|
}) {
|
|
1348
1477
|
const role = props.role;
|
|
1349
|
-
if (!implicitRole || !role || role === implicitRole) return
|
|
1478
|
+
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
|
|
1350
1479
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1351
1480
|
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1352
1481
|
return [
|
|
@@ -1359,11 +1488,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1359
1488
|
}
|
|
1360
1489
|
];
|
|
1361
1490
|
}
|
|
1362
|
-
return
|
|
1491
|
+
return NO_VIOLATIONS2;
|
|
1363
1492
|
}
|
|
1364
1493
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1365
1494
|
const role = props.role;
|
|
1366
|
-
if (!implicitRole || !role || role !== implicitRole) return
|
|
1495
|
+
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
|
|
1367
1496
|
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1368
1497
|
return [
|
|
1369
1498
|
{
|
|
@@ -1377,8 +1506,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1377
1506
|
}
|
|
1378
1507
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1379
1508
|
const role = props.role;
|
|
1380
|
-
if (role !== "region") return
|
|
1381
|
-
if (!isStandaloneTag(tag)) return
|
|
1509
|
+
if (role !== "region") return NO_VIOLATIONS2;
|
|
1510
|
+
if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
|
|
1382
1511
|
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1383
1512
|
return [
|
|
1384
1513
|
{
|
|
@@ -1395,7 +1524,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1395
1524
|
props,
|
|
1396
1525
|
effectiveRole
|
|
1397
1526
|
}) {
|
|
1398
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1527
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1399
1528
|
const results = [];
|
|
1400
1529
|
iterate.forEachEntry(props, (key) => {
|
|
1401
1530
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1413,62 +1542,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1413
1542
|
return results;
|
|
1414
1543
|
}
|
|
1415
1544
|
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1416
|
-
// Accepted value shapes for typed ARIA attributes.
|
|
1417
|
-
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1418
|
-
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1419
|
-
// Boolean (true | false)
|
|
1420
|
-
["aria-atomic", { kind: "boolean" }],
|
|
1421
|
-
["aria-busy", { kind: "boolean" }],
|
|
1422
|
-
["aria-disabled", { kind: "boolean" }],
|
|
1423
|
-
["aria-expanded", { kind: "boolean" }],
|
|
1424
|
-
["aria-hidden", { kind: "boolean" }],
|
|
1425
|
-
["aria-modal", { kind: "boolean" }],
|
|
1426
|
-
["aria-multiline", { kind: "boolean" }],
|
|
1427
|
-
["aria-multiselectable", { kind: "boolean" }],
|
|
1428
|
-
["aria-readonly", { kind: "boolean" }],
|
|
1429
|
-
["aria-required", { kind: "boolean" }],
|
|
1430
|
-
["aria-selected", { kind: "boolean" }],
|
|
1431
|
-
// Tristate (true | false | mixed)
|
|
1432
|
-
["aria-checked", { kind: "tristate" }],
|
|
1433
|
-
["aria-pressed", { kind: "tristate" }],
|
|
1434
|
-
// Numeric (any finite number)
|
|
1435
|
-
["aria-valuenow", { kind: "number" }],
|
|
1436
|
-
["aria-valuemin", { kind: "number" }],
|
|
1437
|
-
["aria-valuemax", { kind: "number" }],
|
|
1438
|
-
// Integer with optional range
|
|
1439
|
-
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1440
|
-
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1441
|
-
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1442
|
-
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1443
|
-
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1444
|
-
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1445
|
-
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1446
|
-
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1447
|
-
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1448
|
-
// Enum (specific allowed tokens)
|
|
1449
|
-
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1450
|
-
[
|
|
1451
|
-
"aria-current",
|
|
1452
|
-
{
|
|
1453
|
-
kind: "enum",
|
|
1454
|
-
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1455
|
-
}
|
|
1456
|
-
],
|
|
1457
|
-
[
|
|
1458
|
-
"aria-haspopup",
|
|
1459
|
-
{
|
|
1460
|
-
kind: "enum",
|
|
1461
|
-
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1462
|
-
}
|
|
1463
|
-
],
|
|
1464
|
-
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1465
|
-
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1466
|
-
[
|
|
1467
|
-
"aria-orientation",
|
|
1468
|
-
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1469
|
-
],
|
|
1470
|
-
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1471
|
-
]);
|
|
1472
1545
|
static #isValidAriaValue(value, type) {
|
|
1473
1546
|
switch (type.kind) {
|
|
1474
1547
|
case "boolean":
|
|
@@ -1513,11 +1586,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1513
1586
|
}
|
|
1514
1587
|
}
|
|
1515
1588
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1516
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1589
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1517
1590
|
const results = [];
|
|
1518
1591
|
iterate.forEachEntry(props, (key, value) => {
|
|
1519
1592
|
if (!key.startsWith("aria-")) return;
|
|
1520
|
-
const type =
|
|
1593
|
+
const type = ARIA_VALUE_TYPES.get(key);
|
|
1521
1594
|
if (!isNonNull(type)) return;
|
|
1522
1595
|
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1523
1596
|
results.push({
|
|
@@ -1536,26 +1609,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1536
1609
|
return results;
|
|
1537
1610
|
}
|
|
1538
1611
|
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1539
|
-
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1540
|
-
["h1", 1],
|
|
1541
|
-
["h2", 2],
|
|
1542
|
-
["h3", 3],
|
|
1543
|
-
["h4", 4],
|
|
1544
|
-
["h5", 5],
|
|
1545
|
-
["h6", 6]
|
|
1546
|
-
]);
|
|
1547
1612
|
static #checkRedundantAriaLevel({
|
|
1548
1613
|
tag,
|
|
1549
1614
|
props,
|
|
1550
1615
|
effectiveRole
|
|
1551
1616
|
}) {
|
|
1552
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1553
|
-
const implicitLevel =
|
|
1554
|
-
if (!isNonNull(implicitLevel)) return
|
|
1617
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1618
|
+
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1619
|
+
if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
|
|
1555
1620
|
const raw = props["aria-level"];
|
|
1556
|
-
if (!isNonNull(raw)) return
|
|
1621
|
+
if (!isNonNull(raw)) return NO_VIOLATIONS2;
|
|
1557
1622
|
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1558
|
-
if (!Number.isFinite(n) || n !== implicitLevel) return
|
|
1623
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
|
|
1559
1624
|
return [
|
|
1560
1625
|
{
|
|
1561
1626
|
valid: false,
|
|
@@ -1568,20 +1633,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1568
1633
|
];
|
|
1569
1634
|
}
|
|
1570
1635
|
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1571
|
-
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1572
|
-
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1573
|
-
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1574
|
-
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1575
|
-
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1576
1636
|
static #checkNameRequiredRoles({
|
|
1577
1637
|
tag,
|
|
1578
1638
|
props,
|
|
1579
1639
|
effectiveRole
|
|
1580
1640
|
}) {
|
|
1581
|
-
if (!effectiveRole || !
|
|
1582
|
-
|
|
1583
|
-
if ("
|
|
1584
|
-
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
|
|
1641
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
|
|
1642
|
+
if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
|
|
1643
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
|
|
1585
1644
|
return [
|
|
1586
1645
|
{
|
|
1587
1646
|
valid: false,
|
|
@@ -1591,51 +1650,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1591
1650
|
}
|
|
1592
1651
|
];
|
|
1593
1652
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1601
|
-
["spinbutton", ["aria-valuenow"]]
|
|
1602
|
-
]);
|
|
1603
|
-
static #checkRequiredAriaProperties({
|
|
1604
|
-
props,
|
|
1605
|
-
effectiveRole
|
|
1606
|
-
}) {
|
|
1607
|
-
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1608
|
-
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1609
|
-
if (!isNonNull(required)) return NO_VIOLATIONS;
|
|
1610
|
-
const results = [];
|
|
1611
|
-
iterate.forEach(required, (attr) => {
|
|
1612
|
-
if (attr in props) return;
|
|
1613
|
-
results.push({
|
|
1614
|
-
valid: false,
|
|
1615
|
-
fixable: false,
|
|
1616
|
-
severity: "warning",
|
|
1617
|
-
attribute: attr,
|
|
1618
|
-
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1619
|
-
});
|
|
1620
|
-
});
|
|
1621
|
-
return results;
|
|
1653
|
+
static #requiredAriaPropertiesRule = {
|
|
1654
|
+
attributesByRole: REQUIRED_ARIA_PROPERTIES,
|
|
1655
|
+
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
1656
|
+
};
|
|
1657
|
+
static #checkRequiredAriaProperties(context) {
|
|
1658
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
1622
1659
|
}
|
|
1623
|
-
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1624
|
-
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1625
|
-
"a",
|
|
1626
|
-
"button",
|
|
1627
|
-
"input",
|
|
1628
|
-
"select",
|
|
1629
|
-
"textarea"
|
|
1630
|
-
]);
|
|
1631
1660
|
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1632
1661
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1633
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
1634
|
-
const isInteractive =
|
|
1662
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
|
|
1663
|
+
const isInteractive = INTERACTIVE_TAGS.has(tag);
|
|
1635
1664
|
if (!isInteractive) {
|
|
1636
1665
|
const tabindex = props.tabindex;
|
|
1637
1666
|
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1638
|
-
if (!Number.isFinite(n) || n < 0) return
|
|
1667
|
+
if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
|
|
1639
1668
|
}
|
|
1640
1669
|
return [
|
|
1641
1670
|
{
|
|
@@ -1654,7 +1683,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1654
1683
|
props,
|
|
1655
1684
|
effectiveRole
|
|
1656
1685
|
}) {
|
|
1657
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
1686
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
|
|
1658
1687
|
const results = [];
|
|
1659
1688
|
iterate.forEachEntry(props, (key) => {
|
|
1660
1689
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1670,18 +1699,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1670
1699
|
});
|
|
1671
1700
|
return results;
|
|
1672
1701
|
}
|
|
1673
|
-
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1674
|
-
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1675
|
-
["alert", "assertive"],
|
|
1676
|
-
["status", "polite"],
|
|
1677
|
-
["log", "polite"],
|
|
1678
|
-
["timer", "off"]
|
|
1679
|
-
]);
|
|
1680
1702
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1681
|
-
if (!effectiveRole) return
|
|
1682
|
-
const impliedLive =
|
|
1683
|
-
if (!impliedLive) return
|
|
1684
|
-
if ("aria-live" in props) return
|
|
1703
|
+
if (!effectiveRole) return NO_VIOLATIONS2;
|
|
1704
|
+
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
1705
|
+
if (!impliedLive) return NO_VIOLATIONS2;
|
|
1706
|
+
if ("aria-live" in props) return NO_VIOLATIONS2;
|
|
1685
1707
|
const injectLive = {
|
|
1686
1708
|
kind: `injectLive:${effectiveRole}`,
|
|
1687
1709
|
apply: (ctx) => ({
|
|
@@ -1700,20 +1722,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1700
1722
|
}
|
|
1701
1723
|
];
|
|
1702
1724
|
}
|
|
1703
|
-
static #
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
valid: false,
|
|
1710
|
-
fixable: false,
|
|
1711
|
-
severity: "warning",
|
|
1712
|
-
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1713
|
-
}
|
|
1714
|
-
];
|
|
1725
|
+
static #missingAtomicRule = {
|
|
1726
|
+
attributesByRole: ATOMIC_REQUIREMENTS,
|
|
1727
|
+
diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
|
|
1728
|
+
};
|
|
1729
|
+
static #checkMissingAtomic(context) {
|
|
1730
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
|
|
1715
1731
|
}
|
|
1716
|
-
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1717
1732
|
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1718
1733
|
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1719
1734
|
// produce inconsistent results on cache hits.
|
|
@@ -1727,10 +1742,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1727
1742
|
};
|
|
1728
1743
|
static #checkInvalidAriaRelevant({ props }) {
|
|
1729
1744
|
const relevant = props["aria-relevant"];
|
|
1730
|
-
if (relevant === void 0) return
|
|
1731
|
-
if (typeof relevant !== "string") return
|
|
1745
|
+
if (relevant === void 0) return NO_VIOLATIONS2;
|
|
1746
|
+
if (typeof relevant !== "string") return NO_VIOLATIONS2;
|
|
1732
1747
|
const tokens = relevant.trim().split(/\s+/);
|
|
1733
|
-
const invalid = tokens.filter((t) => !
|
|
1748
|
+
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
1734
1749
|
if (invalid.length > 0) {
|
|
1735
1750
|
return [
|
|
1736
1751
|
{
|
|
@@ -1755,7 +1770,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1755
1770
|
}
|
|
1756
1771
|
];
|
|
1757
1772
|
}
|
|
1758
|
-
return
|
|
1773
|
+
return NO_VIOLATIONS2;
|
|
1759
1774
|
}
|
|
1760
1775
|
};
|
|
1761
1776
|
|
|
@@ -2075,7 +2090,59 @@ var readonlyProps = ({
|
|
|
2075
2090
|
// ../core/src/html/evaluators.ts
|
|
2076
2091
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
2077
2092
|
|
|
2078
|
-
// ../core/src/html/input
|
|
2093
|
+
// ../core/src/html/spec/vocabulary/input.ts
|
|
2094
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2095
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2096
|
+
"number",
|
|
2097
|
+
"range",
|
|
2098
|
+
"date",
|
|
2099
|
+
"month",
|
|
2100
|
+
"week",
|
|
2101
|
+
"time",
|
|
2102
|
+
"datetime-local"
|
|
2103
|
+
];
|
|
2104
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2105
|
+
...TEXT_INPUT_TYPES,
|
|
2106
|
+
...NUMERIC_INPUT_TYPES,
|
|
2107
|
+
"checkbox",
|
|
2108
|
+
"radio",
|
|
2109
|
+
"file",
|
|
2110
|
+
"color",
|
|
2111
|
+
"hidden",
|
|
2112
|
+
"button",
|
|
2113
|
+
"submit",
|
|
2114
|
+
"reset",
|
|
2115
|
+
"image"
|
|
2116
|
+
]);
|
|
2117
|
+
|
|
2118
|
+
// ../core/src/html/spec/attributes/input.ts
|
|
2119
|
+
var INPUT_ATTRIBUTE_TYPE_POLICIES = [
|
|
2120
|
+
{ attribute: "checked", allowedTypes: ["checkbox", "radio"] },
|
|
2121
|
+
{ attribute: "multiple", allowedTypes: ["email", "file"] },
|
|
2122
|
+
{ attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2123
|
+
{ attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2124
|
+
{ attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
|
|
2125
|
+
{ attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2126
|
+
{ attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2127
|
+
{ attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2128
|
+
{ attribute: "accept", allowedTypes: ["file"] },
|
|
2129
|
+
{ attribute: "capture", allowedTypes: ["file"] },
|
|
2130
|
+
{ attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
|
|
2131
|
+
{ attribute: "alt", allowedTypes: ["image"] },
|
|
2132
|
+
{ attribute: "height", allowedTypes: ["image"] },
|
|
2133
|
+
{ attribute: "width", allowedTypes: ["image"] }
|
|
2134
|
+
];
|
|
2135
|
+
|
|
2136
|
+
// ../core/src/html/spec/constraints/input.ts
|
|
2137
|
+
var REQUIRED_READONLY_CONFLICT = {
|
|
2138
|
+
props: ["required", "readOnly"],
|
|
2139
|
+
diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
|
|
2140
|
+
};
|
|
2141
|
+
var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
|
|
2142
|
+
REQUIRED_READONLY_CONFLICT
|
|
2143
|
+
];
|
|
2144
|
+
|
|
2145
|
+
// ../core/src/html/spec/validators/attribute-type-validator.ts
|
|
2079
2146
|
var DEFAULT_INPUT_TYPE = "text";
|
|
2080
2147
|
function omit(props, key) {
|
|
2081
2148
|
const next = { ...props };
|
|
@@ -2091,7 +2158,10 @@ function removeAttributeFix(attribute) {
|
|
|
2091
2158
|
}
|
|
2092
2159
|
};
|
|
2093
2160
|
}
|
|
2094
|
-
function
|
|
2161
|
+
function createInputAttributeTypeRule({
|
|
2162
|
+
attribute,
|
|
2163
|
+
allowedTypes
|
|
2164
|
+
}) {
|
|
2095
2165
|
const rule = ({ tag, props }) => {
|
|
2096
2166
|
if (tag !== "input" || !(attribute in props)) return [];
|
|
2097
2167
|
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
@@ -2107,31 +2177,30 @@ function inputAttributeRequiresType(attribute, allowedTypes) {
|
|
|
2107
2177
|
}
|
|
2108
2178
|
];
|
|
2109
2179
|
};
|
|
2110
|
-
return Object.assign(rule, { readsProps: ["type", attribute] });
|
|
2180
|
+
return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// ../core/src/html/spec/validators/mutually-exclusive-validator.ts
|
|
2184
|
+
function createMutuallyExclusiveRule({
|
|
2185
|
+
props: conflictingProps,
|
|
2186
|
+
diagnostic: createDiagnostic
|
|
2187
|
+
}) {
|
|
2188
|
+
const [first, second] = conflictingProps;
|
|
2189
|
+
const rule = ({ tag, props }) => {
|
|
2190
|
+
if (tag !== "input" || !props[first] || !props[second]) return [];
|
|
2191
|
+
const diagnostic = createDiagnostic();
|
|
2192
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2193
|
+
};
|
|
2194
|
+
return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
// ../core/src/html/input-rules.ts
|
|
2198
|
+
var policyByAttribute = Object.fromEntries(
|
|
2199
|
+
INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
|
|
2200
|
+
);
|
|
2201
|
+
function policyFor(attribute) {
|
|
2202
|
+
return policyByAttribute[attribute];
|
|
2111
2203
|
}
|
|
2112
|
-
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2113
|
-
var NUMERIC_INPUT_TYPES = [
|
|
2114
|
-
"number",
|
|
2115
|
-
"range",
|
|
2116
|
-
"date",
|
|
2117
|
-
"month",
|
|
2118
|
-
"week",
|
|
2119
|
-
"time",
|
|
2120
|
-
"datetime-local"
|
|
2121
|
-
];
|
|
2122
|
-
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2123
|
-
...TEXT_INPUT_TYPES,
|
|
2124
|
-
...NUMERIC_INPUT_TYPES,
|
|
2125
|
-
"checkbox",
|
|
2126
|
-
"radio",
|
|
2127
|
-
"file",
|
|
2128
|
-
"color",
|
|
2129
|
-
"hidden",
|
|
2130
|
-
"button",
|
|
2131
|
-
"submit",
|
|
2132
|
-
"reset",
|
|
2133
|
-
"image"
|
|
2134
|
-
]);
|
|
2135
2204
|
var supportedInputTypeRule = Object.assign(
|
|
2136
2205
|
({ tag, props }) => {
|
|
2137
2206
|
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
@@ -2147,30 +2216,22 @@ var supportedInputTypeRule = Object.assign(
|
|
|
2147
2216
|
}
|
|
2148
2217
|
];
|
|
2149
2218
|
},
|
|
2150
|
-
{ readsProps: ["type"] }
|
|
2151
|
-
);
|
|
2152
|
-
var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
|
|
2153
|
-
"checkbox",
|
|
2154
|
-
"radio"
|
|
2155
|
-
]);
|
|
2156
|
-
var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
|
|
2157
|
-
"email",
|
|
2158
|
-
"file"
|
|
2159
|
-
]);
|
|
2160
|
-
var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2161
|
-
"maxLength",
|
|
2162
|
-
TEXT_INPUT_TYPES
|
|
2219
|
+
{ readsProps: ["type"], tags: ["input"] }
|
|
2163
2220
|
);
|
|
2164
|
-
var
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
);
|
|
2168
|
-
var patternRequiresTextTypeRule =
|
|
2169
|
-
var minRequiresNumericTypeRule =
|
|
2170
|
-
var maxRequiresNumericTypeRule =
|
|
2171
|
-
var stepRequiresNumericTypeRule =
|
|
2172
|
-
var acceptRequiresFileTypeRule =
|
|
2173
|
-
var captureRequiresFileTypeRule =
|
|
2221
|
+
var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
|
|
2222
|
+
var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
|
|
2223
|
+
var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
|
|
2224
|
+
var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
|
|
2225
|
+
var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
|
|
2226
|
+
var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
|
|
2227
|
+
var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
|
|
2228
|
+
var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
|
|
2229
|
+
var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
|
|
2230
|
+
var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
|
|
2231
|
+
var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
|
|
2232
|
+
var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
|
|
2233
|
+
var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
|
|
2234
|
+
var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
|
|
2174
2235
|
var inputAccessibleNameRule = Object.assign(
|
|
2175
2236
|
({ tag, props }) => {
|
|
2176
2237
|
if (tag !== "input" || props.type === "hidden") return [];
|
|
@@ -2179,7 +2240,10 @@ var inputAccessibleNameRule = Object.assign(
|
|
|
2179
2240
|
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2180
2241
|
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2181
2242
|
},
|
|
2182
|
-
{
|
|
2243
|
+
{
|
|
2244
|
+
readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
|
|
2245
|
+
tags: ["input"]
|
|
2246
|
+
}
|
|
2183
2247
|
);
|
|
2184
2248
|
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2185
2249
|
var passwordAutocompleteRule = Object.assign(
|
|
@@ -2191,16 +2255,9 @@ var passwordAutocompleteRule = Object.assign(
|
|
|
2191
2255
|
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2192
2256
|
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2193
2257
|
},
|
|
2194
|
-
{ readsProps: ["type", "autoComplete"] }
|
|
2195
|
-
);
|
|
2196
|
-
var requiredReadOnlyConflictRule = Object.assign(
|
|
2197
|
-
({ tag, props }) => {
|
|
2198
|
-
if (tag !== "input" || !props.required || !props.readOnly) return [];
|
|
2199
|
-
const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
|
|
2200
|
-
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2201
|
-
},
|
|
2202
|
-
{ readsProps: ["required", "readOnly"] }
|
|
2258
|
+
{ readsProps: ["type", "autoComplete"], tags: ["input"] }
|
|
2203
2259
|
);
|
|
2260
|
+
var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
|
|
2204
2261
|
var INPUT_RULES = [
|
|
2205
2262
|
supportedInputTypeRule,
|
|
2206
2263
|
checkedRequiresCheckableTypeRule,
|
|
@@ -2213,11 +2270,132 @@ var INPUT_RULES = [
|
|
|
2213
2270
|
stepRequiresNumericTypeRule,
|
|
2214
2271
|
acceptRequiresFileTypeRule,
|
|
2215
2272
|
captureRequiresFileTypeRule,
|
|
2273
|
+
sizeRequiresTextTypeRule,
|
|
2274
|
+
altRequiresImageTypeRule,
|
|
2275
|
+
heightRequiresImageTypeRule,
|
|
2276
|
+
widthRequiresImageTypeRule,
|
|
2216
2277
|
inputAccessibleNameRule,
|
|
2217
2278
|
passwordAutocompleteRule,
|
|
2218
2279
|
requiredReadOnlyConflictRule
|
|
2219
2280
|
];
|
|
2220
2281
|
|
|
2282
|
+
// ../core/src/html/spec/types.ts
|
|
2283
|
+
function definePropRolePolicy(prop, map2, fallback) {
|
|
2284
|
+
return { kind: "byProp", prop, map: map2, fallback };
|
|
2285
|
+
}
|
|
2286
|
+
function resolveAllowedRoles(spec, props) {
|
|
2287
|
+
const policy = spec.allowedRoles;
|
|
2288
|
+
if (!policy) return void 0;
|
|
2289
|
+
switch (policy.kind) {
|
|
2290
|
+
case "fixed":
|
|
2291
|
+
return policy.roles;
|
|
2292
|
+
case "byProp": {
|
|
2293
|
+
const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
|
|
2294
|
+
return policy.map[value];
|
|
2295
|
+
}
|
|
2296
|
+
case "dynamic":
|
|
2297
|
+
return policy.resolve({ props });
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// ../core/src/html/spec/roles/input.ts
|
|
2302
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2303
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2304
|
+
radio: ["menuitemradio"],
|
|
2305
|
+
range: [],
|
|
2306
|
+
number: [],
|
|
2307
|
+
search: ["combobox"],
|
|
2308
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2309
|
+
email: ["combobox"],
|
|
2310
|
+
tel: ["combobox"],
|
|
2311
|
+
url: ["combobox"],
|
|
2312
|
+
button: [
|
|
2313
|
+
"link",
|
|
2314
|
+
"menuitem",
|
|
2315
|
+
"menuitemcheckbox",
|
|
2316
|
+
"menuitemradio",
|
|
2317
|
+
"option",
|
|
2318
|
+
"radio",
|
|
2319
|
+
"switch",
|
|
2320
|
+
"tab"
|
|
2321
|
+
],
|
|
2322
|
+
submit: [
|
|
2323
|
+
"link",
|
|
2324
|
+
"menuitem",
|
|
2325
|
+
"menuitemcheckbox",
|
|
2326
|
+
"menuitemradio",
|
|
2327
|
+
"option",
|
|
2328
|
+
"radio",
|
|
2329
|
+
"switch",
|
|
2330
|
+
"tab"
|
|
2331
|
+
],
|
|
2332
|
+
reset: [
|
|
2333
|
+
"link",
|
|
2334
|
+
"menuitem",
|
|
2335
|
+
"menuitemcheckbox",
|
|
2336
|
+
"menuitemradio",
|
|
2337
|
+
"option",
|
|
2338
|
+
"radio",
|
|
2339
|
+
"switch",
|
|
2340
|
+
"tab"
|
|
2341
|
+
],
|
|
2342
|
+
image: [
|
|
2343
|
+
"link",
|
|
2344
|
+
"menuitem",
|
|
2345
|
+
"menuitemcheckbox",
|
|
2346
|
+
"menuitemradio",
|
|
2347
|
+
"option",
|
|
2348
|
+
"radio",
|
|
2349
|
+
"switch",
|
|
2350
|
+
"tab"
|
|
2351
|
+
],
|
|
2352
|
+
hidden: []
|
|
2353
|
+
};
|
|
2354
|
+
|
|
2355
|
+
// ../core/src/html/spec/elements/input.ts
|
|
2356
|
+
var inputElementSpec = {
|
|
2357
|
+
tag: "input",
|
|
2358
|
+
allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
|
|
2359
|
+
attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
|
|
2360
|
+
mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// ../core/src/html/spec/roles/img.ts
|
|
2364
|
+
var IMG_NAMED_ROLES = [
|
|
2365
|
+
"button",
|
|
2366
|
+
"checkbox",
|
|
2367
|
+
"link",
|
|
2368
|
+
"menuitem",
|
|
2369
|
+
"menuitemcheckbox",
|
|
2370
|
+
"menuitemradio",
|
|
2371
|
+
"option",
|
|
2372
|
+
"progressbar",
|
|
2373
|
+
"scrollbar",
|
|
2374
|
+
"separator",
|
|
2375
|
+
"slider",
|
|
2376
|
+
"switch",
|
|
2377
|
+
"tab",
|
|
2378
|
+
"treeitem"
|
|
2379
|
+
];
|
|
2380
|
+
|
|
2381
|
+
// ../core/src/html/spec/elements/img.ts
|
|
2382
|
+
var imgElementSpec = {
|
|
2383
|
+
tag: "img",
|
|
2384
|
+
allowedRoles: {
|
|
2385
|
+
kind: "dynamic",
|
|
2386
|
+
resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
// ../core/src/html/spec/roles/table.ts
|
|
2391
|
+
var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
|
|
2392
|
+
|
|
2393
|
+
// ../core/src/html/spec/elements/table.ts
|
|
2394
|
+
var tableElementSpec = {
|
|
2395
|
+
tag: "table",
|
|
2396
|
+
allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
|
|
2397
|
+
};
|
|
2398
|
+
|
|
2221
2399
|
// ../core/src/html/role-restrictions.ts
|
|
2222
2400
|
var ALLOWED_ROLES = {
|
|
2223
2401
|
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
@@ -2290,86 +2468,17 @@ var ALLOWED_ROLES = {
|
|
|
2290
2468
|
"tab",
|
|
2291
2469
|
"treeitem"
|
|
2292
2470
|
],
|
|
2293
|
-
table: ["grid", "treegrid"],
|
|
2294
2471
|
dialog: ["alertdialog"],
|
|
2295
2472
|
fieldset: ["none", "presentation", "radiogroup"]
|
|
2296
2473
|
};
|
|
2297
|
-
var
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
"menuitem",
|
|
2302
|
-
"menuitemcheckbox",
|
|
2303
|
-
"menuitemradio",
|
|
2304
|
-
"option",
|
|
2305
|
-
"progressbar",
|
|
2306
|
-
"scrollbar",
|
|
2307
|
-
"separator",
|
|
2308
|
-
"slider",
|
|
2309
|
-
"switch",
|
|
2310
|
-
"tab",
|
|
2311
|
-
"treeitem"
|
|
2312
|
-
];
|
|
2313
|
-
var ALLOWED_INPUT_ROLES = {
|
|
2314
|
-
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2315
|
-
radio: ["menuitemradio"],
|
|
2316
|
-
range: [],
|
|
2317
|
-
number: [],
|
|
2318
|
-
search: ["combobox"],
|
|
2319
|
-
text: ["combobox", "searchbox", "spinbutton"],
|
|
2320
|
-
email: ["combobox"],
|
|
2321
|
-
tel: ["combobox"],
|
|
2322
|
-
url: ["combobox"],
|
|
2323
|
-
button: [
|
|
2324
|
-
"link",
|
|
2325
|
-
"menuitem",
|
|
2326
|
-
"menuitemcheckbox",
|
|
2327
|
-
"menuitemradio",
|
|
2328
|
-
"option",
|
|
2329
|
-
"radio",
|
|
2330
|
-
"switch",
|
|
2331
|
-
"tab"
|
|
2332
|
-
],
|
|
2333
|
-
submit: [
|
|
2334
|
-
"link",
|
|
2335
|
-
"menuitem",
|
|
2336
|
-
"menuitemcheckbox",
|
|
2337
|
-
"menuitemradio",
|
|
2338
|
-
"option",
|
|
2339
|
-
"radio",
|
|
2340
|
-
"switch",
|
|
2341
|
-
"tab"
|
|
2342
|
-
],
|
|
2343
|
-
reset: [
|
|
2344
|
-
"link",
|
|
2345
|
-
"menuitem",
|
|
2346
|
-
"menuitemcheckbox",
|
|
2347
|
-
"menuitemradio",
|
|
2348
|
-
"option",
|
|
2349
|
-
"radio",
|
|
2350
|
-
"switch",
|
|
2351
|
-
"tab"
|
|
2352
|
-
],
|
|
2353
|
-
image: [
|
|
2354
|
-
"link",
|
|
2355
|
-
"menuitem",
|
|
2356
|
-
"menuitemcheckbox",
|
|
2357
|
-
"menuitemradio",
|
|
2358
|
-
"option",
|
|
2359
|
-
"radio",
|
|
2360
|
-
"switch",
|
|
2361
|
-
"tab"
|
|
2362
|
-
],
|
|
2363
|
-
hidden: []
|
|
2474
|
+
var ELEMENT_SPECS = {
|
|
2475
|
+
input: inputElementSpec,
|
|
2476
|
+
img: imgElementSpec,
|
|
2477
|
+
table: tableElementSpec
|
|
2364
2478
|
};
|
|
2365
2479
|
function getAllowedRoles(tag, props) {
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
return ALLOWED_INPUT_ROLES[type];
|
|
2369
|
-
}
|
|
2370
|
-
if (tag === "img") {
|
|
2371
|
-
return props.alt === "" ? [] : IMG_NAMED_ROLES;
|
|
2372
|
-
}
|
|
2480
|
+
const spec = ELEMENT_SPECS[tag];
|
|
2481
|
+
if (spec) return resolveAllowedRoles(spec, props);
|
|
2373
2482
|
return ALLOWED_ROLES[tag];
|
|
2374
2483
|
}
|
|
2375
2484
|
var removeRoleFix = {
|
|
@@ -2410,21 +2519,24 @@ var removeLandmarkRoleOverride = {
|
|
|
2410
2519
|
return { applied: true, next: rest, previous: props };
|
|
2411
2520
|
}
|
|
2412
2521
|
};
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2522
|
+
var landmarkRoleRule = Object.assign(
|
|
2523
|
+
({ tag, props, implicitRole }) => {
|
|
2524
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2525
|
+
const role = props.role;
|
|
2526
|
+
if (!role || role === implicitRole) return [];
|
|
2527
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2528
|
+
return [
|
|
2529
|
+
{
|
|
2530
|
+
valid: false,
|
|
2531
|
+
fixable: true,
|
|
2532
|
+
severity: diagnostic.severity,
|
|
2533
|
+
fix: removeLandmarkRoleOverride,
|
|
2534
|
+
diagnostic
|
|
2535
|
+
}
|
|
2536
|
+
];
|
|
2537
|
+
},
|
|
2538
|
+
{ tags: [...LANDMARK_TAG_SET] }
|
|
2539
|
+
);
|
|
2428
2540
|
function requireAccessibleName({ tag, props }) {
|
|
2429
2541
|
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2430
2542
|
return [
|
|
@@ -2437,10 +2549,13 @@ function requireAccessibleName({ tag, props }) {
|
|
|
2437
2549
|
];
|
|
2438
2550
|
}
|
|
2439
2551
|
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2552
|
+
var landmarkNameAdvisory = Object.assign(
|
|
2553
|
+
(ctx) => {
|
|
2554
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2555
|
+
return requireAccessibleName(ctx);
|
|
2556
|
+
},
|
|
2557
|
+
{ tags: [...NAMED_LANDMARK_TAGS] }
|
|
2558
|
+
);
|
|
2444
2559
|
var HTML_ARIA_RULES = [
|
|
2445
2560
|
landmarkRoleRule,
|
|
2446
2561
|
landmarkNameAdvisory,
|
|
@@ -2541,6 +2656,62 @@ var figureContract = contract([
|
|
|
2541
2656
|
]);
|
|
2542
2657
|
var detailsContract = firstChildContract("summary", "summary");
|
|
2543
2658
|
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2659
|
+
var objectContract = contract([
|
|
2660
|
+
{ name: "param", match: isTag("param") },
|
|
2661
|
+
{ name: "content", match: isOpenContent("param") }
|
|
2662
|
+
]);
|
|
2663
|
+
var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
|
|
2664
|
+
var buttonContract = closedContract([
|
|
2665
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2666
|
+
]);
|
|
2667
|
+
var anchorContract = closedContract([
|
|
2668
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2669
|
+
]);
|
|
2670
|
+
var LABELABLE_TAGS = [
|
|
2671
|
+
"button",
|
|
2672
|
+
"input",
|
|
2673
|
+
"meter",
|
|
2674
|
+
"output",
|
|
2675
|
+
"progress",
|
|
2676
|
+
"select",
|
|
2677
|
+
"textarea"
|
|
2678
|
+
];
|
|
2679
|
+
var labelContract = contract([
|
|
2680
|
+
{ name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
|
|
2681
|
+
]);
|
|
2682
|
+
var P_BLOCKED_TAGS = [
|
|
2683
|
+
"address",
|
|
2684
|
+
"article",
|
|
2685
|
+
"aside",
|
|
2686
|
+
"blockquote",
|
|
2687
|
+
"details",
|
|
2688
|
+
"dialog",
|
|
2689
|
+
"div",
|
|
2690
|
+
"dl",
|
|
2691
|
+
"fieldset",
|
|
2692
|
+
"figure",
|
|
2693
|
+
"footer",
|
|
2694
|
+
"form",
|
|
2695
|
+
"h1",
|
|
2696
|
+
"h2",
|
|
2697
|
+
"h3",
|
|
2698
|
+
"h4",
|
|
2699
|
+
"h5",
|
|
2700
|
+
"h6",
|
|
2701
|
+
"header",
|
|
2702
|
+
"hr",
|
|
2703
|
+
"main",
|
|
2704
|
+
"nav",
|
|
2705
|
+
"ol",
|
|
2706
|
+
"p",
|
|
2707
|
+
"pre",
|
|
2708
|
+
"section",
|
|
2709
|
+
"table",
|
|
2710
|
+
"ul"
|
|
2711
|
+
];
|
|
2712
|
+
var pContract = closedContract([
|
|
2713
|
+
{ name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
|
|
2714
|
+
]);
|
|
2544
2715
|
var mediaContract = contract([
|
|
2545
2716
|
{ name: "source", match: isTag("source") },
|
|
2546
2717
|
{ name: "track", match: isTag("track") },
|
|
@@ -2595,6 +2766,11 @@ var htmlContracts = {
|
|
|
2595
2766
|
details: detailsContract,
|
|
2596
2767
|
fieldset: fieldsetContract,
|
|
2597
2768
|
dialog: dialogContract,
|
|
2769
|
+
object: objectContract,
|
|
2770
|
+
button: buttonContract,
|
|
2771
|
+
a: anchorContract,
|
|
2772
|
+
label: labelContract,
|
|
2773
|
+
p: pContract,
|
|
2598
2774
|
head: headContract,
|
|
2599
2775
|
html: htmlContract
|
|
2600
2776
|
};
|