praxis-kit 6.2.0 → 6.2.3
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 +16 -0
- package/dist/_shared/diagnostics.js +16 -0
- package/dist/{chunk-4YVNOMUS.js → chunk-H3D2JA3J.js} +491 -26
- package/dist/lit/index.js +480 -15
- package/dist/preact/index.js +491 -26
- package/dist/react/index.js +1 -1
- package/dist/react/legacy.js +1 -1
- package/dist/solid/index.js +491 -26
- package/dist/svelte/index.js +491 -26
- package/dist/tailwind/index.js +1 -1
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.js +15 -0
- package/dist/vite-plugin/index.d.ts +16 -0
- package/dist/vue/index.js +491 -26
- package/dist/web/index.js +480 -15
- package/package.json +9 -2
package/dist/react/legacy.js
CHANGED
package/dist/solid/index.js
CHANGED
|
@@ -954,11 +954,24 @@ 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
|
+
};
|
|
957
969
|
var HtmlDiagnostics = {
|
|
958
970
|
emptyRole(tag) {
|
|
959
971
|
return {
|
|
960
972
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
961
973
|
category: DiagnosticCategory3.HTML,
|
|
974
|
+
severity: "warning",
|
|
962
975
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
963
976
|
};
|
|
964
977
|
},
|
|
@@ -966,6 +979,7 @@ var HtmlDiagnostics = {
|
|
|
966
979
|
return {
|
|
967
980
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
968
981
|
category: DiagnosticCategory3.HTML,
|
|
982
|
+
severity: "warning",
|
|
969
983
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
970
984
|
};
|
|
971
985
|
},
|
|
@@ -973,6 +987,7 @@ var HtmlDiagnostics = {
|
|
|
973
987
|
return {
|
|
974
988
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
975
989
|
category: DiagnosticCategory3.HTML,
|
|
990
|
+
severity: "error",
|
|
976
991
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
977
992
|
};
|
|
978
993
|
},
|
|
@@ -980,6 +995,7 @@ var HtmlDiagnostics = {
|
|
|
980
995
|
return {
|
|
981
996
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
982
997
|
category: DiagnosticCategory3.HTML,
|
|
998
|
+
severity: "error",
|
|
983
999
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
984
1000
|
};
|
|
985
1001
|
},
|
|
@@ -987,6 +1003,7 @@ var HtmlDiagnostics = {
|
|
|
987
1003
|
return {
|
|
988
1004
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
989
1005
|
category: DiagnosticCategory3.HTML,
|
|
1006
|
+
severity: "error",
|
|
990
1007
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
991
1008
|
};
|
|
992
1009
|
},
|
|
@@ -994,34 +1011,148 @@ var HtmlDiagnostics = {
|
|
|
994
1011
|
return {
|
|
995
1012
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
996
1013
|
category: DiagnosticCategory3.HTML,
|
|
1014
|
+
severity: "error",
|
|
997
1015
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
998
1016
|
};
|
|
1017
|
+
},
|
|
1018
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
1019
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
1020
|
+
return {
|
|
1021
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
1022
|
+
category: DiagnosticCategory3.HTML,
|
|
1023
|
+
severity: "error",
|
|
1024
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
1025
|
+
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.'
|
|
1026
|
+
};
|
|
1027
|
+
},
|
|
1028
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
1029
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
1030
|
+
input: {
|
|
1031
|
+
unsupportedType(type) {
|
|
1032
|
+
return {
|
|
1033
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
1034
|
+
category: DiagnosticCategory3.HTML,
|
|
1035
|
+
severity: "warning",
|
|
1036
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
1037
|
+
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).',
|
|
1038
|
+
suggestions: [
|
|
1039
|
+
{
|
|
1040
|
+
title: "Check for a typo in the type value",
|
|
1041
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
1042
|
+
}
|
|
1043
|
+
]
|
|
1044
|
+
};
|
|
1045
|
+
},
|
|
1046
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
1047
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
1048
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
1049
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
1050
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
1051
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
1052
|
+
return {
|
|
1053
|
+
code,
|
|
1054
|
+
category: DiagnosticCategory3.HTML,
|
|
1055
|
+
severity: "warning",
|
|
1056
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
1057
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
1058
|
+
suggestions: [
|
|
1059
|
+
{
|
|
1060
|
+
title: `Remove "${attribute}"`,
|
|
1061
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
1062
|
+
}
|
|
1063
|
+
]
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
999
1066
|
}
|
|
1000
1067
|
};
|
|
1001
1068
|
|
|
1002
|
-
// ../../lib/contract/src/diagnostics/
|
|
1069
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
1003
1070
|
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "../_shared/diagnostics.js";
|
|
1071
|
+
function accessibilityFact(input) {
|
|
1072
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
1073
|
+
}
|
|
1074
|
+
var InputAccessibilityDiagnostics = {
|
|
1075
|
+
missingAccessibleName() {
|
|
1076
|
+
return accessibilityFact({
|
|
1077
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
1078
|
+
severity: "warning",
|
|
1079
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1080
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
1081
|
+
suggestions: [
|
|
1082
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1083
|
+
{
|
|
1084
|
+
title: "Add an associated <label>",
|
|
1085
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1086
|
+
}
|
|
1087
|
+
]
|
|
1088
|
+
});
|
|
1089
|
+
},
|
|
1090
|
+
placeholderIsNotLabel() {
|
|
1091
|
+
return accessibilityFact({
|
|
1092
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
1093
|
+
severity: "warning",
|
|
1094
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1095
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1096
|
+
suggestions: [
|
|
1097
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1098
|
+
{
|
|
1099
|
+
title: "Add an associated <label>",
|
|
1100
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1101
|
+
}
|
|
1102
|
+
]
|
|
1103
|
+
});
|
|
1104
|
+
},
|
|
1105
|
+
passwordMissingAutocomplete() {
|
|
1106
|
+
return accessibilityFact({
|
|
1107
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1108
|
+
severity: "warning",
|
|
1109
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1110
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1111
|
+
suggestions: [
|
|
1112
|
+
{
|
|
1113
|
+
title: 'Set autoComplete="current-password"',
|
|
1114
|
+
description: "Use this for sign-in forms."
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
title: 'Set autoComplete="new-password"',
|
|
1118
|
+
description: "Use this for sign-up / change-password forms."
|
|
1119
|
+
}
|
|
1120
|
+
]
|
|
1121
|
+
});
|
|
1122
|
+
},
|
|
1123
|
+
requiredReadOnlyConflict() {
|
|
1124
|
+
return accessibilityFact({
|
|
1125
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1126
|
+
severity: "warning",
|
|
1127
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1128
|
+
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."
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1134
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
1004
1135
|
var SlotDiagnostics = {
|
|
1005
1136
|
exclusive(name) {
|
|
1006
1137
|
return {
|
|
1007
|
-
code:
|
|
1008
|
-
category:
|
|
1138
|
+
code: DiagnosticCode5.SlotExclusive,
|
|
1139
|
+
category: DiagnosticCategory5.Contract,
|
|
1009
1140
|
component: name,
|
|
1010
1141
|
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
1011
1142
|
};
|
|
1012
1143
|
},
|
|
1013
1144
|
singleChildRequired(name, elementTerm) {
|
|
1014
1145
|
return {
|
|
1015
|
-
code:
|
|
1016
|
-
category:
|
|
1146
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1147
|
+
category: DiagnosticCategory5.Contract,
|
|
1017
1148
|
component: name,
|
|
1018
1149
|
message: `${name}: asChild requires a ${elementTerm} child`
|
|
1019
1150
|
};
|
|
1020
1151
|
},
|
|
1021
1152
|
singleChildExceeded(name, elementTerm, count) {
|
|
1022
1153
|
return {
|
|
1023
|
-
code:
|
|
1024
|
-
category:
|
|
1154
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1155
|
+
category: DiagnosticCategory5.Contract,
|
|
1025
1156
|
component: name,
|
|
1026
1157
|
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
1027
1158
|
};
|
|
@@ -1029,16 +1160,16 @@ var SlotDiagnostics = {
|
|
|
1029
1160
|
discardedChildren(name, elementTerm, count) {
|
|
1030
1161
|
const suffix = count === 1 ? "" : "ren";
|
|
1031
1162
|
return {
|
|
1032
|
-
code:
|
|
1033
|
-
category:
|
|
1163
|
+
code: DiagnosticCode5.SlotDiscardedChildren,
|
|
1164
|
+
category: DiagnosticCategory5.Contract,
|
|
1034
1165
|
component: name,
|
|
1035
1166
|
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
1036
1167
|
};
|
|
1037
1168
|
},
|
|
1038
1169
|
renderFnRequired(name, received) {
|
|
1039
1170
|
return {
|
|
1040
|
-
code:
|
|
1041
|
-
category:
|
|
1171
|
+
code: DiagnosticCode5.SlotRenderFn,
|
|
1172
|
+
category: DiagnosticCategory5.Contract,
|
|
1042
1173
|
component: name,
|
|
1043
1174
|
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
1044
1175
|
};
|
|
@@ -1100,7 +1231,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1100
1231
|
tag,
|
|
1101
1232
|
role: "",
|
|
1102
1233
|
attribute: void 0,
|
|
1103
|
-
severity:
|
|
1234
|
+
severity: d.severity,
|
|
1104
1235
|
phase: "evaluate"
|
|
1105
1236
|
}
|
|
1106
1237
|
]
|
|
@@ -1359,13 +1490,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1359
1490
|
const role = props.role;
|
|
1360
1491
|
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
|
|
1361
1492
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1493
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1362
1494
|
return [
|
|
1363
1495
|
{
|
|
1364
1496
|
valid: false,
|
|
1365
1497
|
fixable: true,
|
|
1366
|
-
severity:
|
|
1498
|
+
severity: diagnostic.severity,
|
|
1367
1499
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1368
|
-
diagnostic
|
|
1500
|
+
diagnostic
|
|
1369
1501
|
}
|
|
1370
1502
|
];
|
|
1371
1503
|
}
|
|
@@ -1374,13 +1506,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1374
1506
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1375
1507
|
const role = props.role;
|
|
1376
1508
|
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
|
|
1509
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1377
1510
|
return [
|
|
1378
1511
|
{
|
|
1379
1512
|
valid: false,
|
|
1380
1513
|
fixable: true,
|
|
1381
|
-
severity:
|
|
1514
|
+
severity: diagnostic.severity,
|
|
1382
1515
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1383
|
-
diagnostic
|
|
1516
|
+
diagnostic
|
|
1384
1517
|
}
|
|
1385
1518
|
];
|
|
1386
1519
|
}
|
|
@@ -1388,13 +1521,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1388
1521
|
const role = props.role;
|
|
1389
1522
|
if (role !== "region") return NO_VIOLATIONS;
|
|
1390
1523
|
if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
|
|
1524
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1391
1525
|
return [
|
|
1392
1526
|
{
|
|
1393
1527
|
valid: false,
|
|
1394
1528
|
fixable: true,
|
|
1395
|
-
severity:
|
|
1529
|
+
severity: diagnostic.severity,
|
|
1396
1530
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1397
|
-
diagnostic
|
|
1531
|
+
diagnostic
|
|
1398
1532
|
}
|
|
1399
1533
|
];
|
|
1400
1534
|
}
|
|
@@ -2083,6 +2217,331 @@ var readonlyProps = ({
|
|
|
2083
2217
|
// ../core/src/html/evaluators.ts
|
|
2084
2218
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
2085
2219
|
|
|
2220
|
+
// ../core/src/html/input-rules.ts
|
|
2221
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2222
|
+
function omit(props, key) {
|
|
2223
|
+
const next = { ...props };
|
|
2224
|
+
delete next[key];
|
|
2225
|
+
return next;
|
|
2226
|
+
}
|
|
2227
|
+
function removeAttributeFix(attribute) {
|
|
2228
|
+
return {
|
|
2229
|
+
kind: `removeAttribute:${attribute}`,
|
|
2230
|
+
apply: ({ props }) => {
|
|
2231
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2232
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
function inputAttributeRequiresType(attribute, allowedTypes) {
|
|
2237
|
+
const rule = ({ tag, props }) => {
|
|
2238
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2239
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2240
|
+
if (allowedTypes.includes(type)) return [];
|
|
2241
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2242
|
+
return [
|
|
2243
|
+
{
|
|
2244
|
+
valid: false,
|
|
2245
|
+
fixable: true,
|
|
2246
|
+
severity: diagnostic.severity,
|
|
2247
|
+
fix: removeAttributeFix(attribute),
|
|
2248
|
+
diagnostic
|
|
2249
|
+
}
|
|
2250
|
+
];
|
|
2251
|
+
};
|
|
2252
|
+
return Object.assign(rule, { readsProps: ["type", attribute] });
|
|
2253
|
+
}
|
|
2254
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2255
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2256
|
+
"number",
|
|
2257
|
+
"range",
|
|
2258
|
+
"date",
|
|
2259
|
+
"month",
|
|
2260
|
+
"week",
|
|
2261
|
+
"time",
|
|
2262
|
+
"datetime-local"
|
|
2263
|
+
];
|
|
2264
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2265
|
+
...TEXT_INPUT_TYPES,
|
|
2266
|
+
...NUMERIC_INPUT_TYPES,
|
|
2267
|
+
"checkbox",
|
|
2268
|
+
"radio",
|
|
2269
|
+
"file",
|
|
2270
|
+
"color",
|
|
2271
|
+
"hidden",
|
|
2272
|
+
"button",
|
|
2273
|
+
"submit",
|
|
2274
|
+
"reset",
|
|
2275
|
+
"image"
|
|
2276
|
+
]);
|
|
2277
|
+
var supportedInputTypeRule = Object.assign(
|
|
2278
|
+
({ tag, props }) => {
|
|
2279
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2280
|
+
const type = props.type;
|
|
2281
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2282
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2283
|
+
return [
|
|
2284
|
+
{
|
|
2285
|
+
valid: false,
|
|
2286
|
+
fixable: false,
|
|
2287
|
+
severity: diagnostic.severity,
|
|
2288
|
+
diagnostic
|
|
2289
|
+
}
|
|
2290
|
+
];
|
|
2291
|
+
},
|
|
2292
|
+
{ readsProps: ["type"] }
|
|
2293
|
+
);
|
|
2294
|
+
var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
|
|
2295
|
+
"checkbox",
|
|
2296
|
+
"radio"
|
|
2297
|
+
]);
|
|
2298
|
+
var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
|
|
2299
|
+
"email",
|
|
2300
|
+
"file"
|
|
2301
|
+
]);
|
|
2302
|
+
var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2303
|
+
"maxLength",
|
|
2304
|
+
TEXT_INPUT_TYPES
|
|
2305
|
+
);
|
|
2306
|
+
var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2307
|
+
"minLength",
|
|
2308
|
+
TEXT_INPUT_TYPES
|
|
2309
|
+
);
|
|
2310
|
+
var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
|
|
2311
|
+
var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
|
|
2312
|
+
var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
|
|
2313
|
+
var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
|
|
2314
|
+
var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
|
|
2315
|
+
var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
|
|
2316
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2317
|
+
({ tag, props }) => {
|
|
2318
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2319
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2320
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2321
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2322
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2323
|
+
},
|
|
2324
|
+
{ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
|
|
2325
|
+
);
|
|
2326
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2327
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2328
|
+
({ tag, props }) => {
|
|
2329
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2330
|
+
const autoComplete = props.autoComplete;
|
|
2331
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2332
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2333
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2334
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2335
|
+
},
|
|
2336
|
+
{ readsProps: ["type", "autoComplete"] }
|
|
2337
|
+
);
|
|
2338
|
+
var requiredReadOnlyConflictRule = Object.assign(
|
|
2339
|
+
({ tag, props }) => {
|
|
2340
|
+
if (tag !== "input" || !props.required || !props.readOnly) return [];
|
|
2341
|
+
const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
|
|
2342
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2343
|
+
},
|
|
2344
|
+
{ readsProps: ["required", "readOnly"] }
|
|
2345
|
+
);
|
|
2346
|
+
var INPUT_RULES = [
|
|
2347
|
+
supportedInputTypeRule,
|
|
2348
|
+
checkedRequiresCheckableTypeRule,
|
|
2349
|
+
multipleRequiresSupportedTypeRule,
|
|
2350
|
+
maxLengthRequiresTextTypeRule,
|
|
2351
|
+
minLengthRequiresTextTypeRule,
|
|
2352
|
+
patternRequiresTextTypeRule,
|
|
2353
|
+
minRequiresNumericTypeRule,
|
|
2354
|
+
maxRequiresNumericTypeRule,
|
|
2355
|
+
stepRequiresNumericTypeRule,
|
|
2356
|
+
acceptRequiresFileTypeRule,
|
|
2357
|
+
captureRequiresFileTypeRule,
|
|
2358
|
+
inputAccessibleNameRule,
|
|
2359
|
+
passwordAutocompleteRule,
|
|
2360
|
+
requiredReadOnlyConflictRule
|
|
2361
|
+
];
|
|
2362
|
+
|
|
2363
|
+
// ../core/src/html/role-restrictions.ts
|
|
2364
|
+
var ALLOWED_ROLES = {
|
|
2365
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2366
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2367
|
+
footer: ["group", "none", "presentation"],
|
|
2368
|
+
header: ["group", "none", "presentation"],
|
|
2369
|
+
main: [],
|
|
2370
|
+
nav: [],
|
|
2371
|
+
a: [
|
|
2372
|
+
"button",
|
|
2373
|
+
"checkbox",
|
|
2374
|
+
"menuitem",
|
|
2375
|
+
"menuitemcheckbox",
|
|
2376
|
+
"menuitemradio",
|
|
2377
|
+
"option",
|
|
2378
|
+
"radio",
|
|
2379
|
+
"switch",
|
|
2380
|
+
"tab",
|
|
2381
|
+
"treeitem"
|
|
2382
|
+
],
|
|
2383
|
+
button: [
|
|
2384
|
+
"checkbox",
|
|
2385
|
+
"link",
|
|
2386
|
+
"menuitem",
|
|
2387
|
+
"menuitemcheckbox",
|
|
2388
|
+
"menuitemradio",
|
|
2389
|
+
"option",
|
|
2390
|
+
"radio",
|
|
2391
|
+
"switch",
|
|
2392
|
+
"tab"
|
|
2393
|
+
],
|
|
2394
|
+
select: ["menu"],
|
|
2395
|
+
h1: ["tab", "presentation", "none"],
|
|
2396
|
+
h2: ["tab", "presentation", "none"],
|
|
2397
|
+
h3: ["tab", "presentation", "none"],
|
|
2398
|
+
h4: ["tab", "presentation", "none"],
|
|
2399
|
+
h5: ["tab", "presentation", "none"],
|
|
2400
|
+
h6: ["tab", "presentation", "none"],
|
|
2401
|
+
ul: [
|
|
2402
|
+
"directory",
|
|
2403
|
+
"group",
|
|
2404
|
+
"listbox",
|
|
2405
|
+
"menu",
|
|
2406
|
+
"menubar",
|
|
2407
|
+
"radiogroup",
|
|
2408
|
+
"tablist",
|
|
2409
|
+
"toolbar",
|
|
2410
|
+
"tree"
|
|
2411
|
+
],
|
|
2412
|
+
ol: [
|
|
2413
|
+
"directory",
|
|
2414
|
+
"group",
|
|
2415
|
+
"listbox",
|
|
2416
|
+
"menu",
|
|
2417
|
+
"menubar",
|
|
2418
|
+
"radiogroup",
|
|
2419
|
+
"tablist",
|
|
2420
|
+
"toolbar",
|
|
2421
|
+
"tree"
|
|
2422
|
+
],
|
|
2423
|
+
li: [
|
|
2424
|
+
"menuitem",
|
|
2425
|
+
"menuitemcheckbox",
|
|
2426
|
+
"menuitemradio",
|
|
2427
|
+
"option",
|
|
2428
|
+
"none",
|
|
2429
|
+
"presentation",
|
|
2430
|
+
"radio",
|
|
2431
|
+
"separator",
|
|
2432
|
+
"tab",
|
|
2433
|
+
"treeitem"
|
|
2434
|
+
],
|
|
2435
|
+
table: ["grid", "treegrid"],
|
|
2436
|
+
dialog: ["alertdialog"],
|
|
2437
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2438
|
+
};
|
|
2439
|
+
var IMG_NAMED_ROLES = [
|
|
2440
|
+
"button",
|
|
2441
|
+
"checkbox",
|
|
2442
|
+
"link",
|
|
2443
|
+
"menuitem",
|
|
2444
|
+
"menuitemcheckbox",
|
|
2445
|
+
"menuitemradio",
|
|
2446
|
+
"option",
|
|
2447
|
+
"progressbar",
|
|
2448
|
+
"scrollbar",
|
|
2449
|
+
"separator",
|
|
2450
|
+
"slider",
|
|
2451
|
+
"switch",
|
|
2452
|
+
"tab",
|
|
2453
|
+
"treeitem"
|
|
2454
|
+
];
|
|
2455
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2456
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2457
|
+
radio: ["menuitemradio"],
|
|
2458
|
+
range: [],
|
|
2459
|
+
number: [],
|
|
2460
|
+
search: ["combobox"],
|
|
2461
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2462
|
+
email: ["combobox"],
|
|
2463
|
+
tel: ["combobox"],
|
|
2464
|
+
url: ["combobox"],
|
|
2465
|
+
button: [
|
|
2466
|
+
"link",
|
|
2467
|
+
"menuitem",
|
|
2468
|
+
"menuitemcheckbox",
|
|
2469
|
+
"menuitemradio",
|
|
2470
|
+
"option",
|
|
2471
|
+
"radio",
|
|
2472
|
+
"switch",
|
|
2473
|
+
"tab"
|
|
2474
|
+
],
|
|
2475
|
+
submit: [
|
|
2476
|
+
"link",
|
|
2477
|
+
"menuitem",
|
|
2478
|
+
"menuitemcheckbox",
|
|
2479
|
+
"menuitemradio",
|
|
2480
|
+
"option",
|
|
2481
|
+
"radio",
|
|
2482
|
+
"switch",
|
|
2483
|
+
"tab"
|
|
2484
|
+
],
|
|
2485
|
+
reset: [
|
|
2486
|
+
"link",
|
|
2487
|
+
"menuitem",
|
|
2488
|
+
"menuitemcheckbox",
|
|
2489
|
+
"menuitemradio",
|
|
2490
|
+
"option",
|
|
2491
|
+
"radio",
|
|
2492
|
+
"switch",
|
|
2493
|
+
"tab"
|
|
2494
|
+
],
|
|
2495
|
+
image: [
|
|
2496
|
+
"link",
|
|
2497
|
+
"menuitem",
|
|
2498
|
+
"menuitemcheckbox",
|
|
2499
|
+
"menuitemradio",
|
|
2500
|
+
"option",
|
|
2501
|
+
"radio",
|
|
2502
|
+
"switch",
|
|
2503
|
+
"tab"
|
|
2504
|
+
],
|
|
2505
|
+
hidden: []
|
|
2506
|
+
};
|
|
2507
|
+
function getAllowedRoles(tag, props) {
|
|
2508
|
+
if (tag === "input") {
|
|
2509
|
+
const type = typeof props.type === "string" ? props.type : "text";
|
|
2510
|
+
return ALLOWED_INPUT_ROLES[type];
|
|
2511
|
+
}
|
|
2512
|
+
if (tag === "img") {
|
|
2513
|
+
return props.alt === "" ? [] : IMG_NAMED_ROLES;
|
|
2514
|
+
}
|
|
2515
|
+
return ALLOWED_ROLES[tag];
|
|
2516
|
+
}
|
|
2517
|
+
var removeRoleFix = {
|
|
2518
|
+
kind: "removeRole",
|
|
2519
|
+
apply: ({ props }) => {
|
|
2520
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2521
|
+
const { role: _role, ...rest } = props;
|
|
2522
|
+
return { applied: true, next: rest, previous: props };
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
var roleNotPermittedRule = Object.assign(
|
|
2526
|
+
({ tag, props, implicitRole }) => {
|
|
2527
|
+
const role = props.role;
|
|
2528
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2529
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2530
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2531
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2532
|
+
return [
|
|
2533
|
+
{
|
|
2534
|
+
valid: false,
|
|
2535
|
+
fixable: true,
|
|
2536
|
+
severity: diagnostic.severity,
|
|
2537
|
+
fix: removeRoleFix,
|
|
2538
|
+
diagnostic
|
|
2539
|
+
}
|
|
2540
|
+
];
|
|
2541
|
+
},
|
|
2542
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2543
|
+
);
|
|
2544
|
+
|
|
2086
2545
|
// ../core/src/html/aria-rules.ts
|
|
2087
2546
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
2088
2547
|
var removeLandmarkRoleOverride = {
|
|
@@ -2097,13 +2556,14 @@ function landmarkRoleRule({ tag, props, implicitRole }) {
|
|
|
2097
2556
|
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2098
2557
|
const role = props.role;
|
|
2099
2558
|
if (!role || role === implicitRole) return [];
|
|
2559
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2100
2560
|
return [
|
|
2101
2561
|
{
|
|
2102
2562
|
valid: false,
|
|
2103
2563
|
fixable: true,
|
|
2104
|
-
severity:
|
|
2564
|
+
severity: diagnostic.severity,
|
|
2105
2565
|
fix: removeLandmarkRoleOverride,
|
|
2106
|
-
diagnostic
|
|
2566
|
+
diagnostic
|
|
2107
2567
|
}
|
|
2108
2568
|
];
|
|
2109
2569
|
}
|
|
@@ -2123,7 +2583,12 @@ function landmarkNameAdvisory(ctx) {
|
|
|
2123
2583
|
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2124
2584
|
return requireAccessibleName(ctx);
|
|
2125
2585
|
}
|
|
2126
|
-
var HTML_ARIA_RULES = [
|
|
2586
|
+
var HTML_ARIA_RULES = [
|
|
2587
|
+
landmarkRoleRule,
|
|
2588
|
+
landmarkNameAdvisory,
|
|
2589
|
+
roleNotPermittedRule,
|
|
2590
|
+
...INPUT_RULES
|
|
2591
|
+
];
|
|
2127
2592
|
|
|
2128
2593
|
// ../core/src/html/contracts.ts
|
|
2129
2594
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2584,21 +3049,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2584
3049
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2585
3050
|
|
|
2586
3051
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2587
|
-
import { DiagnosticCategory as
|
|
3052
|
+
import { DiagnosticCategory as DiagnosticCategory6, DiagnosticCode as DiagnosticCode6 } from "../_shared/diagnostics.js";
|
|
2588
3053
|
var PluginDiagnostics = {
|
|
2589
3054
|
invalidShape(received) {
|
|
2590
3055
|
const got = received === null ? "null" : typeof received;
|
|
2591
3056
|
return {
|
|
2592
|
-
code:
|
|
2593
|
-
category:
|
|
3057
|
+
code: DiagnosticCode6.PluginInvalidShape,
|
|
3058
|
+
category: DiagnosticCategory6.Internal,
|
|
2594
3059
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2595
3060
|
};
|
|
2596
3061
|
},
|
|
2597
3062
|
pipelineReturnType(received) {
|
|
2598
3063
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2599
3064
|
return {
|
|
2600
|
-
code:
|
|
2601
|
-
category:
|
|
3065
|
+
code: DiagnosticCode6.PluginPipelineReturnType,
|
|
3066
|
+
category: DiagnosticCategory6.Internal,
|
|
2602
3067
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2603
3068
|
};
|
|
2604
3069
|
}
|