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/preact/index.js
CHANGED
|
@@ -995,11 +995,24 @@ var ContractDiagnostics = {
|
|
|
995
995
|
|
|
996
996
|
// ../../lib/contract/src/diagnostics/html.ts
|
|
997
997
|
import { DiagnosticCategory as DiagnosticCategory3, DiagnosticCode as DiagnosticCode3 } from "../_shared/diagnostics.js";
|
|
998
|
+
var ATTRIBUTE_IGNORED_CODES = {
|
|
999
|
+
checked: DiagnosticCode3.HtmlInputCheckedIgnoredForType,
|
|
1000
|
+
multiple: DiagnosticCode3.HtmlInputMultipleIgnoredForType,
|
|
1001
|
+
maxLength: DiagnosticCode3.HtmlInputMaxLengthIgnoredForType,
|
|
1002
|
+
minLength: DiagnosticCode3.HtmlInputMinLengthIgnoredForType,
|
|
1003
|
+
pattern: DiagnosticCode3.HtmlInputPatternIgnoredForType,
|
|
1004
|
+
min: DiagnosticCode3.HtmlInputMinIgnoredForType,
|
|
1005
|
+
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
1006
|
+
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
1007
|
+
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
1008
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType
|
|
1009
|
+
};
|
|
998
1010
|
var HtmlDiagnostics = {
|
|
999
1011
|
emptyRole(tag) {
|
|
1000
1012
|
return {
|
|
1001
1013
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
1002
1014
|
category: DiagnosticCategory3.HTML,
|
|
1015
|
+
severity: "warning",
|
|
1003
1016
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
1004
1017
|
};
|
|
1005
1018
|
},
|
|
@@ -1007,6 +1020,7 @@ var HtmlDiagnostics = {
|
|
|
1007
1020
|
return {
|
|
1008
1021
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
1009
1022
|
category: DiagnosticCategory3.HTML,
|
|
1023
|
+
severity: "warning",
|
|
1010
1024
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
1011
1025
|
};
|
|
1012
1026
|
},
|
|
@@ -1014,6 +1028,7 @@ var HtmlDiagnostics = {
|
|
|
1014
1028
|
return {
|
|
1015
1029
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
1016
1030
|
category: DiagnosticCategory3.HTML,
|
|
1031
|
+
severity: "error",
|
|
1017
1032
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
1018
1033
|
};
|
|
1019
1034
|
},
|
|
@@ -1021,6 +1036,7 @@ var HtmlDiagnostics = {
|
|
|
1021
1036
|
return {
|
|
1022
1037
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
1023
1038
|
category: DiagnosticCategory3.HTML,
|
|
1039
|
+
severity: "error",
|
|
1024
1040
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
1025
1041
|
};
|
|
1026
1042
|
},
|
|
@@ -1028,6 +1044,7 @@ var HtmlDiagnostics = {
|
|
|
1028
1044
|
return {
|
|
1029
1045
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
1030
1046
|
category: DiagnosticCategory3.HTML,
|
|
1047
|
+
severity: "error",
|
|
1031
1048
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
1032
1049
|
};
|
|
1033
1050
|
},
|
|
@@ -1035,34 +1052,148 @@ var HtmlDiagnostics = {
|
|
|
1035
1052
|
return {
|
|
1036
1053
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
1037
1054
|
category: DiagnosticCategory3.HTML,
|
|
1055
|
+
severity: "error",
|
|
1038
1056
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
1039
1057
|
};
|
|
1058
|
+
},
|
|
1059
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
1060
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
1061
|
+
return {
|
|
1062
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
1063
|
+
category: DiagnosticCategory3.HTML,
|
|
1064
|
+
severity: "error",
|
|
1065
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
1066
|
+
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.'
|
|
1067
|
+
};
|
|
1068
|
+
},
|
|
1069
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
1070
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
1071
|
+
input: {
|
|
1072
|
+
unsupportedType(type) {
|
|
1073
|
+
return {
|
|
1074
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
1075
|
+
category: DiagnosticCategory3.HTML,
|
|
1076
|
+
severity: "warning",
|
|
1077
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
1078
|
+
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).',
|
|
1079
|
+
suggestions: [
|
|
1080
|
+
{
|
|
1081
|
+
title: "Check for a typo in the type value",
|
|
1082
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
1083
|
+
}
|
|
1084
|
+
]
|
|
1085
|
+
};
|
|
1086
|
+
},
|
|
1087
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
1088
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
1089
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
1090
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
1091
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
1092
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
1093
|
+
return {
|
|
1094
|
+
code,
|
|
1095
|
+
category: DiagnosticCategory3.HTML,
|
|
1096
|
+
severity: "warning",
|
|
1097
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
1098
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
1099
|
+
suggestions: [
|
|
1100
|
+
{
|
|
1101
|
+
title: `Remove "${attribute}"`,
|
|
1102
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
1103
|
+
}
|
|
1104
|
+
]
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1040
1107
|
}
|
|
1041
1108
|
};
|
|
1042
1109
|
|
|
1043
|
-
// ../../lib/contract/src/diagnostics/
|
|
1110
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
1044
1111
|
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "../_shared/diagnostics.js";
|
|
1112
|
+
function accessibilityFact(input) {
|
|
1113
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
1114
|
+
}
|
|
1115
|
+
var InputAccessibilityDiagnostics = {
|
|
1116
|
+
missingAccessibleName() {
|
|
1117
|
+
return accessibilityFact({
|
|
1118
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
1119
|
+
severity: "warning",
|
|
1120
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1121
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
1122
|
+
suggestions: [
|
|
1123
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1124
|
+
{
|
|
1125
|
+
title: "Add an associated <label>",
|
|
1126
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1127
|
+
}
|
|
1128
|
+
]
|
|
1129
|
+
});
|
|
1130
|
+
},
|
|
1131
|
+
placeholderIsNotLabel() {
|
|
1132
|
+
return accessibilityFact({
|
|
1133
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
1134
|
+
severity: "warning",
|
|
1135
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
1136
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1137
|
+
suggestions: [
|
|
1138
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1139
|
+
{
|
|
1140
|
+
title: "Add an associated <label>",
|
|
1141
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1142
|
+
}
|
|
1143
|
+
]
|
|
1144
|
+
});
|
|
1145
|
+
},
|
|
1146
|
+
passwordMissingAutocomplete() {
|
|
1147
|
+
return accessibilityFact({
|
|
1148
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1149
|
+
severity: "warning",
|
|
1150
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1151
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1152
|
+
suggestions: [
|
|
1153
|
+
{
|
|
1154
|
+
title: 'Set autoComplete="current-password"',
|
|
1155
|
+
description: "Use this for sign-in forms."
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
title: 'Set autoComplete="new-password"',
|
|
1159
|
+
description: "Use this for sign-up / change-password forms."
|
|
1160
|
+
}
|
|
1161
|
+
]
|
|
1162
|
+
});
|
|
1163
|
+
},
|
|
1164
|
+
requiredReadOnlyConflict() {
|
|
1165
|
+
return accessibilityFact({
|
|
1166
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1167
|
+
severity: "warning",
|
|
1168
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1169
|
+
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."
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1175
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
1045
1176
|
var SlotDiagnostics = {
|
|
1046
1177
|
exclusive(name) {
|
|
1047
1178
|
return {
|
|
1048
|
-
code:
|
|
1049
|
-
category:
|
|
1179
|
+
code: DiagnosticCode5.SlotExclusive,
|
|
1180
|
+
category: DiagnosticCategory5.Contract,
|
|
1050
1181
|
component: name,
|
|
1051
1182
|
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
1052
1183
|
};
|
|
1053
1184
|
},
|
|
1054
1185
|
singleChildRequired(name, elementTerm) {
|
|
1055
1186
|
return {
|
|
1056
|
-
code:
|
|
1057
|
-
category:
|
|
1187
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1188
|
+
category: DiagnosticCategory5.Contract,
|
|
1058
1189
|
component: name,
|
|
1059
1190
|
message: `${name}: asChild requires a ${elementTerm} child`
|
|
1060
1191
|
};
|
|
1061
1192
|
},
|
|
1062
1193
|
singleChildExceeded(name, elementTerm, count) {
|
|
1063
1194
|
return {
|
|
1064
|
-
code:
|
|
1065
|
-
category:
|
|
1195
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1196
|
+
category: DiagnosticCategory5.Contract,
|
|
1066
1197
|
component: name,
|
|
1067
1198
|
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
1068
1199
|
};
|
|
@@ -1070,16 +1201,16 @@ var SlotDiagnostics = {
|
|
|
1070
1201
|
discardedChildren(name, elementTerm, count) {
|
|
1071
1202
|
const suffix = count === 1 ? "" : "ren";
|
|
1072
1203
|
return {
|
|
1073
|
-
code:
|
|
1074
|
-
category:
|
|
1204
|
+
code: DiagnosticCode5.SlotDiscardedChildren,
|
|
1205
|
+
category: DiagnosticCategory5.Contract,
|
|
1075
1206
|
component: name,
|
|
1076
1207
|
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
1077
1208
|
};
|
|
1078
1209
|
},
|
|
1079
1210
|
renderFnRequired(name, received) {
|
|
1080
1211
|
return {
|
|
1081
|
-
code:
|
|
1082
|
-
category:
|
|
1212
|
+
code: DiagnosticCode5.SlotRenderFn,
|
|
1213
|
+
category: DiagnosticCategory5.Contract,
|
|
1083
1214
|
component: name,
|
|
1084
1215
|
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
1085
1216
|
};
|
|
@@ -1141,7 +1272,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1141
1272
|
tag,
|
|
1142
1273
|
role: "",
|
|
1143
1274
|
attribute: void 0,
|
|
1144
|
-
severity:
|
|
1275
|
+
severity: d.severity,
|
|
1145
1276
|
phase: "evaluate"
|
|
1146
1277
|
}
|
|
1147
1278
|
]
|
|
@@ -1400,13 +1531,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1400
1531
|
const role = props.role;
|
|
1401
1532
|
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
|
|
1402
1533
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1534
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1403
1535
|
return [
|
|
1404
1536
|
{
|
|
1405
1537
|
valid: false,
|
|
1406
1538
|
fixable: true,
|
|
1407
|
-
severity:
|
|
1539
|
+
severity: diagnostic.severity,
|
|
1408
1540
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1409
|
-
diagnostic
|
|
1541
|
+
diagnostic
|
|
1410
1542
|
}
|
|
1411
1543
|
];
|
|
1412
1544
|
}
|
|
@@ -1415,13 +1547,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1415
1547
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1416
1548
|
const role = props.role;
|
|
1417
1549
|
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
|
|
1550
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1418
1551
|
return [
|
|
1419
1552
|
{
|
|
1420
1553
|
valid: false,
|
|
1421
1554
|
fixable: true,
|
|
1422
|
-
severity:
|
|
1555
|
+
severity: diagnostic.severity,
|
|
1423
1556
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1424
|
-
diagnostic
|
|
1557
|
+
diagnostic
|
|
1425
1558
|
}
|
|
1426
1559
|
];
|
|
1427
1560
|
}
|
|
@@ -1429,13 +1562,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1429
1562
|
const role = props.role;
|
|
1430
1563
|
if (role !== "region") return NO_VIOLATIONS;
|
|
1431
1564
|
if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
|
|
1565
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1432
1566
|
return [
|
|
1433
1567
|
{
|
|
1434
1568
|
valid: false,
|
|
1435
1569
|
fixable: true,
|
|
1436
|
-
severity:
|
|
1570
|
+
severity: diagnostic.severity,
|
|
1437
1571
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1438
|
-
diagnostic
|
|
1572
|
+
diagnostic
|
|
1439
1573
|
}
|
|
1440
1574
|
];
|
|
1441
1575
|
}
|
|
@@ -2124,6 +2258,331 @@ var readonlyProps = ({
|
|
|
2124
2258
|
// ../core/src/html/evaluators.ts
|
|
2125
2259
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
2126
2260
|
|
|
2261
|
+
// ../core/src/html/input-rules.ts
|
|
2262
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2263
|
+
function omit(props, key) {
|
|
2264
|
+
const next = { ...props };
|
|
2265
|
+
delete next[key];
|
|
2266
|
+
return next;
|
|
2267
|
+
}
|
|
2268
|
+
function removeAttributeFix(attribute) {
|
|
2269
|
+
return {
|
|
2270
|
+
kind: `removeAttribute:${attribute}`,
|
|
2271
|
+
apply: ({ props }) => {
|
|
2272
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2273
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
function inputAttributeRequiresType(attribute, allowedTypes) {
|
|
2278
|
+
const rule = ({ tag, props }) => {
|
|
2279
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2280
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2281
|
+
if (allowedTypes.includes(type)) return [];
|
|
2282
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2283
|
+
return [
|
|
2284
|
+
{
|
|
2285
|
+
valid: false,
|
|
2286
|
+
fixable: true,
|
|
2287
|
+
severity: diagnostic.severity,
|
|
2288
|
+
fix: removeAttributeFix(attribute),
|
|
2289
|
+
diagnostic
|
|
2290
|
+
}
|
|
2291
|
+
];
|
|
2292
|
+
};
|
|
2293
|
+
return Object.assign(rule, { readsProps: ["type", attribute] });
|
|
2294
|
+
}
|
|
2295
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2296
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2297
|
+
"number",
|
|
2298
|
+
"range",
|
|
2299
|
+
"date",
|
|
2300
|
+
"month",
|
|
2301
|
+
"week",
|
|
2302
|
+
"time",
|
|
2303
|
+
"datetime-local"
|
|
2304
|
+
];
|
|
2305
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2306
|
+
...TEXT_INPUT_TYPES,
|
|
2307
|
+
...NUMERIC_INPUT_TYPES,
|
|
2308
|
+
"checkbox",
|
|
2309
|
+
"radio",
|
|
2310
|
+
"file",
|
|
2311
|
+
"color",
|
|
2312
|
+
"hidden",
|
|
2313
|
+
"button",
|
|
2314
|
+
"submit",
|
|
2315
|
+
"reset",
|
|
2316
|
+
"image"
|
|
2317
|
+
]);
|
|
2318
|
+
var supportedInputTypeRule = Object.assign(
|
|
2319
|
+
({ tag, props }) => {
|
|
2320
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2321
|
+
const type = props.type;
|
|
2322
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2323
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2324
|
+
return [
|
|
2325
|
+
{
|
|
2326
|
+
valid: false,
|
|
2327
|
+
fixable: false,
|
|
2328
|
+
severity: diagnostic.severity,
|
|
2329
|
+
diagnostic
|
|
2330
|
+
}
|
|
2331
|
+
];
|
|
2332
|
+
},
|
|
2333
|
+
{ readsProps: ["type"] }
|
|
2334
|
+
);
|
|
2335
|
+
var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
|
|
2336
|
+
"checkbox",
|
|
2337
|
+
"radio"
|
|
2338
|
+
]);
|
|
2339
|
+
var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
|
|
2340
|
+
"email",
|
|
2341
|
+
"file"
|
|
2342
|
+
]);
|
|
2343
|
+
var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2344
|
+
"maxLength",
|
|
2345
|
+
TEXT_INPUT_TYPES
|
|
2346
|
+
);
|
|
2347
|
+
var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2348
|
+
"minLength",
|
|
2349
|
+
TEXT_INPUT_TYPES
|
|
2350
|
+
);
|
|
2351
|
+
var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
|
|
2352
|
+
var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
|
|
2353
|
+
var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
|
|
2354
|
+
var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
|
|
2355
|
+
var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
|
|
2356
|
+
var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
|
|
2357
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2358
|
+
({ tag, props }) => {
|
|
2359
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2360
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2361
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2362
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2363
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2364
|
+
},
|
|
2365
|
+
{ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
|
|
2366
|
+
);
|
|
2367
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2368
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2369
|
+
({ tag, props }) => {
|
|
2370
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2371
|
+
const autoComplete = props.autoComplete;
|
|
2372
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2373
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2374
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2375
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2376
|
+
},
|
|
2377
|
+
{ readsProps: ["type", "autoComplete"] }
|
|
2378
|
+
);
|
|
2379
|
+
var requiredReadOnlyConflictRule = Object.assign(
|
|
2380
|
+
({ tag, props }) => {
|
|
2381
|
+
if (tag !== "input" || !props.required || !props.readOnly) return [];
|
|
2382
|
+
const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
|
|
2383
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2384
|
+
},
|
|
2385
|
+
{ readsProps: ["required", "readOnly"] }
|
|
2386
|
+
);
|
|
2387
|
+
var INPUT_RULES = [
|
|
2388
|
+
supportedInputTypeRule,
|
|
2389
|
+
checkedRequiresCheckableTypeRule,
|
|
2390
|
+
multipleRequiresSupportedTypeRule,
|
|
2391
|
+
maxLengthRequiresTextTypeRule,
|
|
2392
|
+
minLengthRequiresTextTypeRule,
|
|
2393
|
+
patternRequiresTextTypeRule,
|
|
2394
|
+
minRequiresNumericTypeRule,
|
|
2395
|
+
maxRequiresNumericTypeRule,
|
|
2396
|
+
stepRequiresNumericTypeRule,
|
|
2397
|
+
acceptRequiresFileTypeRule,
|
|
2398
|
+
captureRequiresFileTypeRule,
|
|
2399
|
+
inputAccessibleNameRule,
|
|
2400
|
+
passwordAutocompleteRule,
|
|
2401
|
+
requiredReadOnlyConflictRule
|
|
2402
|
+
];
|
|
2403
|
+
|
|
2404
|
+
// ../core/src/html/role-restrictions.ts
|
|
2405
|
+
var ALLOWED_ROLES = {
|
|
2406
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2407
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2408
|
+
footer: ["group", "none", "presentation"],
|
|
2409
|
+
header: ["group", "none", "presentation"],
|
|
2410
|
+
main: [],
|
|
2411
|
+
nav: [],
|
|
2412
|
+
a: [
|
|
2413
|
+
"button",
|
|
2414
|
+
"checkbox",
|
|
2415
|
+
"menuitem",
|
|
2416
|
+
"menuitemcheckbox",
|
|
2417
|
+
"menuitemradio",
|
|
2418
|
+
"option",
|
|
2419
|
+
"radio",
|
|
2420
|
+
"switch",
|
|
2421
|
+
"tab",
|
|
2422
|
+
"treeitem"
|
|
2423
|
+
],
|
|
2424
|
+
button: [
|
|
2425
|
+
"checkbox",
|
|
2426
|
+
"link",
|
|
2427
|
+
"menuitem",
|
|
2428
|
+
"menuitemcheckbox",
|
|
2429
|
+
"menuitemradio",
|
|
2430
|
+
"option",
|
|
2431
|
+
"radio",
|
|
2432
|
+
"switch",
|
|
2433
|
+
"tab"
|
|
2434
|
+
],
|
|
2435
|
+
select: ["menu"],
|
|
2436
|
+
h1: ["tab", "presentation", "none"],
|
|
2437
|
+
h2: ["tab", "presentation", "none"],
|
|
2438
|
+
h3: ["tab", "presentation", "none"],
|
|
2439
|
+
h4: ["tab", "presentation", "none"],
|
|
2440
|
+
h5: ["tab", "presentation", "none"],
|
|
2441
|
+
h6: ["tab", "presentation", "none"],
|
|
2442
|
+
ul: [
|
|
2443
|
+
"directory",
|
|
2444
|
+
"group",
|
|
2445
|
+
"listbox",
|
|
2446
|
+
"menu",
|
|
2447
|
+
"menubar",
|
|
2448
|
+
"radiogroup",
|
|
2449
|
+
"tablist",
|
|
2450
|
+
"toolbar",
|
|
2451
|
+
"tree"
|
|
2452
|
+
],
|
|
2453
|
+
ol: [
|
|
2454
|
+
"directory",
|
|
2455
|
+
"group",
|
|
2456
|
+
"listbox",
|
|
2457
|
+
"menu",
|
|
2458
|
+
"menubar",
|
|
2459
|
+
"radiogroup",
|
|
2460
|
+
"tablist",
|
|
2461
|
+
"toolbar",
|
|
2462
|
+
"tree"
|
|
2463
|
+
],
|
|
2464
|
+
li: [
|
|
2465
|
+
"menuitem",
|
|
2466
|
+
"menuitemcheckbox",
|
|
2467
|
+
"menuitemradio",
|
|
2468
|
+
"option",
|
|
2469
|
+
"none",
|
|
2470
|
+
"presentation",
|
|
2471
|
+
"radio",
|
|
2472
|
+
"separator",
|
|
2473
|
+
"tab",
|
|
2474
|
+
"treeitem"
|
|
2475
|
+
],
|
|
2476
|
+
table: ["grid", "treegrid"],
|
|
2477
|
+
dialog: ["alertdialog"],
|
|
2478
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2479
|
+
};
|
|
2480
|
+
var IMG_NAMED_ROLES = [
|
|
2481
|
+
"button",
|
|
2482
|
+
"checkbox",
|
|
2483
|
+
"link",
|
|
2484
|
+
"menuitem",
|
|
2485
|
+
"menuitemcheckbox",
|
|
2486
|
+
"menuitemradio",
|
|
2487
|
+
"option",
|
|
2488
|
+
"progressbar",
|
|
2489
|
+
"scrollbar",
|
|
2490
|
+
"separator",
|
|
2491
|
+
"slider",
|
|
2492
|
+
"switch",
|
|
2493
|
+
"tab",
|
|
2494
|
+
"treeitem"
|
|
2495
|
+
];
|
|
2496
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2497
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2498
|
+
radio: ["menuitemradio"],
|
|
2499
|
+
range: [],
|
|
2500
|
+
number: [],
|
|
2501
|
+
search: ["combobox"],
|
|
2502
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2503
|
+
email: ["combobox"],
|
|
2504
|
+
tel: ["combobox"],
|
|
2505
|
+
url: ["combobox"],
|
|
2506
|
+
button: [
|
|
2507
|
+
"link",
|
|
2508
|
+
"menuitem",
|
|
2509
|
+
"menuitemcheckbox",
|
|
2510
|
+
"menuitemradio",
|
|
2511
|
+
"option",
|
|
2512
|
+
"radio",
|
|
2513
|
+
"switch",
|
|
2514
|
+
"tab"
|
|
2515
|
+
],
|
|
2516
|
+
submit: [
|
|
2517
|
+
"link",
|
|
2518
|
+
"menuitem",
|
|
2519
|
+
"menuitemcheckbox",
|
|
2520
|
+
"menuitemradio",
|
|
2521
|
+
"option",
|
|
2522
|
+
"radio",
|
|
2523
|
+
"switch",
|
|
2524
|
+
"tab"
|
|
2525
|
+
],
|
|
2526
|
+
reset: [
|
|
2527
|
+
"link",
|
|
2528
|
+
"menuitem",
|
|
2529
|
+
"menuitemcheckbox",
|
|
2530
|
+
"menuitemradio",
|
|
2531
|
+
"option",
|
|
2532
|
+
"radio",
|
|
2533
|
+
"switch",
|
|
2534
|
+
"tab"
|
|
2535
|
+
],
|
|
2536
|
+
image: [
|
|
2537
|
+
"link",
|
|
2538
|
+
"menuitem",
|
|
2539
|
+
"menuitemcheckbox",
|
|
2540
|
+
"menuitemradio",
|
|
2541
|
+
"option",
|
|
2542
|
+
"radio",
|
|
2543
|
+
"switch",
|
|
2544
|
+
"tab"
|
|
2545
|
+
],
|
|
2546
|
+
hidden: []
|
|
2547
|
+
};
|
|
2548
|
+
function getAllowedRoles(tag, props) {
|
|
2549
|
+
if (tag === "input") {
|
|
2550
|
+
const type = typeof props.type === "string" ? props.type : "text";
|
|
2551
|
+
return ALLOWED_INPUT_ROLES[type];
|
|
2552
|
+
}
|
|
2553
|
+
if (tag === "img") {
|
|
2554
|
+
return props.alt === "" ? [] : IMG_NAMED_ROLES;
|
|
2555
|
+
}
|
|
2556
|
+
return ALLOWED_ROLES[tag];
|
|
2557
|
+
}
|
|
2558
|
+
var removeRoleFix = {
|
|
2559
|
+
kind: "removeRole",
|
|
2560
|
+
apply: ({ props }) => {
|
|
2561
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2562
|
+
const { role: _role, ...rest } = props;
|
|
2563
|
+
return { applied: true, next: rest, previous: props };
|
|
2564
|
+
}
|
|
2565
|
+
};
|
|
2566
|
+
var roleNotPermittedRule = Object.assign(
|
|
2567
|
+
({ tag, props, implicitRole }) => {
|
|
2568
|
+
const role = props.role;
|
|
2569
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2570
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2571
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2572
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2573
|
+
return [
|
|
2574
|
+
{
|
|
2575
|
+
valid: false,
|
|
2576
|
+
fixable: true,
|
|
2577
|
+
severity: diagnostic.severity,
|
|
2578
|
+
fix: removeRoleFix,
|
|
2579
|
+
diagnostic
|
|
2580
|
+
}
|
|
2581
|
+
];
|
|
2582
|
+
},
|
|
2583
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2584
|
+
);
|
|
2585
|
+
|
|
2127
2586
|
// ../core/src/html/aria-rules.ts
|
|
2128
2587
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
2129
2588
|
var removeLandmarkRoleOverride = {
|
|
@@ -2138,13 +2597,14 @@ function landmarkRoleRule({ tag, props, implicitRole }) {
|
|
|
2138
2597
|
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2139
2598
|
const role = props.role;
|
|
2140
2599
|
if (!role || role === implicitRole) return [];
|
|
2600
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2141
2601
|
return [
|
|
2142
2602
|
{
|
|
2143
2603
|
valid: false,
|
|
2144
2604
|
fixable: true,
|
|
2145
|
-
severity:
|
|
2605
|
+
severity: diagnostic.severity,
|
|
2146
2606
|
fix: removeLandmarkRoleOverride,
|
|
2147
|
-
diagnostic
|
|
2607
|
+
diagnostic
|
|
2148
2608
|
}
|
|
2149
2609
|
];
|
|
2150
2610
|
}
|
|
@@ -2164,7 +2624,12 @@ function landmarkNameAdvisory(ctx) {
|
|
|
2164
2624
|
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2165
2625
|
return requireAccessibleName(ctx);
|
|
2166
2626
|
}
|
|
2167
|
-
var HTML_ARIA_RULES = [
|
|
2627
|
+
var HTML_ARIA_RULES = [
|
|
2628
|
+
landmarkRoleRule,
|
|
2629
|
+
landmarkNameAdvisory,
|
|
2630
|
+
roleNotPermittedRule,
|
|
2631
|
+
...INPUT_RULES
|
|
2632
|
+
];
|
|
2168
2633
|
|
|
2169
2634
|
// ../core/src/html/contracts.ts
|
|
2170
2635
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2625,21 +3090,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2625
3090
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2626
3091
|
|
|
2627
3092
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2628
|
-
import { DiagnosticCategory as
|
|
3093
|
+
import { DiagnosticCategory as DiagnosticCategory6, DiagnosticCode as DiagnosticCode6 } from "../_shared/diagnostics.js";
|
|
2629
3094
|
var PluginDiagnostics = {
|
|
2630
3095
|
invalidShape(received) {
|
|
2631
3096
|
const got = received === null ? "null" : typeof received;
|
|
2632
3097
|
return {
|
|
2633
|
-
code:
|
|
2634
|
-
category:
|
|
3098
|
+
code: DiagnosticCode6.PluginInvalidShape,
|
|
3099
|
+
category: DiagnosticCategory6.Internal,
|
|
2635
3100
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2636
3101
|
};
|
|
2637
3102
|
},
|
|
2638
3103
|
pipelineReturnType(received) {
|
|
2639
3104
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2640
3105
|
return {
|
|
2641
|
-
code:
|
|
2642
|
-
category:
|
|
3106
|
+
code: DiagnosticCode6.PluginPipelineReturnType,
|
|
3107
|
+
category: DiagnosticCategory6.Internal,
|
|
2643
3108
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2644
3109
|
};
|
|
2645
3110
|
}
|