praxis-kit 6.2.1 → 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 +20 -0
- package/dist/_shared/diagnostics.js +20 -0
- package/dist/{chunk-4YVNOMUS.js → chunk-AM5J6PY3.js} +839 -198
- package/dist/contract/index.d.ts +1 -0
- package/dist/lit/index.d.ts +1 -0
- package/dist/lit/index.js +828 -187
- package/dist/preact/index.d.ts +1 -0
- package/dist/preact/index.js +839 -198
- 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 +839 -198
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +839 -198
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.js +15 -0
- package/dist/vite-plugin/index.d.ts +20 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +839 -198
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +828 -187
- package/package.json +13 -6
package/dist/solid/index.js
CHANGED
|
@@ -954,11 +954,28 @@ var ContractDiagnostics = {
|
|
|
954
954
|
|
|
955
955
|
// ../../lib/contract/src/diagnostics/html.ts
|
|
956
956
|
import { DiagnosticCategory as DiagnosticCategory3, DiagnosticCode as DiagnosticCode3 } from "../_shared/diagnostics.js";
|
|
957
|
+
var ATTRIBUTE_IGNORED_CODES = {
|
|
958
|
+
checked: DiagnosticCode3.HtmlInputCheckedIgnoredForType,
|
|
959
|
+
multiple: DiagnosticCode3.HtmlInputMultipleIgnoredForType,
|
|
960
|
+
maxLength: DiagnosticCode3.HtmlInputMaxLengthIgnoredForType,
|
|
961
|
+
minLength: DiagnosticCode3.HtmlInputMinLengthIgnoredForType,
|
|
962
|
+
pattern: DiagnosticCode3.HtmlInputPatternIgnoredForType,
|
|
963
|
+
min: DiagnosticCode3.HtmlInputMinIgnoredForType,
|
|
964
|
+
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
965
|
+
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
966
|
+
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
967
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
|
|
968
|
+
size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
|
|
969
|
+
alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
|
|
970
|
+
height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
|
|
971
|
+
width: DiagnosticCode3.HtmlInputWidthIgnoredForType
|
|
972
|
+
};
|
|
957
973
|
var HtmlDiagnostics = {
|
|
958
974
|
emptyRole(tag) {
|
|
959
975
|
return {
|
|
960
976
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
961
977
|
category: DiagnosticCategory3.HTML,
|
|
978
|
+
severity: "warning",
|
|
962
979
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
963
980
|
};
|
|
964
981
|
},
|
|
@@ -966,6 +983,7 @@ var HtmlDiagnostics = {
|
|
|
966
983
|
return {
|
|
967
984
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
968
985
|
category: DiagnosticCategory3.HTML,
|
|
986
|
+
severity: "warning",
|
|
969
987
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
970
988
|
};
|
|
971
989
|
},
|
|
@@ -973,6 +991,7 @@ var HtmlDiagnostics = {
|
|
|
973
991
|
return {
|
|
974
992
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
975
993
|
category: DiagnosticCategory3.HTML,
|
|
994
|
+
severity: "error",
|
|
976
995
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
977
996
|
};
|
|
978
997
|
},
|
|
@@ -980,6 +999,7 @@ var HtmlDiagnostics = {
|
|
|
980
999
|
return {
|
|
981
1000
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
982
1001
|
category: DiagnosticCategory3.HTML,
|
|
1002
|
+
severity: "error",
|
|
983
1003
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
984
1004
|
};
|
|
985
1005
|
},
|
|
@@ -987,6 +1007,7 @@ var HtmlDiagnostics = {
|
|
|
987
1007
|
return {
|
|
988
1008
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
989
1009
|
category: DiagnosticCategory3.HTML,
|
|
1010
|
+
severity: "error",
|
|
990
1011
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
991
1012
|
};
|
|
992
1013
|
},
|
|
@@ -994,34 +1015,148 @@ var HtmlDiagnostics = {
|
|
|
994
1015
|
return {
|
|
995
1016
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
996
1017
|
category: DiagnosticCategory3.HTML,
|
|
1018
|
+
severity: "error",
|
|
997
1019
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
998
1020
|
};
|
|
1021
|
+
},
|
|
1022
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
1023
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
1024
|
+
return {
|
|
1025
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
1026
|
+
category: DiagnosticCategory3.HTML,
|
|
1027
|
+
severity: "error",
|
|
1028
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
1029
|
+
rationale: 'The WAI-ARIA "ARIA in HTML" specification restricts which explicit roles a native element may take. A role outside that set is ignored or produces undefined behavior in assistive technology.'
|
|
1030
|
+
};
|
|
1031
|
+
},
|
|
1032
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
1033
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
1034
|
+
input: {
|
|
1035
|
+
unsupportedType(type) {
|
|
1036
|
+
return {
|
|
1037
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
1038
|
+
category: DiagnosticCategory3.HTML,
|
|
1039
|
+
severity: "warning",
|
|
1040
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
1041
|
+
rationale: 'An unrecognized input type is not invalid markup \u2014 the spec requires the "text" fallback \u2014 but it usually means a typo, since the input keeps working while silently losing the intended type-specific behavior (validation, virtual keyboard, picker UI).',
|
|
1042
|
+
suggestions: [
|
|
1043
|
+
{
|
|
1044
|
+
title: "Check for a typo in the type value",
|
|
1045
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
1046
|
+
}
|
|
1047
|
+
]
|
|
1048
|
+
};
|
|
1049
|
+
},
|
|
1050
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
1051
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
1052
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
1053
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
1054
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
1055
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
1056
|
+
return {
|
|
1057
|
+
code,
|
|
1058
|
+
category: DiagnosticCategory3.HTML,
|
|
1059
|
+
severity: "warning",
|
|
1060
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
1061
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
1062
|
+
suggestions: [
|
|
1063
|
+
{
|
|
1064
|
+
title: `Remove "${attribute}"`,
|
|
1065
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
1066
|
+
}
|
|
1067
|
+
]
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
999
1070
|
}
|
|
1000
1071
|
};
|
|
1001
1072
|
|
|
1002
|
-
// ../../lib/contract/src/diagnostics/
|
|
1073
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
1003
1074
|
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "../_shared/diagnostics.js";
|
|
1075
|
+
function accessibilityFact(input) {
|
|
1076
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
1077
|
+
}
|
|
1078
|
+
var InputAccessibilityDiagnostics = {
|
|
1079
|
+
missingAccessibleName() {
|
|
1080
|
+
return accessibilityFact({
|
|
1081
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
1082
|
+
severity: "warning",
|
|
1083
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1084
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
1085
|
+
suggestions: [
|
|
1086
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1087
|
+
{
|
|
1088
|
+
title: "Add an associated <label>",
|
|
1089
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1090
|
+
}
|
|
1091
|
+
]
|
|
1092
|
+
});
|
|
1093
|
+
},
|
|
1094
|
+
placeholderIsNotLabel() {
|
|
1095
|
+
return accessibilityFact({
|
|
1096
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
1097
|
+
severity: "warning",
|
|
1098
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1099
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1100
|
+
suggestions: [
|
|
1101
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1102
|
+
{
|
|
1103
|
+
title: "Add an associated <label>",
|
|
1104
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1105
|
+
}
|
|
1106
|
+
]
|
|
1107
|
+
});
|
|
1108
|
+
},
|
|
1109
|
+
passwordMissingAutocomplete() {
|
|
1110
|
+
return accessibilityFact({
|
|
1111
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1112
|
+
severity: "warning",
|
|
1113
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1114
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1115
|
+
suggestions: [
|
|
1116
|
+
{
|
|
1117
|
+
title: 'Set autoComplete="current-password"',
|
|
1118
|
+
description: "Use this for sign-in forms."
|
|
1119
|
+
},
|
|
1120
|
+
{
|
|
1121
|
+
title: 'Set autoComplete="new-password"',
|
|
1122
|
+
description: "Use this for sign-up / change-password forms."
|
|
1123
|
+
}
|
|
1124
|
+
]
|
|
1125
|
+
});
|
|
1126
|
+
},
|
|
1127
|
+
requiredReadOnlyConflict() {
|
|
1128
|
+
return accessibilityFact({
|
|
1129
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1130
|
+
severity: "warning",
|
|
1131
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1132
|
+
rationale: "This combination is valid HTML but usually indicates an unintended state. Consider using disabled instead of readOnly, or only applying required when the field is editable."
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1138
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
1004
1139
|
var SlotDiagnostics = {
|
|
1005
1140
|
exclusive(name) {
|
|
1006
1141
|
return {
|
|
1007
|
-
code:
|
|
1008
|
-
category:
|
|
1142
|
+
code: DiagnosticCode5.SlotExclusive,
|
|
1143
|
+
category: DiagnosticCategory5.Contract,
|
|
1009
1144
|
component: name,
|
|
1010
1145
|
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
1011
1146
|
};
|
|
1012
1147
|
},
|
|
1013
1148
|
singleChildRequired(name, elementTerm) {
|
|
1014
1149
|
return {
|
|
1015
|
-
code:
|
|
1016
|
-
category:
|
|
1150
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1151
|
+
category: DiagnosticCategory5.Contract,
|
|
1017
1152
|
component: name,
|
|
1018
1153
|
message: `${name}: asChild requires a ${elementTerm} child`
|
|
1019
1154
|
};
|
|
1020
1155
|
},
|
|
1021
1156
|
singleChildExceeded(name, elementTerm, count) {
|
|
1022
1157
|
return {
|
|
1023
|
-
code:
|
|
1024
|
-
category:
|
|
1158
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1159
|
+
category: DiagnosticCategory5.Contract,
|
|
1025
1160
|
component: name,
|
|
1026
1161
|
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
1027
1162
|
};
|
|
@@ -1029,16 +1164,16 @@ var SlotDiagnostics = {
|
|
|
1029
1164
|
discardedChildren(name, elementTerm, count) {
|
|
1030
1165
|
const suffix = count === 1 ? "" : "ren";
|
|
1031
1166
|
return {
|
|
1032
|
-
code:
|
|
1033
|
-
category:
|
|
1167
|
+
code: DiagnosticCode5.SlotDiscardedChildren,
|
|
1168
|
+
category: DiagnosticCategory5.Contract,
|
|
1034
1169
|
component: name,
|
|
1035
1170
|
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
1036
1171
|
};
|
|
1037
1172
|
},
|
|
1038
1173
|
renderFnRequired(name, received) {
|
|
1039
1174
|
return {
|
|
1040
|
-
code:
|
|
1041
|
-
category:
|
|
1175
|
+
code: DiagnosticCode5.SlotRenderFn,
|
|
1176
|
+
category: DiagnosticCategory5.Contract,
|
|
1042
1177
|
component: name,
|
|
1043
1178
|
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
1044
1179
|
};
|
|
@@ -1067,8 +1202,132 @@ var InvariantBase = class {
|
|
|
1067
1202
|
}
|
|
1068
1203
|
};
|
|
1069
1204
|
|
|
1070
|
-
// ../../lib/contract/src/aria/
|
|
1205
|
+
// ../../lib/contract/src/aria/spec/roles/required-properties.ts
|
|
1206
|
+
var REQUIRED_ARIA_PROPERTIES = {
|
|
1207
|
+
combobox: ["aria-expanded"],
|
|
1208
|
+
option: ["aria-selected"],
|
|
1209
|
+
slider: ["aria-valuenow"],
|
|
1210
|
+
scrollbar: ["aria-controls", "aria-valuenow"],
|
|
1211
|
+
spinbutton: ["aria-valuenow"]
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
// ../../lib/contract/src/aria/spec/roles/name-required.ts
|
|
1215
|
+
var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1216
|
+
|
|
1217
|
+
// ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
|
|
1071
1218
|
var NO_VIOLATIONS = [{ valid: true }];
|
|
1219
|
+
function requiredAttributeByRole(roles, attribute) {
|
|
1220
|
+
return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
|
|
1221
|
+
}
|
|
1222
|
+
function checkRequiredAttributes(requirement, { props, effectiveRole }) {
|
|
1223
|
+
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1224
|
+
const requiredAttributes = requirement.attributesByRole[effectiveRole];
|
|
1225
|
+
if (!requiredAttributes) return NO_VIOLATIONS;
|
|
1226
|
+
const results = [];
|
|
1227
|
+
for (const attribute of requiredAttributes) {
|
|
1228
|
+
if (attribute in props) continue;
|
|
1229
|
+
results.push({
|
|
1230
|
+
valid: false,
|
|
1231
|
+
fixable: false,
|
|
1232
|
+
severity: "warning",
|
|
1233
|
+
attribute,
|
|
1234
|
+
diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
return results;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// ../../lib/contract/src/aria/spec/roles/live-region.ts
|
|
1241
|
+
var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1242
|
+
["alert", "assertive"],
|
|
1243
|
+
["status", "polite"],
|
|
1244
|
+
["log", "polite"],
|
|
1245
|
+
["timer", "off"]
|
|
1246
|
+
]);
|
|
1247
|
+
var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
|
|
1248
|
+
|
|
1249
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
|
|
1250
|
+
var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1251
|
+
// Boolean (true | false)
|
|
1252
|
+
["aria-atomic", { kind: "boolean" }],
|
|
1253
|
+
["aria-busy", { kind: "boolean" }],
|
|
1254
|
+
["aria-disabled", { kind: "boolean" }],
|
|
1255
|
+
["aria-expanded", { kind: "boolean" }],
|
|
1256
|
+
["aria-hidden", { kind: "boolean" }],
|
|
1257
|
+
["aria-modal", { kind: "boolean" }],
|
|
1258
|
+
["aria-multiline", { kind: "boolean" }],
|
|
1259
|
+
["aria-multiselectable", { kind: "boolean" }],
|
|
1260
|
+
["aria-readonly", { kind: "boolean" }],
|
|
1261
|
+
["aria-required", { kind: "boolean" }],
|
|
1262
|
+
["aria-selected", { kind: "boolean" }],
|
|
1263
|
+
// Tristate (true | false | mixed)
|
|
1264
|
+
["aria-checked", { kind: "tristate" }],
|
|
1265
|
+
["aria-pressed", { kind: "tristate" }],
|
|
1266
|
+
// Numeric (any finite number)
|
|
1267
|
+
["aria-valuenow", { kind: "number" }],
|
|
1268
|
+
["aria-valuemin", { kind: "number" }],
|
|
1269
|
+
["aria-valuemax", { kind: "number" }],
|
|
1270
|
+
// Integer with optional range
|
|
1271
|
+
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1272
|
+
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1273
|
+
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1274
|
+
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1275
|
+
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1276
|
+
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1277
|
+
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1278
|
+
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1279
|
+
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1280
|
+
// Enum (specific allowed tokens)
|
|
1281
|
+
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1282
|
+
[
|
|
1283
|
+
"aria-current",
|
|
1284
|
+
{
|
|
1285
|
+
kind: "enum",
|
|
1286
|
+
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1287
|
+
}
|
|
1288
|
+
],
|
|
1289
|
+
[
|
|
1290
|
+
"aria-haspopup",
|
|
1291
|
+
{
|
|
1292
|
+
kind: "enum",
|
|
1293
|
+
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1294
|
+
}
|
|
1295
|
+
],
|
|
1296
|
+
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1297
|
+
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1298
|
+
["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
|
|
1299
|
+
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1300
|
+
]);
|
|
1301
|
+
|
|
1302
|
+
// ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
|
|
1303
|
+
var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
|
|
1304
|
+
"additions",
|
|
1305
|
+
"removals",
|
|
1306
|
+
"text",
|
|
1307
|
+
"all"
|
|
1308
|
+
]);
|
|
1309
|
+
|
|
1310
|
+
// ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
|
|
1311
|
+
var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1312
|
+
["h1", 1],
|
|
1313
|
+
["h2", 2],
|
|
1314
|
+
["h3", 3],
|
|
1315
|
+
["h4", 4],
|
|
1316
|
+
["h5", 5],
|
|
1317
|
+
["h6", 6]
|
|
1318
|
+
]);
|
|
1319
|
+
|
|
1320
|
+
// ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
|
|
1321
|
+
var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1322
|
+
"a",
|
|
1323
|
+
"button",
|
|
1324
|
+
"input",
|
|
1325
|
+
"select",
|
|
1326
|
+
"textarea"
|
|
1327
|
+
]);
|
|
1328
|
+
|
|
1329
|
+
// ../../lib/contract/src/aria/polymorphic-validator.ts
|
|
1330
|
+
var NO_VIOLATIONS2 = [{ valid: true }];
|
|
1072
1331
|
function isIntrinsicTag(tag) {
|
|
1073
1332
|
return isString(tag);
|
|
1074
1333
|
}
|
|
@@ -1100,7 +1359,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1100
1359
|
tag,
|
|
1101
1360
|
role: "",
|
|
1102
1361
|
attribute: void 0,
|
|
1103
|
-
severity:
|
|
1362
|
+
severity: d.severity,
|
|
1104
1363
|
phase: "evaluate"
|
|
1105
1364
|
}
|
|
1106
1365
|
]
|
|
@@ -1130,6 +1389,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1130
1389
|
const violations = [];
|
|
1131
1390
|
const fixes = [];
|
|
1132
1391
|
iterate.forEach(rules, (rule) => {
|
|
1392
|
+
if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
|
|
1133
1393
|
iterate.forEach(rule(context), (result) => {
|
|
1134
1394
|
if (result.valid) return;
|
|
1135
1395
|
const {
|
|
@@ -1155,7 +1415,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1155
1415
|
return { violations, fixes };
|
|
1156
1416
|
}
|
|
1157
1417
|
static #getRules(context) {
|
|
1158
|
-
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) &&
|
|
1418
|
+
if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
|
|
1159
1419
|
return _AriaPolicyEngine.#pipeline;
|
|
1160
1420
|
}
|
|
1161
1421
|
return _AriaPolicyEngine.#implicitOnlyRules;
|
|
@@ -1357,44 +1617,47 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1357
1617
|
implicitRole
|
|
1358
1618
|
}) {
|
|
1359
1619
|
const role = props.role;
|
|
1360
|
-
if (!implicitRole || !role || role === implicitRole) return
|
|
1620
|
+
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
|
|
1361
1621
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1622
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1362
1623
|
return [
|
|
1363
1624
|
{
|
|
1364
1625
|
valid: false,
|
|
1365
1626
|
fixable: true,
|
|
1366
|
-
severity:
|
|
1627
|
+
severity: diagnostic.severity,
|
|
1367
1628
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1368
|
-
diagnostic
|
|
1629
|
+
diagnostic
|
|
1369
1630
|
}
|
|
1370
1631
|
];
|
|
1371
1632
|
}
|
|
1372
|
-
return
|
|
1633
|
+
return NO_VIOLATIONS2;
|
|
1373
1634
|
}
|
|
1374
1635
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1375
1636
|
const role = props.role;
|
|
1376
|
-
if (!implicitRole || !role || role !== implicitRole) return
|
|
1637
|
+
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
|
|
1638
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1377
1639
|
return [
|
|
1378
1640
|
{
|
|
1379
1641
|
valid: false,
|
|
1380
1642
|
fixable: true,
|
|
1381
|
-
severity:
|
|
1643
|
+
severity: diagnostic.severity,
|
|
1382
1644
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1383
|
-
diagnostic
|
|
1645
|
+
diagnostic
|
|
1384
1646
|
}
|
|
1385
1647
|
];
|
|
1386
1648
|
}
|
|
1387
1649
|
static #checkStandaloneRegion({ tag, props, implicitRole }) {
|
|
1388
1650
|
const role = props.role;
|
|
1389
|
-
if (role !== "region") return
|
|
1390
|
-
if (!isStandaloneTag(tag)) return
|
|
1651
|
+
if (role !== "region") return NO_VIOLATIONS2;
|
|
1652
|
+
if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
|
|
1653
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1391
1654
|
return [
|
|
1392
1655
|
{
|
|
1393
1656
|
valid: false,
|
|
1394
1657
|
fixable: true,
|
|
1395
|
-
severity:
|
|
1658
|
+
severity: diagnostic.severity,
|
|
1396
1659
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1397
|
-
diagnostic
|
|
1660
|
+
diagnostic
|
|
1398
1661
|
}
|
|
1399
1662
|
];
|
|
1400
1663
|
}
|
|
@@ -1403,7 +1666,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1403
1666
|
props,
|
|
1404
1667
|
effectiveRole
|
|
1405
1668
|
}) {
|
|
1406
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1669
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1407
1670
|
const results = [];
|
|
1408
1671
|
iterate.forEachEntry(props, (key) => {
|
|
1409
1672
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1421,62 +1684,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1421
1684
|
return results;
|
|
1422
1685
|
}
|
|
1423
1686
|
// ─── ARIA attribute value validation ──────────────────────────────────────
|
|
1424
|
-
// Accepted value shapes for typed ARIA attributes.
|
|
1425
|
-
// Attributes not in this map are unconstrained (arbitrary string values permitted).
|
|
1426
|
-
static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
|
|
1427
|
-
// Boolean (true | false)
|
|
1428
|
-
["aria-atomic", { kind: "boolean" }],
|
|
1429
|
-
["aria-busy", { kind: "boolean" }],
|
|
1430
|
-
["aria-disabled", { kind: "boolean" }],
|
|
1431
|
-
["aria-expanded", { kind: "boolean" }],
|
|
1432
|
-
["aria-hidden", { kind: "boolean" }],
|
|
1433
|
-
["aria-modal", { kind: "boolean" }],
|
|
1434
|
-
["aria-multiline", { kind: "boolean" }],
|
|
1435
|
-
["aria-multiselectable", { kind: "boolean" }],
|
|
1436
|
-
["aria-readonly", { kind: "boolean" }],
|
|
1437
|
-
["aria-required", { kind: "boolean" }],
|
|
1438
|
-
["aria-selected", { kind: "boolean" }],
|
|
1439
|
-
// Tristate (true | false | mixed)
|
|
1440
|
-
["aria-checked", { kind: "tristate" }],
|
|
1441
|
-
["aria-pressed", { kind: "tristate" }],
|
|
1442
|
-
// Numeric (any finite number)
|
|
1443
|
-
["aria-valuenow", { kind: "number" }],
|
|
1444
|
-
["aria-valuemin", { kind: "number" }],
|
|
1445
|
-
["aria-valuemax", { kind: "number" }],
|
|
1446
|
-
// Integer with optional range
|
|
1447
|
-
["aria-level", { kind: "integer", min: 1, max: 6 }],
|
|
1448
|
-
["aria-posinset", { kind: "integer", min: 1 }],
|
|
1449
|
-
["aria-setsize", { kind: "integer", min: -1 }],
|
|
1450
|
-
["aria-rowcount", { kind: "integer", min: -1 }],
|
|
1451
|
-
["aria-colcount", { kind: "integer", min: -1 }],
|
|
1452
|
-
["aria-rowindex", { kind: "integer", min: 1 }],
|
|
1453
|
-
["aria-colindex", { kind: "integer", min: 1 }],
|
|
1454
|
-
["aria-rowspan", { kind: "integer", min: 0 }],
|
|
1455
|
-
["aria-colspan", { kind: "integer", min: 0 }],
|
|
1456
|
-
// Enum (specific allowed tokens)
|
|
1457
|
-
["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
|
|
1458
|
-
[
|
|
1459
|
-
"aria-current",
|
|
1460
|
-
{
|
|
1461
|
-
kind: "enum",
|
|
1462
|
-
values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
|
|
1463
|
-
}
|
|
1464
|
-
],
|
|
1465
|
-
[
|
|
1466
|
-
"aria-haspopup",
|
|
1467
|
-
{
|
|
1468
|
-
kind: "enum",
|
|
1469
|
-
values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
|
|
1470
|
-
}
|
|
1471
|
-
],
|
|
1472
|
-
["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
|
|
1473
|
-
["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
|
|
1474
|
-
[
|
|
1475
|
-
"aria-orientation",
|
|
1476
|
-
{ kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
|
|
1477
|
-
],
|
|
1478
|
-
["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
|
|
1479
|
-
]);
|
|
1480
1687
|
static #isValidAriaValue(value, type) {
|
|
1481
1688
|
switch (type.kind) {
|
|
1482
1689
|
case "boolean":
|
|
@@ -1521,11 +1728,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1521
1728
|
}
|
|
1522
1729
|
}
|
|
1523
1730
|
static #checkAriaAttributeValues({ props, effectiveRole }) {
|
|
1524
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1731
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1525
1732
|
const results = [];
|
|
1526
1733
|
iterate.forEachEntry(props, (key, value) => {
|
|
1527
1734
|
if (!key.startsWith("aria-")) return;
|
|
1528
|
-
const type =
|
|
1735
|
+
const type = ARIA_VALUE_TYPES.get(key);
|
|
1529
1736
|
if (!isNonNull(type)) return;
|
|
1530
1737
|
if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
|
|
1531
1738
|
results.push({
|
|
@@ -1544,26 +1751,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1544
1751
|
return results;
|
|
1545
1752
|
}
|
|
1546
1753
|
// ─── Heading implicit level ────────────────────────────────────────────────
|
|
1547
|
-
static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
|
|
1548
|
-
["h1", 1],
|
|
1549
|
-
["h2", 2],
|
|
1550
|
-
["h3", 3],
|
|
1551
|
-
["h4", 4],
|
|
1552
|
-
["h5", 5],
|
|
1553
|
-
["h6", 6]
|
|
1554
|
-
]);
|
|
1555
1754
|
static #checkRedundantAriaLevel({
|
|
1556
1755
|
tag,
|
|
1557
1756
|
props,
|
|
1558
1757
|
effectiveRole
|
|
1559
1758
|
}) {
|
|
1560
|
-
if (effectiveRole === "none" || effectiveRole === "presentation") return
|
|
1561
|
-
const implicitLevel =
|
|
1562
|
-
if (!isNonNull(implicitLevel)) return
|
|
1759
|
+
if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
|
|
1760
|
+
const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
|
|
1761
|
+
if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
|
|
1563
1762
|
const raw = props["aria-level"];
|
|
1564
|
-
if (!isNonNull(raw)) return
|
|
1763
|
+
if (!isNonNull(raw)) return NO_VIOLATIONS2;
|
|
1565
1764
|
const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
|
|
1566
|
-
if (!Number.isFinite(n) || n !== implicitLevel) return
|
|
1765
|
+
if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
|
|
1567
1766
|
return [
|
|
1568
1767
|
{
|
|
1569
1768
|
valid: false,
|
|
@@ -1576,20 +1775,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1576
1775
|
];
|
|
1577
1776
|
}
|
|
1578
1777
|
// ─── Name-required roles ───────────────────────────────────────────────────
|
|
1579
|
-
// Roles that always require an accessible name per WAI-ARIA APG.
|
|
1580
|
-
// Dialog and landmark names are enforced via contracts (ariaContract) rather than
|
|
1581
|
-
// the built-in pipeline so consumers can opt in; img is built in because role=img
|
|
1582
|
-
// on any element (including bare <img>) is definitionally useless without a name.
|
|
1583
|
-
static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
|
|
1584
1778
|
static #checkNameRequiredRoles({
|
|
1585
1779
|
tag,
|
|
1586
1780
|
props,
|
|
1587
1781
|
effectiveRole
|
|
1588
1782
|
}) {
|
|
1589
|
-
if (!effectiveRole || !
|
|
1590
|
-
|
|
1591
|
-
if ("
|
|
1592
|
-
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
|
|
1783
|
+
if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
|
|
1784
|
+
if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
|
|
1785
|
+
if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
|
|
1593
1786
|
return [
|
|
1594
1787
|
{
|
|
1595
1788
|
valid: false,
|
|
@@ -1599,51 +1792,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1599
1792
|
}
|
|
1600
1793
|
];
|
|
1601
1794
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
["scrollbar", ["aria-controls", "aria-valuenow"]],
|
|
1609
|
-
["spinbutton", ["aria-valuenow"]]
|
|
1610
|
-
]);
|
|
1611
|
-
static #checkRequiredAriaProperties({
|
|
1612
|
-
props,
|
|
1613
|
-
effectiveRole
|
|
1614
|
-
}) {
|
|
1615
|
-
if (!effectiveRole) return NO_VIOLATIONS;
|
|
1616
|
-
const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
|
|
1617
|
-
if (!isNonNull(required)) return NO_VIOLATIONS;
|
|
1618
|
-
const results = [];
|
|
1619
|
-
iterate.forEach(required, (attr) => {
|
|
1620
|
-
if (attr in props) return;
|
|
1621
|
-
results.push({
|
|
1622
|
-
valid: false,
|
|
1623
|
-
fixable: false,
|
|
1624
|
-
severity: "warning",
|
|
1625
|
-
attribute: attr,
|
|
1626
|
-
diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
|
|
1627
|
-
});
|
|
1628
|
-
});
|
|
1629
|
-
return results;
|
|
1795
|
+
static #requiredAriaPropertiesRule = {
|
|
1796
|
+
attributesByRole: REQUIRED_ARIA_PROPERTIES,
|
|
1797
|
+
diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
|
|
1798
|
+
};
|
|
1799
|
+
static #checkRequiredAriaProperties(context) {
|
|
1800
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
|
|
1630
1801
|
}
|
|
1631
|
-
// Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
|
|
1632
|
-
static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
|
|
1633
|
-
"a",
|
|
1634
|
-
"button",
|
|
1635
|
-
"input",
|
|
1636
|
-
"select",
|
|
1637
|
-
"textarea"
|
|
1638
|
-
]);
|
|
1639
1802
|
// WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
|
|
1640
1803
|
static #checkAriaHiddenOnFocusable({ tag, props }) {
|
|
1641
|
-
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return
|
|
1642
|
-
const isInteractive =
|
|
1804
|
+
if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
|
|
1805
|
+
const isInteractive = INTERACTIVE_TAGS.has(tag);
|
|
1643
1806
|
if (!isInteractive) {
|
|
1644
1807
|
const tabindex = props.tabindex;
|
|
1645
1808
|
const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
|
|
1646
|
-
if (!Number.isFinite(n) || n < 0) return
|
|
1809
|
+
if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
|
|
1647
1810
|
}
|
|
1648
1811
|
return [
|
|
1649
1812
|
{
|
|
@@ -1662,7 +1825,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1662
1825
|
props,
|
|
1663
1826
|
effectiveRole
|
|
1664
1827
|
}) {
|
|
1665
|
-
if (effectiveRole !== "none" && effectiveRole !== "presentation") return
|
|
1828
|
+
if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
|
|
1666
1829
|
const results = [];
|
|
1667
1830
|
iterate.forEachEntry(props, (key) => {
|
|
1668
1831
|
if (!key.startsWith("aria-")) return;
|
|
@@ -1678,18 +1841,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1678
1841
|
});
|
|
1679
1842
|
return results;
|
|
1680
1843
|
}
|
|
1681
|
-
// WAI-ARIA live region roles and their implied aria-live politeness values.
|
|
1682
|
-
static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
|
|
1683
|
-
["alert", "assertive"],
|
|
1684
|
-
["status", "polite"],
|
|
1685
|
-
["log", "polite"],
|
|
1686
|
-
["timer", "off"]
|
|
1687
|
-
]);
|
|
1688
1844
|
static #checkMissingLiveRegion({ effectiveRole, props }) {
|
|
1689
|
-
if (!effectiveRole) return
|
|
1690
|
-
const impliedLive =
|
|
1691
|
-
if (!impliedLive) return
|
|
1692
|
-
if ("aria-live" in props) return
|
|
1845
|
+
if (!effectiveRole) return NO_VIOLATIONS2;
|
|
1846
|
+
const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
|
|
1847
|
+
if (!impliedLive) return NO_VIOLATIONS2;
|
|
1848
|
+
if ("aria-live" in props) return NO_VIOLATIONS2;
|
|
1693
1849
|
const injectLive = {
|
|
1694
1850
|
kind: `injectLive:${effectiveRole}`,
|
|
1695
1851
|
apply: (ctx) => ({
|
|
@@ -1708,20 +1864,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1708
1864
|
}
|
|
1709
1865
|
];
|
|
1710
1866
|
}
|
|
1711
|
-
static #
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
valid: false,
|
|
1718
|
-
fixable: false,
|
|
1719
|
-
severity: "warning",
|
|
1720
|
-
diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
|
|
1721
|
-
}
|
|
1722
|
-
];
|
|
1867
|
+
static #missingAtomicRule = {
|
|
1868
|
+
attributesByRole: ATOMIC_REQUIREMENTS,
|
|
1869
|
+
diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
|
|
1870
|
+
};
|
|
1871
|
+
static #checkMissingAtomic(context) {
|
|
1872
|
+
return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
|
|
1723
1873
|
}
|
|
1724
|
-
static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
|
|
1725
1874
|
// Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
|
|
1726
1875
|
// replays stored fixes against new prop objects, so fixes that close over external state will
|
|
1727
1876
|
// produce inconsistent results on cache hits.
|
|
@@ -1735,10 +1884,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1735
1884
|
};
|
|
1736
1885
|
static #checkInvalidAriaRelevant({ props }) {
|
|
1737
1886
|
const relevant = props["aria-relevant"];
|
|
1738
|
-
if (relevant === void 0) return
|
|
1739
|
-
if (typeof relevant !== "string") return
|
|
1887
|
+
if (relevant === void 0) return NO_VIOLATIONS2;
|
|
1888
|
+
if (typeof relevant !== "string") return NO_VIOLATIONS2;
|
|
1740
1889
|
const tokens = relevant.trim().split(/\s+/);
|
|
1741
|
-
const invalid = tokens.filter((t) => !
|
|
1890
|
+
const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
|
|
1742
1891
|
if (invalid.length > 0) {
|
|
1743
1892
|
return [
|
|
1744
1893
|
{
|
|
@@ -1763,7 +1912,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1763
1912
|
}
|
|
1764
1913
|
];
|
|
1765
1914
|
}
|
|
1766
|
-
return
|
|
1915
|
+
return NO_VIOLATIONS2;
|
|
1767
1916
|
}
|
|
1768
1917
|
};
|
|
1769
1918
|
|
|
@@ -2083,6 +2232,425 @@ var readonlyProps = ({
|
|
|
2083
2232
|
// ../core/src/html/evaluators.ts
|
|
2084
2233
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
2085
2234
|
|
|
2235
|
+
// ../core/src/html/spec/vocabulary/input.ts
|
|
2236
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2237
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2238
|
+
"number",
|
|
2239
|
+
"range",
|
|
2240
|
+
"date",
|
|
2241
|
+
"month",
|
|
2242
|
+
"week",
|
|
2243
|
+
"time",
|
|
2244
|
+
"datetime-local"
|
|
2245
|
+
];
|
|
2246
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2247
|
+
...TEXT_INPUT_TYPES,
|
|
2248
|
+
...NUMERIC_INPUT_TYPES,
|
|
2249
|
+
"checkbox",
|
|
2250
|
+
"radio",
|
|
2251
|
+
"file",
|
|
2252
|
+
"color",
|
|
2253
|
+
"hidden",
|
|
2254
|
+
"button",
|
|
2255
|
+
"submit",
|
|
2256
|
+
"reset",
|
|
2257
|
+
"image"
|
|
2258
|
+
]);
|
|
2259
|
+
|
|
2260
|
+
// ../core/src/html/spec/attributes/input.ts
|
|
2261
|
+
var INPUT_ATTRIBUTE_TYPE_POLICIES = [
|
|
2262
|
+
{ attribute: "checked", allowedTypes: ["checkbox", "radio"] },
|
|
2263
|
+
{ attribute: "multiple", allowedTypes: ["email", "file"] },
|
|
2264
|
+
{ attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2265
|
+
{ attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
|
|
2266
|
+
{ attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
|
|
2267
|
+
{ attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2268
|
+
{ attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2269
|
+
{ attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
|
|
2270
|
+
{ attribute: "accept", allowedTypes: ["file"] },
|
|
2271
|
+
{ attribute: "capture", allowedTypes: ["file"] },
|
|
2272
|
+
{ attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
|
|
2273
|
+
{ attribute: "alt", allowedTypes: ["image"] },
|
|
2274
|
+
{ attribute: "height", allowedTypes: ["image"] },
|
|
2275
|
+
{ attribute: "width", allowedTypes: ["image"] }
|
|
2276
|
+
];
|
|
2277
|
+
|
|
2278
|
+
// ../core/src/html/spec/constraints/input.ts
|
|
2279
|
+
var REQUIRED_READONLY_CONFLICT = {
|
|
2280
|
+
props: ["required", "readOnly"],
|
|
2281
|
+
diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
|
|
2282
|
+
};
|
|
2283
|
+
var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
|
|
2284
|
+
REQUIRED_READONLY_CONFLICT
|
|
2285
|
+
];
|
|
2286
|
+
|
|
2287
|
+
// ../core/src/html/spec/validators/attribute-type-validator.ts
|
|
2288
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2289
|
+
function omit(props, key) {
|
|
2290
|
+
const next = { ...props };
|
|
2291
|
+
delete next[key];
|
|
2292
|
+
return next;
|
|
2293
|
+
}
|
|
2294
|
+
function removeAttributeFix(attribute) {
|
|
2295
|
+
return {
|
|
2296
|
+
kind: `removeAttribute:${attribute}`,
|
|
2297
|
+
apply: ({ props }) => {
|
|
2298
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2299
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
function createInputAttributeTypeRule({
|
|
2304
|
+
attribute,
|
|
2305
|
+
allowedTypes
|
|
2306
|
+
}) {
|
|
2307
|
+
const rule = ({ tag, props }) => {
|
|
2308
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2309
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2310
|
+
if (allowedTypes.includes(type)) return [];
|
|
2311
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2312
|
+
return [
|
|
2313
|
+
{
|
|
2314
|
+
valid: false,
|
|
2315
|
+
fixable: true,
|
|
2316
|
+
severity: diagnostic.severity,
|
|
2317
|
+
fix: removeAttributeFix(attribute),
|
|
2318
|
+
diagnostic
|
|
2319
|
+
}
|
|
2320
|
+
];
|
|
2321
|
+
};
|
|
2322
|
+
return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// ../core/src/html/spec/validators/mutually-exclusive-validator.ts
|
|
2326
|
+
function createMutuallyExclusiveRule({
|
|
2327
|
+
props: conflictingProps,
|
|
2328
|
+
diagnostic: createDiagnostic
|
|
2329
|
+
}) {
|
|
2330
|
+
const [first, second] = conflictingProps;
|
|
2331
|
+
const rule = ({ tag, props }) => {
|
|
2332
|
+
if (tag !== "input" || !props[first] || !props[second]) return [];
|
|
2333
|
+
const diagnostic = createDiagnostic();
|
|
2334
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2335
|
+
};
|
|
2336
|
+
return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
// ../core/src/html/input-rules.ts
|
|
2340
|
+
var policyByAttribute = Object.fromEntries(
|
|
2341
|
+
INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
|
|
2342
|
+
);
|
|
2343
|
+
function policyFor(attribute) {
|
|
2344
|
+
return policyByAttribute[attribute];
|
|
2345
|
+
}
|
|
2346
|
+
var supportedInputTypeRule = Object.assign(
|
|
2347
|
+
({ tag, props }) => {
|
|
2348
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2349
|
+
const type = props.type;
|
|
2350
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2351
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2352
|
+
return [
|
|
2353
|
+
{
|
|
2354
|
+
valid: false,
|
|
2355
|
+
fixable: false,
|
|
2356
|
+
severity: diagnostic.severity,
|
|
2357
|
+
diagnostic
|
|
2358
|
+
}
|
|
2359
|
+
];
|
|
2360
|
+
},
|
|
2361
|
+
{ readsProps: ["type"], tags: ["input"] }
|
|
2362
|
+
);
|
|
2363
|
+
var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
|
|
2364
|
+
var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
|
|
2365
|
+
var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
|
|
2366
|
+
var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
|
|
2367
|
+
var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
|
|
2368
|
+
var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
|
|
2369
|
+
var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
|
|
2370
|
+
var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
|
|
2371
|
+
var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
|
|
2372
|
+
var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
|
|
2373
|
+
var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
|
|
2374
|
+
var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
|
|
2375
|
+
var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
|
|
2376
|
+
var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
|
|
2377
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2378
|
+
({ tag, props }) => {
|
|
2379
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2380
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2381
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2382
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2383
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2384
|
+
},
|
|
2385
|
+
{
|
|
2386
|
+
readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
|
|
2387
|
+
tags: ["input"]
|
|
2388
|
+
}
|
|
2389
|
+
);
|
|
2390
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2391
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2392
|
+
({ tag, props }) => {
|
|
2393
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2394
|
+
const autoComplete = props.autoComplete;
|
|
2395
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2396
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2397
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2398
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2399
|
+
},
|
|
2400
|
+
{ readsProps: ["type", "autoComplete"], tags: ["input"] }
|
|
2401
|
+
);
|
|
2402
|
+
var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
|
|
2403
|
+
var INPUT_RULES = [
|
|
2404
|
+
supportedInputTypeRule,
|
|
2405
|
+
checkedRequiresCheckableTypeRule,
|
|
2406
|
+
multipleRequiresSupportedTypeRule,
|
|
2407
|
+
maxLengthRequiresTextTypeRule,
|
|
2408
|
+
minLengthRequiresTextTypeRule,
|
|
2409
|
+
patternRequiresTextTypeRule,
|
|
2410
|
+
minRequiresNumericTypeRule,
|
|
2411
|
+
maxRequiresNumericTypeRule,
|
|
2412
|
+
stepRequiresNumericTypeRule,
|
|
2413
|
+
acceptRequiresFileTypeRule,
|
|
2414
|
+
captureRequiresFileTypeRule,
|
|
2415
|
+
sizeRequiresTextTypeRule,
|
|
2416
|
+
altRequiresImageTypeRule,
|
|
2417
|
+
heightRequiresImageTypeRule,
|
|
2418
|
+
widthRequiresImageTypeRule,
|
|
2419
|
+
inputAccessibleNameRule,
|
|
2420
|
+
passwordAutocompleteRule,
|
|
2421
|
+
requiredReadOnlyConflictRule
|
|
2422
|
+
];
|
|
2423
|
+
|
|
2424
|
+
// ../core/src/html/spec/types.ts
|
|
2425
|
+
function definePropRolePolicy(prop, map2, fallback) {
|
|
2426
|
+
return { kind: "byProp", prop, map: map2, fallback };
|
|
2427
|
+
}
|
|
2428
|
+
function resolveAllowedRoles(spec, props) {
|
|
2429
|
+
const policy = spec.allowedRoles;
|
|
2430
|
+
if (!policy) return void 0;
|
|
2431
|
+
switch (policy.kind) {
|
|
2432
|
+
case "fixed":
|
|
2433
|
+
return policy.roles;
|
|
2434
|
+
case "byProp": {
|
|
2435
|
+
const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
|
|
2436
|
+
return policy.map[value];
|
|
2437
|
+
}
|
|
2438
|
+
case "dynamic":
|
|
2439
|
+
return policy.resolve({ props });
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// ../core/src/html/spec/roles/input.ts
|
|
2444
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2445
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2446
|
+
radio: ["menuitemradio"],
|
|
2447
|
+
range: [],
|
|
2448
|
+
number: [],
|
|
2449
|
+
search: ["combobox"],
|
|
2450
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2451
|
+
email: ["combobox"],
|
|
2452
|
+
tel: ["combobox"],
|
|
2453
|
+
url: ["combobox"],
|
|
2454
|
+
button: [
|
|
2455
|
+
"link",
|
|
2456
|
+
"menuitem",
|
|
2457
|
+
"menuitemcheckbox",
|
|
2458
|
+
"menuitemradio",
|
|
2459
|
+
"option",
|
|
2460
|
+
"radio",
|
|
2461
|
+
"switch",
|
|
2462
|
+
"tab"
|
|
2463
|
+
],
|
|
2464
|
+
submit: [
|
|
2465
|
+
"link",
|
|
2466
|
+
"menuitem",
|
|
2467
|
+
"menuitemcheckbox",
|
|
2468
|
+
"menuitemradio",
|
|
2469
|
+
"option",
|
|
2470
|
+
"radio",
|
|
2471
|
+
"switch",
|
|
2472
|
+
"tab"
|
|
2473
|
+
],
|
|
2474
|
+
reset: [
|
|
2475
|
+
"link",
|
|
2476
|
+
"menuitem",
|
|
2477
|
+
"menuitemcheckbox",
|
|
2478
|
+
"menuitemradio",
|
|
2479
|
+
"option",
|
|
2480
|
+
"radio",
|
|
2481
|
+
"switch",
|
|
2482
|
+
"tab"
|
|
2483
|
+
],
|
|
2484
|
+
image: [
|
|
2485
|
+
"link",
|
|
2486
|
+
"menuitem",
|
|
2487
|
+
"menuitemcheckbox",
|
|
2488
|
+
"menuitemradio",
|
|
2489
|
+
"option",
|
|
2490
|
+
"radio",
|
|
2491
|
+
"switch",
|
|
2492
|
+
"tab"
|
|
2493
|
+
],
|
|
2494
|
+
hidden: []
|
|
2495
|
+
};
|
|
2496
|
+
|
|
2497
|
+
// ../core/src/html/spec/elements/input.ts
|
|
2498
|
+
var inputElementSpec = {
|
|
2499
|
+
tag: "input",
|
|
2500
|
+
allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
|
|
2501
|
+
attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
|
|
2502
|
+
mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
|
|
2503
|
+
};
|
|
2504
|
+
|
|
2505
|
+
// ../core/src/html/spec/roles/img.ts
|
|
2506
|
+
var IMG_NAMED_ROLES = [
|
|
2507
|
+
"button",
|
|
2508
|
+
"checkbox",
|
|
2509
|
+
"link",
|
|
2510
|
+
"menuitem",
|
|
2511
|
+
"menuitemcheckbox",
|
|
2512
|
+
"menuitemradio",
|
|
2513
|
+
"option",
|
|
2514
|
+
"progressbar",
|
|
2515
|
+
"scrollbar",
|
|
2516
|
+
"separator",
|
|
2517
|
+
"slider",
|
|
2518
|
+
"switch",
|
|
2519
|
+
"tab",
|
|
2520
|
+
"treeitem"
|
|
2521
|
+
];
|
|
2522
|
+
|
|
2523
|
+
// ../core/src/html/spec/elements/img.ts
|
|
2524
|
+
var imgElementSpec = {
|
|
2525
|
+
tag: "img",
|
|
2526
|
+
allowedRoles: {
|
|
2527
|
+
kind: "dynamic",
|
|
2528
|
+
resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
|
|
2529
|
+
}
|
|
2530
|
+
};
|
|
2531
|
+
|
|
2532
|
+
// ../core/src/html/spec/roles/table.ts
|
|
2533
|
+
var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
|
|
2534
|
+
|
|
2535
|
+
// ../core/src/html/spec/elements/table.ts
|
|
2536
|
+
var tableElementSpec = {
|
|
2537
|
+
tag: "table",
|
|
2538
|
+
allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
|
|
2539
|
+
};
|
|
2540
|
+
|
|
2541
|
+
// ../core/src/html/role-restrictions.ts
|
|
2542
|
+
var ALLOWED_ROLES = {
|
|
2543
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2544
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2545
|
+
footer: ["group", "none", "presentation"],
|
|
2546
|
+
header: ["group", "none", "presentation"],
|
|
2547
|
+
main: [],
|
|
2548
|
+
nav: [],
|
|
2549
|
+
a: [
|
|
2550
|
+
"button",
|
|
2551
|
+
"checkbox",
|
|
2552
|
+
"menuitem",
|
|
2553
|
+
"menuitemcheckbox",
|
|
2554
|
+
"menuitemradio",
|
|
2555
|
+
"option",
|
|
2556
|
+
"radio",
|
|
2557
|
+
"switch",
|
|
2558
|
+
"tab",
|
|
2559
|
+
"treeitem"
|
|
2560
|
+
],
|
|
2561
|
+
button: [
|
|
2562
|
+
"checkbox",
|
|
2563
|
+
"link",
|
|
2564
|
+
"menuitem",
|
|
2565
|
+
"menuitemcheckbox",
|
|
2566
|
+
"menuitemradio",
|
|
2567
|
+
"option",
|
|
2568
|
+
"radio",
|
|
2569
|
+
"switch",
|
|
2570
|
+
"tab"
|
|
2571
|
+
],
|
|
2572
|
+
select: ["menu"],
|
|
2573
|
+
h1: ["tab", "presentation", "none"],
|
|
2574
|
+
h2: ["tab", "presentation", "none"],
|
|
2575
|
+
h3: ["tab", "presentation", "none"],
|
|
2576
|
+
h4: ["tab", "presentation", "none"],
|
|
2577
|
+
h5: ["tab", "presentation", "none"],
|
|
2578
|
+
h6: ["tab", "presentation", "none"],
|
|
2579
|
+
ul: [
|
|
2580
|
+
"directory",
|
|
2581
|
+
"group",
|
|
2582
|
+
"listbox",
|
|
2583
|
+
"menu",
|
|
2584
|
+
"menubar",
|
|
2585
|
+
"radiogroup",
|
|
2586
|
+
"tablist",
|
|
2587
|
+
"toolbar",
|
|
2588
|
+
"tree"
|
|
2589
|
+
],
|
|
2590
|
+
ol: [
|
|
2591
|
+
"directory",
|
|
2592
|
+
"group",
|
|
2593
|
+
"listbox",
|
|
2594
|
+
"menu",
|
|
2595
|
+
"menubar",
|
|
2596
|
+
"radiogroup",
|
|
2597
|
+
"tablist",
|
|
2598
|
+
"toolbar",
|
|
2599
|
+
"tree"
|
|
2600
|
+
],
|
|
2601
|
+
li: [
|
|
2602
|
+
"menuitem",
|
|
2603
|
+
"menuitemcheckbox",
|
|
2604
|
+
"menuitemradio",
|
|
2605
|
+
"option",
|
|
2606
|
+
"none",
|
|
2607
|
+
"presentation",
|
|
2608
|
+
"radio",
|
|
2609
|
+
"separator",
|
|
2610
|
+
"tab",
|
|
2611
|
+
"treeitem"
|
|
2612
|
+
],
|
|
2613
|
+
dialog: ["alertdialog"],
|
|
2614
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2615
|
+
};
|
|
2616
|
+
var ELEMENT_SPECS = {
|
|
2617
|
+
input: inputElementSpec,
|
|
2618
|
+
img: imgElementSpec,
|
|
2619
|
+
table: tableElementSpec
|
|
2620
|
+
};
|
|
2621
|
+
function getAllowedRoles(tag, props) {
|
|
2622
|
+
const spec = ELEMENT_SPECS[tag];
|
|
2623
|
+
if (spec) return resolveAllowedRoles(spec, props);
|
|
2624
|
+
return ALLOWED_ROLES[tag];
|
|
2625
|
+
}
|
|
2626
|
+
var removeRoleFix = {
|
|
2627
|
+
kind: "removeRole",
|
|
2628
|
+
apply: ({ props }) => {
|
|
2629
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2630
|
+
const { role: _role, ...rest } = props;
|
|
2631
|
+
return { applied: true, next: rest, previous: props };
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
var roleNotPermittedRule = Object.assign(
|
|
2635
|
+
({ tag, props, implicitRole }) => {
|
|
2636
|
+
const role = props.role;
|
|
2637
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2638
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2639
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2640
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2641
|
+
return [
|
|
2642
|
+
{
|
|
2643
|
+
valid: false,
|
|
2644
|
+
fixable: true,
|
|
2645
|
+
severity: diagnostic.severity,
|
|
2646
|
+
fix: removeRoleFix,
|
|
2647
|
+
diagnostic
|
|
2648
|
+
}
|
|
2649
|
+
];
|
|
2650
|
+
},
|
|
2651
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2652
|
+
);
|
|
2653
|
+
|
|
2086
2654
|
// ../core/src/html/aria-rules.ts
|
|
2087
2655
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
2088
2656
|
var removeLandmarkRoleOverride = {
|
|
@@ -2093,20 +2661,24 @@ var removeLandmarkRoleOverride = {
|
|
|
2093
2661
|
return { applied: true, next: rest, previous: props };
|
|
2094
2662
|
}
|
|
2095
2663
|
};
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
}
|
|
2664
|
+
var landmarkRoleRule = Object.assign(
|
|
2665
|
+
({ tag, props, implicitRole }) => {
|
|
2666
|
+
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2667
|
+
const role = props.role;
|
|
2668
|
+
if (!role || role === implicitRole) return [];
|
|
2669
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2670
|
+
return [
|
|
2671
|
+
{
|
|
2672
|
+
valid: false,
|
|
2673
|
+
fixable: true,
|
|
2674
|
+
severity: diagnostic.severity,
|
|
2675
|
+
fix: removeLandmarkRoleOverride,
|
|
2676
|
+
diagnostic
|
|
2677
|
+
}
|
|
2678
|
+
];
|
|
2679
|
+
},
|
|
2680
|
+
{ tags: [...LANDMARK_TAG_SET] }
|
|
2681
|
+
);
|
|
2110
2682
|
function requireAccessibleName({ tag, props }) {
|
|
2111
2683
|
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2112
2684
|
return [
|
|
@@ -2119,11 +2691,19 @@ function requireAccessibleName({ tag, props }) {
|
|
|
2119
2691
|
];
|
|
2120
2692
|
}
|
|
2121
2693
|
var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2694
|
+
var landmarkNameAdvisory = Object.assign(
|
|
2695
|
+
(ctx) => {
|
|
2696
|
+
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2697
|
+
return requireAccessibleName(ctx);
|
|
2698
|
+
},
|
|
2699
|
+
{ tags: [...NAMED_LANDMARK_TAGS] }
|
|
2700
|
+
);
|
|
2701
|
+
var HTML_ARIA_RULES = [
|
|
2702
|
+
landmarkRoleRule,
|
|
2703
|
+
landmarkNameAdvisory,
|
|
2704
|
+
roleNotPermittedRule,
|
|
2705
|
+
...INPUT_RULES
|
|
2706
|
+
];
|
|
2127
2707
|
|
|
2128
2708
|
// ../core/src/html/contracts.ts
|
|
2129
2709
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2218,6 +2798,62 @@ var figureContract = contract([
|
|
|
2218
2798
|
]);
|
|
2219
2799
|
var detailsContract = firstChildContract("summary", "summary");
|
|
2220
2800
|
var fieldsetContract = firstChildContract("legend", "legend");
|
|
2801
|
+
var objectContract = contract([
|
|
2802
|
+
{ name: "param", match: isTag("param") },
|
|
2803
|
+
{ name: "content", match: isOpenContent("param") }
|
|
2804
|
+
]);
|
|
2805
|
+
var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
|
|
2806
|
+
var buttonContract = closedContract([
|
|
2807
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2808
|
+
]);
|
|
2809
|
+
var anchorContract = closedContract([
|
|
2810
|
+
{ name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
|
|
2811
|
+
]);
|
|
2812
|
+
var LABELABLE_TAGS = [
|
|
2813
|
+
"button",
|
|
2814
|
+
"input",
|
|
2815
|
+
"meter",
|
|
2816
|
+
"output",
|
|
2817
|
+
"progress",
|
|
2818
|
+
"select",
|
|
2819
|
+
"textarea"
|
|
2820
|
+
];
|
|
2821
|
+
var labelContract = contract([
|
|
2822
|
+
{ name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
|
|
2823
|
+
]);
|
|
2824
|
+
var P_BLOCKED_TAGS = [
|
|
2825
|
+
"address",
|
|
2826
|
+
"article",
|
|
2827
|
+
"aside",
|
|
2828
|
+
"blockquote",
|
|
2829
|
+
"details",
|
|
2830
|
+
"dialog",
|
|
2831
|
+
"div",
|
|
2832
|
+
"dl",
|
|
2833
|
+
"fieldset",
|
|
2834
|
+
"figure",
|
|
2835
|
+
"footer",
|
|
2836
|
+
"form",
|
|
2837
|
+
"h1",
|
|
2838
|
+
"h2",
|
|
2839
|
+
"h3",
|
|
2840
|
+
"h4",
|
|
2841
|
+
"h5",
|
|
2842
|
+
"h6",
|
|
2843
|
+
"header",
|
|
2844
|
+
"hr",
|
|
2845
|
+
"main",
|
|
2846
|
+
"nav",
|
|
2847
|
+
"ol",
|
|
2848
|
+
"p",
|
|
2849
|
+
"pre",
|
|
2850
|
+
"section",
|
|
2851
|
+
"table",
|
|
2852
|
+
"ul"
|
|
2853
|
+
];
|
|
2854
|
+
var pContract = closedContract([
|
|
2855
|
+
{ name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
|
|
2856
|
+
]);
|
|
2221
2857
|
var mediaContract = contract([
|
|
2222
2858
|
{ name: "source", match: isTag("source") },
|
|
2223
2859
|
{ name: "track", match: isTag("track") },
|
|
@@ -2272,6 +2908,11 @@ var htmlContracts = {
|
|
|
2272
2908
|
details: detailsContract,
|
|
2273
2909
|
fieldset: fieldsetContract,
|
|
2274
2910
|
dialog: dialogContract,
|
|
2911
|
+
object: objectContract,
|
|
2912
|
+
button: buttonContract,
|
|
2913
|
+
a: anchorContract,
|
|
2914
|
+
label: labelContract,
|
|
2915
|
+
p: pContract,
|
|
2275
2916
|
head: headContract,
|
|
2276
2917
|
html: htmlContract
|
|
2277
2918
|
};
|
|
@@ -2584,21 +3225,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2584
3225
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2585
3226
|
|
|
2586
3227
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2587
|
-
import { DiagnosticCategory as
|
|
3228
|
+
import { DiagnosticCategory as DiagnosticCategory6, DiagnosticCode as DiagnosticCode6 } from "../_shared/diagnostics.js";
|
|
2588
3229
|
var PluginDiagnostics = {
|
|
2589
3230
|
invalidShape(received) {
|
|
2590
3231
|
const got = received === null ? "null" : typeof received;
|
|
2591
3232
|
return {
|
|
2592
|
-
code:
|
|
2593
|
-
category:
|
|
3233
|
+
code: DiagnosticCode6.PluginInvalidShape,
|
|
3234
|
+
category: DiagnosticCategory6.Internal,
|
|
2594
3235
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2595
3236
|
};
|
|
2596
3237
|
},
|
|
2597
3238
|
pipelineReturnType(received) {
|
|
2598
3239
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2599
3240
|
return {
|
|
2600
|
-
code:
|
|
2601
|
-
category:
|
|
3241
|
+
code: DiagnosticCode6.PluginPipelineReturnType,
|
|
3242
|
+
category: DiagnosticCategory6.Internal,
|
|
2602
3243
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2603
3244
|
};
|
|
2604
3245
|
}
|