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
|
@@ -1003,7 +1003,11 @@ var ATTRIBUTE_IGNORED_CODES = {
|
|
|
1003
1003
|
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
1004
1004
|
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
1005
1005
|
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
1006
|
-
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType
|
|
1006
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
|
|
1007
|
+
size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
|
|
1008
|
+
alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
|
|
1009
|
+
height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
|
|
1010
|
+
width: DiagnosticCode3.HtmlInputWidthIgnoredForType
|
|
1007
1011
|
};
|
|
1008
1012
|
var HtmlDiagnostics = {
|
|
1009
1013
|
emptyRole(tag) {
|
|
@@ -1237,8 +1241,132 @@ var InvariantBase = class {
|
|
|
1237
1241
|
}
|
|
1238
1242
|
};
|
|
1239
1243
|
|
|
1240
|
-
// ../../lib/contract/src/aria/
|
|
1244
|
+
// ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1245
|
+
var REQUIRED_ARIA_PROPERTIES = {
|
|
1246
|
+
combobox: ["aria-expanded"],
|
|
1247
|
+
option: ["aria-selected"],
|
|
1248
|
+
slider: ["aria-valuenow"],
|
|
1249
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1250
|
+
spinbutton: ["aria-valuenow"]
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
// ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1254
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1255
|
+
|
|
1256
|
+
// ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1241
1257
|
var NO_VIOLATIONS = [{ valid: true }];
|
|
1258
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1259
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1260
|
+
}
|
|
1261
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1262
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1263
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1264
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1265
|
+
const results = [];
|
|
1266
|
+
for (const attribute of requiredAttributes) {
|
|
1267
|
+
if (attribute in props) continue;
|
|
1268
|
+
results.push({
|
|
1269
|
+
valid: false,
|
|
1270
|
+
fixable: false,
|
|
1271
|
+
severity: "warning",
|
|
1272
|
+
attribute,
|
|
1273
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
return results;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1280
|
+
var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1281
|
+
["alert", "assertive"],
|
|
1282
|
+
["status", "polite"],
|
|
1283
|
+
["log", "polite"],
|
|
1284
|
+
["timer", "off"]
|
|
1285
|
+
]);
|
|
1286
|
+
var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1287
|
+
|
|
1288
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1289
|
+
var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1290
|
+
// Boolean (true | false)
|
|
1291
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1292
|
+
["aria-busy", { kind: "boolean" }],
|
|
1293
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1294
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1295
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1296
|
+
["aria-modal", { kind: "boolean" }],
|
|
1297
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1298
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1299
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1300
|
+
["aria-required", { kind: "boolean" }],
|
|
1301
|
+
["aria-selected", { kind: "boolean" }],
|
|
1302
|
+
// Tristate (true | false | mixed)
|
|
1303
|
+
["aria-checked", { kind: "tristate" }],
|
|
1304
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1305
|
+
// Numeric (any finite number)
|
|
1306
|
+
["aria-valuenow", { kind: "number" }],
|
|
1307
|
+
["aria-valuemin", { kind: "number" }],
|
|
1308
|
+
["aria-valuemax", { kind: "number" }],
|
|
1309
|
+
// Integer with optional range
|
|
1310
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1311
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1312
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1313
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1314
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1315
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1316
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1317
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1318
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1319
|
+
// Enum (specific allowed tokens)
|
|
1320
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1321
|
+
[
|
|
1322
|
+
"aria-current",
|
|
1323
|
+
{
|
|
1324
|
+
kind: "enum",
|
|
1325
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1326
|
+
}
|
|
1327
|
+
],
|
|
1328
|
+
[
|
|
1329
|
+
"aria-haspopup",
|
|
1330
|
+
{
|
|
1331
|
+
kind: "enum",
|
|
1332
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1333
|
+
}
|
|
1334
|
+
],
|
|
1335
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1336
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1337
|
+
["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
|
|
1338
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1339
|
+
]);
|
|
1340
|
+
|
|
1341
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1342
|
+
var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1343
|
+
"additions",
|
|
1344
|
+
"removals",
|
|
1345
|
+
"text",
|
|
1346
|
+
"all"
|
|
1347
|
+
]);
|
|
1348
|
+
|
|
1349
|
+
// ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1350
|
+
var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1351
|
+
["h1", 1],
|
|
1352
|
+
["h2", 2],
|
|
1353
|
+
["h3", 3],
|
|
1354
|
+
["h4", 4],
|
|
1355
|
+
["h5", 5],
|
|
1356
|
+
["h6", 6]
|
|
1357
|
+
]);
|
|
1358
|
+
|
|
1359
|
+
// ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
|
|
1360
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1361
|
+
"a",
|
|
1362
|
+
"button",
|
|
1363
|
+
"input",
|
|
1364
|
+
"select",
|
|
1365
|
+
"textarea"
|
|
1366
|
+
]);
|
|
1367
|
+
|
|
1368
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1369
|
+
var NO_VIOLATIONS2 = [{ valid: true }];
|
|
1242
1370
|
function isIntrinsicTag(tag) {
|
|
1243
1371
|
return isString(tag);
|
|
1244
1372
|
}
|
|
@@ -1300,6 +1428,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1300
1428
|
const violations = [];
|
|
1301
1429
|
const fixes = [];
|
|
1302
1430
|
iterate.forEach(rules, (rule) => {
|
|
1431
|
+
if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
|
|
1303
1432
|
iterate.forEach(rule(context), (result) => {
|
|
1304
1433
|
if (result.valid) return;
|
|
1305
1434
|
const {
|
|
@@ -1325,7 +1454,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1325
1454
|
return { violations, fixes };
|
|
1326
1455
|
}
|
|
1327
1456
|
static #getRules(context) {
|
|
1328
|
-
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) &&
|
|
1457
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1329
1458
|
return _AriaPolicyEngine.#pipeline;
|
|
1330
1459
|
}
|
|
1331
1460
|
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
@@ -1527,7 +1656,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1527
1656
|
implicitRole
|
|
1528
1657
|
}) {
|
|
1529
1658
|
const role = props.role;
|
|
1530
|
-
if (!implicitRole || !role || role === implicitRole) return
|
|
1659
|
+
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
|
|
1531
1660
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1532
1661
|
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1533
1662
|
return [
|
|
@@ -1540,11 +1669,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1540
1669
|
}
|
|
1541
1670
|
];
|
|
1542
1671
|
}
|
|
1543
|
-
return
|
|
1672
|
+
return NO_VIOLATIONS2;
|
|
1544
1673
|
}
|
|
1545
1674
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1546
1675
|
const role = props.role;
|
|
1547
|
-
if (!implicitRole || !role || role !== implicitRole) return
|
|
1676
|
+
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
|
|
1548
1677
|
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1549
1678
|
return [
|
|
1550
1679
|
{
|
|
@@ -1558,8 +1687,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1558
1687
|
}
|
|
1559
1688
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1560
1689
|
const role = props.role;
|
|
1561
|
-
if (role !== "region") return
|
|
1562
|
-
if (!isStandaloneTag(tag)) return
|
|
1690
|
+
if (role !== "region") return NO_VIOLATIONS2;
|
|
1691
|
+
if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
|
|
1563
1692
|
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1564
1693
|
return [
|
|
1565
1694
|
{
|
|
@@ -1576,7 +1705,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1576
1705
|
props,
|
|
1577
1706
|
effectiveRole
|
|
1578
1707
|
}) {
|
|
1579
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1708
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1580
1709
|
const results = [];
|
|
1581
1710
|
iterate.forEachEntry(props, (key) => {
|
|
1582
1711
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1594,62 +1723,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1594
1723
|
return results;
|
|
1595
1724
|
}
|
|
1596
1725
|
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1597
|
-
// Accepted value shapes for typed ARIA attributes.
|
|
1598
|
-
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1599
|
-
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1600
|
-
// Boolean (true | false)
|
|
1601
|
-
["aria-atomic", { kind: "boolean" }],
|
|
1602
|
-
["aria-busy", { kind: "boolean" }],
|
|
1603
|
-
["aria-disabled", { kind: "boolean" }],
|
|
1604
|
-
["aria-expanded", { kind: "boolean" }],
|
|
1605
|
-
["aria-hidden", { kind: "boolean" }],
|
|
1606
|
-
["aria-modal", { kind: "boolean" }],
|
|
1607
|
-
["aria-multiline", { kind: "boolean" }],
|
|
1608
|
-
["aria-multiselectable", { kind: "boolean" }],
|
|
1609
|
-
["aria-readonly", { kind: "boolean" }],
|
|
1610
|
-
["aria-required", { kind: "boolean" }],
|
|
1611
|
-
["aria-selected", { kind: "boolean" }],
|
|
1612
|
-
// Tristate (true | false | mixed)
|
|
1613
|
-
["aria-checked", { kind: "tristate" }],
|
|
1614
|
-
["aria-pressed", { kind: "tristate" }],
|
|
1615
|
-
// Numeric (any finite number)
|
|
1616
|
-
["aria-valuenow", { kind: "number" }],
|
|
1617
|
-
["aria-valuemin", { kind: "number" }],
|
|
1618
|
-
["aria-valuemax", { kind: "number" }],
|
|
1619
|
-
// Integer with optional range
|
|
1620
|
-
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1621
|
-
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1622
|
-
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1623
|
-
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1624
|
-
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1625
|
-
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1626
|
-
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1627
|
-
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1628
|
-
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1629
|
-
// Enum (specific allowed tokens)
|
|
1630
|
-
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1631
|
-
[
|
|
1632
|
-
"aria-current",
|
|
1633
|
-
{
|
|
1634
|
-
kind: "enum",
|
|
1635
|
-
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1636
|
-
}
|
|
1637
|
-
],
|
|
1638
|
-
[
|
|
1639
|
-
"aria-haspopup",
|
|
1640
|
-
{
|
|
1641
|
-
kind: "enum",
|
|
1642
|
-
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1643
|
-
}
|
|
1644
|
-
],
|
|
1645
|
-
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1646
|
-
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1647
|
-
[
|
|
1648
|
-
"aria-orientation",
|
|
1649
|
-
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1650
|
-
],
|
|
1651
|
-
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1652
|
-
]);
|
|
1653
1726
|
static #isValidAriaValue(value, type) {
|
|
1654
1727
|
switch (type.kind) {
|
|
1655
1728
|
case "boolean":
|
|
@@ -1694,11 +1767,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1694
1767
|
}
|
|
1695
1768
|
}
|
|
1696
1769
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1697
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1770
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1698
1771
|
const results = [];
|
|
1699
1772
|
iterate.forEachEntry(props, (key, value) => {
|
|
1700
1773
|
if (!key.startsWith("aria-")) return;
|
|
1701
|
-
const type =
|
|
1774
|
+
const type = ARIA_VALUE_TYPES.get(key);
|
|
1702
1775
|
if (!isNonNull(type)) return;
|
|
1703
1776
|
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1704
1777
|
results.push({
|
|
@@ -1717,26 +1790,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1717
1790
|
return results;
|
|
1718
1791
|
}
|
|
1719
1792
|
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1720
|
-
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1721
|
-
["h1", 1],
|
|
1722
|
-
["h2", 2],
|
|
1723
|
-
["h3", 3],
|
|
1724
|
-
["h4", 4],
|
|
1725
|
-
["h5", 5],
|
|
1726
|
-
["h6", 6]
|
|
1727
|
-
]);
|
|
1728
1793
|
static #checkRedundantAriaLevel({
|
|
1729
1794
|
tag,
|
|
1730
1795
|
props,
|
|
1731
1796
|
effectiveRole
|
|
1732
1797
|
}) {
|
|
1733
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1734
|
-
const implicitLevel =
|
|
1735
|
-
if (!isNonNull(implicitLevel)) return
|
|
1798
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1799
|
+
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1800
|
+
if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
|
|
1736
1801
|
const raw = props["aria-level"];
|
|
1737
|
-
if (!isNonNull(raw)) return
|
|
1802
|
+
if (!isNonNull(raw)) return NO_VIOLATIONS2;
|
|
1738
1803
|
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1739
|
-
if (!Number.isFinite(n) || n !== implicitLevel) return
|
|
1804
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
|
|
1740
1805
|
return [
|
|
1741
1806
|
{
|
|
1742
1807
|
valid: false,
|
|
@@ -1749,20 +1814,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1749
1814
|
];
|
|
1750
1815
|
}
|
|
1751
1816
|
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1752
|
-
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1753
|
-
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1754
|
-
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1755
|
-
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1756
|
-
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1757
1817
|
static #checkNameRequiredRoles({
|
|
1758
1818
|
tag,
|
|
1759
1819
|
props,
|
|
1760
1820
|
effectiveRole
|
|
1761
1821
|
}) {
|
|
1762
|
-
if (!effectiveRole || !
|
|
1763
|
-
|
|
1764
|
-
if ("
|
|
1765
|
-
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
|
|
1822
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
|
|
1823
|
+
if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
|
|
1824
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
|
|
1766
1825
|
return [
|
|
1767
1826
|
{
|
|
1768
1827
|
valid: false,
|
|
@@ -1772,51 +1831,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1772
1831
|
}
|
|
1773
1832
|
];
|
|
1774
1833
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1782
|
-
["spinbutton", ["aria-valuenow"]]
|
|
1783
|
-
]);
|
|
1784
|
-
static #checkRequiredAriaProperties({
|
|
1785
|
-
props,
|
|
1786
|
-
effectiveRole
|
|
1787
|
-
}) {
|
|
1788
|
-
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1789
|
-
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1790
|
-
if (!isNonNull(required)) return NO_VIOLATIONS;
|
|
1791
|
-
const results = [];
|
|
1792
|
-
iterate.forEach(required, (attr) => {
|
|
1793
|
-
if (attr in props) return;
|
|
1794
|
-
results.push({
|
|
1795
|
-
valid: false,
|
|
1796
|
-
fixable: false,
|
|
1797
|
-
severity: "warning",
|
|
1798
|
-
attribute: attr,
|
|
1799
|
-
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1800
|
-
});
|
|
1801
|
-
});
|
|
1802
|
-
return results;
|
|
1834
|
+
static #requiredAriaPropertiesRule = {
|
|
1835
|
+
attributesByRole: REQUIRED_ARIA_PROPERTIES,
|
|
1836
|
+
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
1837
|
+
};
|
|
1838
|
+
static #checkRequiredAriaProperties(context) {
|
|
1839
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
1803
1840
|
}
|
|
1804
|
-
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1805
|
-
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1806
|
-
"a",
|
|
1807
|
-
"button",
|
|
1808
|
-
"input",
|
|
1809
|
-
"select",
|
|
1810
|
-
"textarea"
|
|
1811
|
-
]);
|
|
1812
1841
|
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1813
1842
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1814
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
1815
|
-
const isInteractive =
|
|
1843
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
|
|
1844
|
+
const isInteractive = INTERACTIVE_TAGS.has(tag);
|
|
1816
1845
|
if (!isInteractive) {
|
|
1817
1846
|
const tabindex = props.tabindex;
|
|
1818
1847
|
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1819
|
-
if (!Number.isFinite(n) || n < 0) return
|
|
1848
|
+
if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
|
|
1820
1849
|
}
|
|
1821
1850
|
return [
|
|
1822
1851
|
{
|
|
@@ -1835,7 +1864,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1835
1864
|
props,
|
|
1836
1865
|
effectiveRole
|
|
1837
1866
|
}) {
|
|
1838
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
1867
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
|
|
1839
1868
|
const results = [];
|
|
1840
1869
|
iterate.forEachEntry(props, (key) => {
|
|
1841
1870
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1851,18 +1880,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1851
1880
|
});
|
|
1852
1881
|
return results;
|
|
1853
1882
|
}
|
|
1854
|
-
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1855
|
-
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1856
|
-
["alert", "assertive"],
|
|
1857
|
-
["status", "polite"],
|
|
1858
|
-
["log", "polite"],
|
|
1859
|
-
["timer", "off"]
|
|
1860
|
-
]);
|
|
1861
1883
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1862
|
-
if (!effectiveRole) return
|
|
1863
|
-
const impliedLive =
|
|
1864
|
-
if (!impliedLive) return
|
|
1865
|
-
if ("aria-live" in props) return
|
|
1884
|
+
if (!effectiveRole) return NO_VIOLATIONS2;
|
|
1885
|
+
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
1886
|
+
if (!impliedLive) return NO_VIOLATIONS2;
|
|
1887
|
+
if ("aria-live" in props) return NO_VIOLATIONS2;
|
|
1866
1888
|
const injectLive = {
|
|
1867
1889
|
kind: `injectLive:${effectiveRole}`,
|
|
1868
1890
|
apply: (ctx) => ({
|
|
@@ -1881,20 +1903,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1881
1903
|
}
|
|
1882
1904
|
];
|
|
1883
1905
|
}
|
|
1884
|
-
static #
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
valid: false,
|
|
1891
|
-
fixable: false,
|
|
1892
|
-
severity: "warning",
|
|
1893
|
-
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1894
|
-
}
|
|
1895
|
-
];
|
|
1906
|
+
static #missingAtomicRule = {
|
|
1907
|
+
attributesByRole: ATOMIC_REQUIREMENTS,
|
|
1908
|
+
diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
|
|
1909
|
+
};
|
|
1910
|
+
static #checkMissingAtomic(context) {
|
|
1911
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
|
|
1896
1912
|
}
|
|
1897
|
-
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1898
1913
|
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1899
1914
|
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1900
1915
|
// produce inconsistent results on cache hits.
|
|
@@ -1908,10 +1923,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1908
1923
|
};
|
|
1909
1924
|
static #checkInvalidAriaRelevant({ props }) {
|
|
1910
1925
|
const relevant = props["aria-relevant"];
|
|
1911
|
-
if (relevant === void 0) return
|
|
1912
|
-
if (typeof relevant !== "string") return
|
|
1926
|
+
if (relevant === void 0) return NO_VIOLATIONS2;
|
|
1927
|
+
if (typeof relevant !== "string") return NO_VIOLATIONS2;
|
|
1913
1928
|
const tokens = relevant.trim().split(/\s+/);
|
|
1914
|
-
const invalid = tokens.filter((t) => !
|
|
1929
|
+
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
1915
1930
|
if (invalid.length > 0) {
|
|
1916
1931
|
return [
|
|
1917
1932
|
{
|
|
@@ -1936,7 +1951,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1936
1951
|
}
|
|
1937
1952
|
];
|
|
1938
1953
|
}
|
|
1939
|
-
return
|
|
1954
|
+
return NO_VIOLATIONS2;
|
|
1940
1955
|
}
|
|
1941
1956
|
};
|
|
1942
1957
|
|
|
@@ -2256,7 +2271,59 @@ var readonlyProps = ({
|
|
|
2256
2271
|
// ../core/src/html/evaluators.ts
|
|
2257
2272
|
import { warnDiagnostics as warnDiagnostics2 } from "./_shared/diagnostics.js";
|
|
2258
2273
|
|
|
2259
|
-
// ../core/src/html/input
|
|
2274
|
+
// ../core/src/html/spec/vocabulary/input.ts
|
|
2275
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2276
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2277
|
+
"number",
|
|
2278
|
+
"range",
|
|
2279
|
+
"date",
|
|
2280
|
+
"month",
|
|
2281
|
+
"week",
|
|
2282
|
+
"time",
|
|
2283
|
+
"datetime-local"
|
|
2284
|
+
];
|
|
2285
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2286
|
+
...TEXT_INPUT_TYPES,
|
|
2287
|
+
...NUMERIC_INPUT_TYPES,
|
|
2288
|
+
"checkbox",
|
|
2289
|
+
"radio",
|
|
2290
|
+
"file",
|
|
2291
|
+
"color",
|
|
2292
|
+
"hidden",
|
|
2293
|
+
"button",
|
|
2294
|
+
"submit",
|
|
2295
|
+
"reset",
|
|
2296
|
+
"image"
|
|
2297
|
+
]);
|
|
2298
|
+
|
|
2299
|
+
// ../core/src/html/spec/attributes/input.ts
|
|
2300
|
+
var INPUT_ATTRIBUTE_TYPE_POLICIES = [
|
|
2301
|
+
{ attribute: "checked", allowedTypes: ["checkbox", "radio"] },
|
|
2302
|
+
{ attribute: "multiple", allowedTypes: ["email", "file"] },
|
|
2303
|
+
{ attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2304
|
+
{ attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2305
|
+
{ attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
|
|
2306
|
+
{ attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2307
|
+
{ attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2308
|
+
{ attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2309
|
+
{ attribute: "accept", allowedTypes: ["file"] },
|
|
2310
|
+
{ attribute: "capture", allowedTypes: ["file"] },
|
|
2311
|
+
{ attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
|
|
2312
|
+
{ attribute: "alt", allowedTypes: ["image"] },
|
|
2313
|
+
{ attribute: "height", allowedTypes: ["image"] },
|
|
2314
|
+
{ attribute: "width", allowedTypes: ["image"] }
|
|
2315
|
+
];
|
|
2316
|
+
|
|
2317
|
+
// ../core/src/html/spec/constraints/input.ts
|
|
2318
|
+
var REQUIRED_READONLY_CONFLICT = {
|
|
2319
|
+
props: ["required", "readOnly"],
|
|
2320
|
+
diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
|
|
2321
|
+
};
|
|
2322
|
+
var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
|
|
2323
|
+
REQUIRED_READONLY_CONFLICT
|
|
2324
|
+
];
|
|
2325
|
+
|
|
2326
|
+
// ../core/src/html/spec/validators/attribute-type-validator.ts
|
|
2260
2327
|
var DEFAULT_INPUT_TYPE = "text";
|
|
2261
2328
|
function omit(props, key) {
|
|
2262
2329
|
const next = { ...props };
|
|
@@ -2272,7 +2339,10 @@ function removeAttributeFix(attribute) {
|
|
|
2272
2339
|
}
|
|
2273
2340
|
};
|
|
2274
2341
|
}
|
|
2275
|
-
function
|
|
2342
|
+
function createInputAttributeTypeRule({
|
|
2343
|
+
attribute,
|
|
2344
|
+
allowedTypes
|
|
2345
|
+
}) {
|
|
2276
2346
|
const rule = ({ tag, props }) => {
|
|
2277
2347
|
if (tag !== "input" || !(attribute in props)) return [];
|
|
2278
2348
|
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
@@ -2288,31 +2358,30 @@ function inputAttributeRequiresType(attribute, allowedTypes) {
|
|
|
2288
2358
|
}
|
|
2289
2359
|
];
|
|
2290
2360
|
};
|
|
2291
|
-
return Object.assign(rule, { readsProps: ["type", attribute] });
|
|
2361
|
+
return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
// ../core/src/html/spec/validators/mutually-exclusive-validator.ts
|
|
2365
|
+
function createMutuallyExclusiveRule({
|
|
2366
|
+
props: conflictingProps,
|
|
2367
|
+
diagnostic: createDiagnostic
|
|
2368
|
+
}) {
|
|
2369
|
+
const [first, second] = conflictingProps;
|
|
2370
|
+
const rule = ({ tag, props }) => {
|
|
2371
|
+
if (tag !== "input" || !props[first] || !props[second]) return [];
|
|
2372
|
+
const diagnostic = createDiagnostic();
|
|
2373
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2374
|
+
};
|
|
2375
|
+
return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
// ../core/src/html/input-rules.ts
|
|
2379
|
+
var policyByAttribute = Object.fromEntries(
|
|
2380
|
+
INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
|
|
2381
|
+
);
|
|
2382
|
+
function policyFor(attribute) {
|
|
2383
|
+
return policyByAttribute[attribute];
|
|
2292
2384
|
}
|
|
2293
|
-
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2294
|
-
var NUMERIC_INPUT_TYPES = [
|
|
2295
|
-
"number",
|
|
2296
|
-
"range",
|
|
2297
|
-
"date",
|
|
2298
|
-
"month",
|
|
2299
|
-
"week",
|
|
2300
|
-
"time",
|
|
2301
|
-
"datetime-local"
|
|
2302
|
-
];
|
|
2303
|
-
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2304
|
-
...TEXT_INPUT_TYPES,
|
|
2305
|
-
...NUMERIC_INPUT_TYPES,
|
|
2306
|
-
"checkbox",
|
|
2307
|
-
"radio",
|
|
2308
|
-
"file",
|
|
2309
|
-
"color",
|
|
2310
|
-
"hidden",
|
|
2311
|
-
"button",
|
|
2312
|
-
"submit",
|
|
2313
|
-
"reset",
|
|
2314
|
-
"image"
|
|
2315
|
-
]);
|
|
2316
2385
|
var supportedInputTypeRule = Object.assign(
|
|
2317
2386
|
({ tag, props }) => {
|
|
2318
2387
|
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
@@ -2328,30 +2397,22 @@ var supportedInputTypeRule = Object.assign(
|
|
|
2328
2397
|
}
|
|
2329
2398
|
];
|
|
2330
2399
|
},
|
|
2331
|
-
{ readsProps: ["type"] }
|
|
2400
|
+
{ readsProps: ["type"], tags: ["input"] }
|
|
2332
2401
|
);
|
|
2333
|
-
var checkedRequiresCheckableTypeRule =
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
var
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
var
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
);
|
|
2345
|
-
var
|
|
2346
|
-
|
|
2347
|
-
TEXT_INPUT_TYPES
|
|
2348
|
-
);
|
|
2349
|
-
var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
|
|
2350
|
-
var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
|
|
2351
|
-
var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
|
|
2352
|
-
var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
|
|
2353
|
-
var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
|
|
2354
|
-
var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
|
|
2402
|
+
var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
|
|
2403
|
+
var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
|
|
2404
|
+
var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
|
|
2405
|
+
var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
|
|
2406
|
+
var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
|
|
2407
|
+
var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
|
|
2408
|
+
var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
|
|
2409
|
+
var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
|
|
2410
|
+
var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
|
|
2411
|
+
var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
|
|
2412
|
+
var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
|
|
2413
|
+
var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
|
|
2414
|
+
var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
|
|
2415
|
+
var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
|
|
2355
2416
|
var inputAccessibleNameRule = Object.assign(
|
|
2356
2417
|
({ tag, props }) => {
|
|
2357
2418
|
if (tag !== "input" || props.type === "hidden") return [];
|
|
@@ -2360,7 +2421,10 @@ var inputAccessibleNameRule = Object.assign(
|
|
|
2360
2421
|
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2361
2422
|
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2362
2423
|
},
|
|
2363
|
-
{
|
|
2424
|
+
{
|
|
2425
|
+
readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
|
|
2426
|
+
tags: ["input"]
|
|
2427
|
+
}
|
|
2364
2428
|
);
|
|
2365
2429
|
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2366
2430
|
var passwordAutocompleteRule = Object.assign(
|
|
@@ -2372,16 +2436,9 @@ var passwordAutocompleteRule = Object.assign(
|
|
|
2372
2436
|
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2373
2437
|
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2374
2438
|
},
|
|
2375
|
-
{ readsProps: ["type", "autoComplete"] }
|
|
2376
|
-
);
|
|
2377
|
-
var requiredReadOnlyConflictRule = Object.assign(
|
|
2378
|
-
({ tag, props }) => {
|
|
2379
|
-
if (tag !== "input" || !props.required || !props.readOnly) return [];
|
|
2380
|
-
const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
|
|
2381
|
-
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2382
|
-
},
|
|
2383
|
-
{ readsProps: ["required", "readOnly"] }
|
|
2439
|
+
{ readsProps: ["type", "autoComplete"], tags: ["input"] }
|
|
2384
2440
|
);
|
|
2441
|
+
var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
|
|
2385
2442
|
var INPUT_RULES = [
|
|
2386
2443
|
supportedInputTypeRule,
|
|
2387
2444
|
checkedRequiresCheckableTypeRule,
|
|
@@ -2394,11 +2451,132 @@ var INPUT_RULES = [
|
|
|
2394
2451
|
stepRequiresNumericTypeRule,
|
|
2395
2452
|
acceptRequiresFileTypeRule,
|
|
2396
2453
|
captureRequiresFileTypeRule,
|
|
2454
|
+
sizeRequiresTextTypeRule,
|
|
2455
|
+
altRequiresImageTypeRule,
|
|
2456
|
+
heightRequiresImageTypeRule,
|
|
2457
|
+
widthRequiresImageTypeRule,
|
|
2397
2458
|
inputAccessibleNameRule,
|
|
2398
2459
|
passwordAutocompleteRule,
|
|
2399
2460
|
requiredReadOnlyConflictRule
|
|
2400
2461
|
];
|
|
2401
2462
|
|
|
2463
|
+
// ../core/src/html/spec/types.ts
|
|
2464
|
+
function definePropRolePolicy(prop, map2, fallback) {
|
|
2465
|
+
return { kind: "byProp", prop, map: map2, fallback };
|
|
2466
|
+
}
|
|
2467
|
+
function resolveAllowedRoles(spec, props) {
|
|
2468
|
+
const policy = spec.allowedRoles;
|
|
2469
|
+
if (!policy) return void 0;
|
|
2470
|
+
switch (policy.kind) {
|
|
2471
|
+
case "fixed":
|
|
2472
|
+
return policy.roles;
|
|
2473
|
+
case "byProp": {
|
|
2474
|
+
const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
|
|
2475
|
+
return policy.map[value];
|
|
2476
|
+
}
|
|
2477
|
+
case "dynamic":
|
|
2478
|
+
return policy.resolve({ props });
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
// ../core/src/html/spec/roles/input.ts
|
|
2483
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2484
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2485
|
+
radio: ["menuitemradio"],
|
|
2486
|
+
range: [],
|
|
2487
|
+
number: [],
|
|
2488
|
+
search: ["combobox"],
|
|
2489
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2490
|
+
email: ["combobox"],
|
|
2491
|
+
tel: ["combobox"],
|
|
2492
|
+
url: ["combobox"],
|
|
2493
|
+
button: [
|
|
2494
|
+
"link",
|
|
2495
|
+
"menuitem",
|
|
2496
|
+
"menuitemcheckbox",
|
|
2497
|
+
"menuitemradio",
|
|
2498
|
+
"option",
|
|
2499
|
+
"radio",
|
|
2500
|
+
"switch",
|
|
2501
|
+
"tab"
|
|
2502
|
+
],
|
|
2503
|
+
submit: [
|
|
2504
|
+
"link",
|
|
2505
|
+
"menuitem",
|
|
2506
|
+
"menuitemcheckbox",
|
|
2507
|
+
"menuitemradio",
|
|
2508
|
+
"option",
|
|
2509
|
+
"radio",
|
|
2510
|
+
"switch",
|
|
2511
|
+
"tab"
|
|
2512
|
+
],
|
|
2513
|
+
reset: [
|
|
2514
|
+
"link",
|
|
2515
|
+
"menuitem",
|
|
2516
|
+
"menuitemcheckbox",
|
|
2517
|
+
"menuitemradio",
|
|
2518
|
+
"option",
|
|
2519
|
+
"radio",
|
|
2520
|
+
"switch",
|
|
2521
|
+
"tab"
|
|
2522
|
+
],
|
|
2523
|
+
image: [
|
|
2524
|
+
"link",
|
|
2525
|
+
"menuitem",
|
|
2526
|
+
"menuitemcheckbox",
|
|
2527
|
+
"menuitemradio",
|
|
2528
|
+
"option",
|
|
2529
|
+
"radio",
|
|
2530
|
+
"switch",
|
|
2531
|
+
"tab"
|
|
2532
|
+
],
|
|
2533
|
+
hidden: []
|
|
2534
|
+
};
|
|
2535
|
+
|
|
2536
|
+
// ../core/src/html/spec/elements/input.ts
|
|
2537
|
+
var inputElementSpec = {
|
|
2538
|
+
tag: "input",
|
|
2539
|
+
allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
|
|
2540
|
+
attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
|
|
2541
|
+
mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
// ../core/src/html/spec/roles/img.ts
|
|
2545
|
+
var IMG_NAMED_ROLES = [
|
|
2546
|
+
"button",
|
|
2547
|
+
"checkbox",
|
|
2548
|
+
"link",
|
|
2549
|
+
"menuitem",
|
|
2550
|
+
"menuitemcheckbox",
|
|
2551
|
+
"menuitemradio",
|
|
2552
|
+
"option",
|
|
2553
|
+
"progressbar",
|
|
2554
|
+
"scrollbar",
|
|
2555
|
+
"separator",
|
|
2556
|
+
"slider",
|
|
2557
|
+
"switch",
|
|
2558
|
+
"tab",
|
|
2559
|
+
"treeitem"
|
|
2560
|
+
];
|
|
2561
|
+
|
|
2562
|
+
// ../core/src/html/spec/elements/img.ts
|
|
2563
|
+
var imgElementSpec = {
|
|
2564
|
+
tag: "img",
|
|
2565
|
+
allowedRoles: {
|
|
2566
|
+
kind: "dynamic",
|
|
2567
|
+
resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
|
|
2568
|
+
}
|
|
2569
|
+
};
|
|
2570
|
+
|
|
2571
|
+
// ../core/src/html/spec/roles/table.ts
|
|
2572
|
+
var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
|
|
2573
|
+
|
|
2574
|
+
// ../core/src/html/spec/elements/table.ts
|
|
2575
|
+
var tableElementSpec = {
|
|
2576
|
+
tag: "table",
|
|
2577
|
+
allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
|
|
2578
|
+
};
|
|
2579
|
+
|
|
2402
2580
|
// ../core/src/html/role-restrictions.ts
|
|
2403
2581
|
var ALLOWED_ROLES = {
|
|
2404
2582
|
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
@@ -2471,86 +2649,17 @@ var ALLOWED_ROLES = {
|
|
|
2471
2649
|
"tab",
|
|
2472
2650
|
"treeitem"
|
|
2473
2651
|
],
|
|
2474
|
-
table: ["grid", "treegrid"],
|
|
2475
2652
|
dialog: ["alertdialog"],
|
|
2476
2653
|
fieldset: ["none", "presentation", "radiogroup"]
|
|
2477
2654
|
};
|
|
2478
|
-
var
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
"menuitem",
|
|
2483
|
-
"menuitemcheckbox",
|
|
2484
|
-
"menuitemradio",
|
|
2485
|
-
"option",
|
|
2486
|
-
"progressbar",
|
|
2487
|
-
"scrollbar",
|
|
2488
|
-
"separator",
|
|
2489
|
-
"slider",
|
|
2490
|
-
"switch",
|
|
2491
|
-
"tab",
|
|
2492
|
-
"treeitem"
|
|
2493
|
-
];
|
|
2494
|
-
var ALLOWED_INPUT_ROLES = {
|
|
2495
|
-
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2496
|
-
radio: ["menuitemradio"],
|
|
2497
|
-
range: [],
|
|
2498
|
-
number: [],
|
|
2499
|
-
search: ["combobox"],
|
|
2500
|
-
text: ["combobox", "searchbox", "spinbutton"],
|
|
2501
|
-
email: ["combobox"],
|
|
2502
|
-
tel: ["combobox"],
|
|
2503
|
-
url: ["combobox"],
|
|
2504
|
-
button: [
|
|
2505
|
-
"link",
|
|
2506
|
-
"menuitem",
|
|
2507
|
-
"menuitemcheckbox",
|
|
2508
|
-
"menuitemradio",
|
|
2509
|
-
"option",
|
|
2510
|
-
"radio",
|
|
2511
|
-
"switch",
|
|
2512
|
-
"tab"
|
|
2513
|
-
],
|
|
2514
|
-
submit: [
|
|
2515
|
-
"link",
|
|
2516
|
-
"menuitem",
|
|
2517
|
-
"menuitemcheckbox",
|
|
2518
|
-
"menuitemradio",
|
|
2519
|
-
"option",
|
|
2520
|
-
"radio",
|
|
2521
|
-
"switch",
|
|
2522
|
-
"tab"
|
|
2523
|
-
],
|
|
2524
|
-
reset: [
|
|
2525
|
-
"link",
|
|
2526
|
-
"menuitem",
|
|
2527
|
-
"menuitemcheckbox",
|
|
2528
|
-
"menuitemradio",
|
|
2529
|
-
"option",
|
|
2530
|
-
"radio",
|
|
2531
|
-
"switch",
|
|
2532
|
-
"tab"
|
|
2533
|
-
],
|
|
2534
|
-
image: [
|
|
2535
|
-
"link",
|
|
2536
|
-
"menuitem",
|
|
2537
|
-
"menuitemcheckbox",
|
|
2538
|
-
"menuitemradio",
|
|
2539
|
-
"option",
|
|
2540
|
-
"radio",
|
|
2541
|
-
"switch",
|
|
2542
|
-
"tab"
|
|
2543
|
-
],
|
|
2544
|
-
hidden: []
|
|
2655
|
+
var ELEMENT_SPECS = {
|
|
2656
|
+
input: inputElementSpec,
|
|
2657
|
+
img: imgElementSpec,
|
|
2658
|
+
table: tableElementSpec
|
|
2545
2659
|
};
|
|
2546
2660
|
function getAllowedRoles(tag, props) {
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
return ALLOWED_INPUT_ROLES[type];
|
|
2550
|
-
}
|
|
2551
|
-
if (tag === "img") {
|
|
2552
|
-
return props.alt === "" ? [] : IMG_NAMED_ROLES;
|
|
2553
|
-
}
|
|
2661
|
+
const spec = ELEMENT_SPECS[tag];
|
|
2662
|
+
if (spec) return resolveAllowedRoles(spec, props);
|
|
2554
2663
|
return ALLOWED_ROLES[tag];
|
|
2555
2664
|
}
|
|
2556
2665
|
var removeRoleFix = {
|
|
@@ -2591,21 +2700,24 @@ var removeLandmarkRoleOverride = {
|
|
|
2591
2700
|
return { applied: true, next: rest, previous: props };
|
|
2592
2701
|
}
|
|
2593
2702
|
};
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2703
|
+
var landmarkRoleRule = Object.assign(
|
|
2704
|
+
({ tag, props, implicitRole }) => {
|
|
2705
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2706
|
+
const role = props.role;
|
|
2707
|
+
if (!role || role === implicitRole) return [];
|
|
2708
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2709
|
+
return [
|
|
2710
|
+
{
|
|
2711
|
+
valid: false,
|
|
2712
|
+
fixable: true,
|
|
2713
|
+
severity: diagnostic.severity,
|
|
2714
|
+
fix: removeLandmarkRoleOverride,
|
|
2715
|
+
diagnostic
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
},
|
|
2719
|
+
{ tags: [...LANDMARK_TAG_SET] }
|
|
2720
|
+
);
|
|
2609
2721
|
function requireAccessibleName({ tag, props }) {
|
|
2610
2722
|
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2611
2723
|
return [
|
|
@@ -2618,10 +2730,13 @@ function requireAccessibleName({ tag, props }) {
|
|
|
2618
2730
|
];
|
|
2619
2731
|
}
|
|
2620
2732
|
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2733
|
+
var landmarkNameAdvisory = Object.assign(
|
|
2734
|
+
(ctx) => {
|
|
2735
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2736
|
+
return requireAccessibleName(ctx);
|
|
2737
|
+
},
|
|
2738
|
+
{ tags: [...NAMED_LANDMARK_TAGS] }
|
|
2739
|
+
);
|
|
2625
2740
|
var HTML_ARIA_RULES = [
|
|
2626
2741
|
landmarkRoleRule,
|
|
2627
2742
|
landmarkNameAdvisory,
|
|
@@ -2722,6 +2837,62 @@ var figureContract = contract([
|
|
|
2722
2837
|
]);
|
|
2723
2838
|
var detailsContract = firstChildContract("summary", "summary");
|
|
2724
2839
|
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2840
|
+
var objectContract = contract([
|
|
2841
|
+
{ name: "param", match: isTag("param") },
|
|
2842
|
+
{ name: "content", match: isOpenContent("param") }
|
|
2843
|
+
]);
|
|
2844
|
+
var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
|
|
2845
|
+
var buttonContract = closedContract([
|
|
2846
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2847
|
+
]);
|
|
2848
|
+
var anchorContract = closedContract([
|
|
2849
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2850
|
+
]);
|
|
2851
|
+
var LABELABLE_TAGS = [
|
|
2852
|
+
"button",
|
|
2853
|
+
"input",
|
|
2854
|
+
"meter",
|
|
2855
|
+
"output",
|
|
2856
|
+
"progress",
|
|
2857
|
+
"select",
|
|
2858
|
+
"textarea"
|
|
2859
|
+
];
|
|
2860
|
+
var labelContract = contract([
|
|
2861
|
+
{ name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
|
|
2862
|
+
]);
|
|
2863
|
+
var P_BLOCKED_TAGS = [
|
|
2864
|
+
"address",
|
|
2865
|
+
"article",
|
|
2866
|
+
"aside",
|
|
2867
|
+
"blockquote",
|
|
2868
|
+
"details",
|
|
2869
|
+
"dialog",
|
|
2870
|
+
"div",
|
|
2871
|
+
"dl",
|
|
2872
|
+
"fieldset",
|
|
2873
|
+
"figure",
|
|
2874
|
+
"footer",
|
|
2875
|
+
"form",
|
|
2876
|
+
"h1",
|
|
2877
|
+
"h2",
|
|
2878
|
+
"h3",
|
|
2879
|
+
"h4",
|
|
2880
|
+
"h5",
|
|
2881
|
+
"h6",
|
|
2882
|
+
"header",
|
|
2883
|
+
"hr",
|
|
2884
|
+
"main",
|
|
2885
|
+
"nav",
|
|
2886
|
+
"ol",
|
|
2887
|
+
"p",
|
|
2888
|
+
"pre",
|
|
2889
|
+
"section",
|
|
2890
|
+
"table",
|
|
2891
|
+
"ul"
|
|
2892
|
+
];
|
|
2893
|
+
var pContract = closedContract([
|
|
2894
|
+
{ name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
|
|
2895
|
+
]);
|
|
2725
2896
|
var mediaContract = contract([
|
|
2726
2897
|
{ name: "source", match: isTag("source") },
|
|
2727
2898
|
{ name: "track", match: isTag("track") },
|
|
@@ -2776,6 +2947,11 @@ var htmlContracts = {
|
|
|
2776
2947
|
details: detailsContract,
|
|
2777
2948
|
fieldset: fieldsetContract,
|
|
2778
2949
|
dialog: dialogContract,
|
|
2950
|
+
object: objectContract,
|
|
2951
|
+
button: buttonContract,
|
|
2952
|
+
a: anchorContract,
|
|
2953
|
+
label: labelContract,
|
|
2954
|
+
p: pContract,
|
|
2779
2955
|
head: headContract,
|
|
2780
2956
|
html: htmlContract
|
|
2781
2957
|
};
|