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/lit/index.js
CHANGED
|
@@ -858,11 +858,24 @@ var ContractDiagnostics = {
|
|
|
858
858
|
|
|
859
859
|
// ../../lib/contract/src/diagnostics/html.ts
|
|
860
860
|
import { DiagnosticCategory as DiagnosticCategory3, DiagnosticCode as DiagnosticCode3 } from "../_shared/diagnostics.js";
|
|
861
|
+
var ATTRIBUTE_IGNORED_CODES = {
|
|
862
|
+
checked: DiagnosticCode3.HtmlInputCheckedIgnoredForType,
|
|
863
|
+
multiple: DiagnosticCode3.HtmlInputMultipleIgnoredForType,
|
|
864
|
+
maxLength: DiagnosticCode3.HtmlInputMaxLengthIgnoredForType,
|
|
865
|
+
minLength: DiagnosticCode3.HtmlInputMinLengthIgnoredForType,
|
|
866
|
+
pattern: DiagnosticCode3.HtmlInputPatternIgnoredForType,
|
|
867
|
+
min: DiagnosticCode3.HtmlInputMinIgnoredForType,
|
|
868
|
+
max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
|
|
869
|
+
step: DiagnosticCode3.HtmlInputStepIgnoredForType,
|
|
870
|
+
accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
|
|
871
|
+
capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType
|
|
872
|
+
};
|
|
861
873
|
var HtmlDiagnostics = {
|
|
862
874
|
emptyRole(tag) {
|
|
863
875
|
return {
|
|
864
876
|
code: DiagnosticCode3.HtmlEmptyRole,
|
|
865
877
|
category: DiagnosticCategory3.HTML,
|
|
878
|
+
severity: "warning",
|
|
866
879
|
message: `<${tag}> has an explicit empty role="". Omit the attribute instead.`
|
|
867
880
|
};
|
|
868
881
|
},
|
|
@@ -870,6 +883,7 @@ var HtmlDiagnostics = {
|
|
|
870
883
|
return {
|
|
871
884
|
code: DiagnosticCode3.HtmlImplicitRoleRedundant,
|
|
872
885
|
category: DiagnosticCategory3.HTML,
|
|
886
|
+
severity: "warning",
|
|
873
887
|
message: `<${tag}> already has implicit role="${implicitRole}". Avoid redundant role assignment.`
|
|
874
888
|
};
|
|
875
889
|
},
|
|
@@ -877,6 +891,7 @@ var HtmlDiagnostics = {
|
|
|
877
891
|
return {
|
|
878
892
|
code: DiagnosticCode3.HtmlImplicitRoleOverride,
|
|
879
893
|
category: DiagnosticCategory3.HTML,
|
|
894
|
+
severity: "error",
|
|
880
895
|
message: `<${tag}> should not override its implicit role="${implicitRole}" with role="${role}".`
|
|
881
896
|
};
|
|
882
897
|
},
|
|
@@ -884,6 +899,7 @@ var HtmlDiagnostics = {
|
|
|
884
899
|
return {
|
|
885
900
|
code: DiagnosticCode3.HtmlStandaloneRegionOverride,
|
|
886
901
|
category: DiagnosticCategory3.HTML,
|
|
902
|
+
severity: "error",
|
|
887
903
|
message: `<${tag}> is a self-contained element with implicit role="${implicitRole}". Assigning role="region" has been removed.`
|
|
888
904
|
};
|
|
889
905
|
},
|
|
@@ -891,6 +907,7 @@ var HtmlDiagnostics = {
|
|
|
891
907
|
return {
|
|
892
908
|
code: DiagnosticCode3.HtmlLandmarkRoleOverride,
|
|
893
909
|
category: DiagnosticCategory3.HTML,
|
|
910
|
+
severity: "error",
|
|
894
911
|
message: `<${tag}> has a fixed landmark role="${implicitRole}". role="${role}" overrides it and confuses assistive technology. The override has been removed.`
|
|
895
912
|
};
|
|
896
913
|
},
|
|
@@ -898,8 +915,122 @@ var HtmlDiagnostics = {
|
|
|
898
915
|
return {
|
|
899
916
|
code: DiagnosticCode3.HtmlInvalidChild,
|
|
900
917
|
category: DiagnosticCategory3.HTML,
|
|
918
|
+
severity: "error",
|
|
901
919
|
message: `<${child}> is not a valid direct child of <${parent}>. Allowed: ${allowed}.`
|
|
902
920
|
};
|
|
921
|
+
},
|
|
922
|
+
roleNotPermitted(tag, role, allowedRoles) {
|
|
923
|
+
const allowed = allowedRoles.length > 0 ? allowedRoles.map((r) => `"${r}"`).join(", ") : "none \u2014 no explicit role is permitted on this element";
|
|
924
|
+
return {
|
|
925
|
+
code: DiagnosticCode3.HtmlRoleNotPermitted,
|
|
926
|
+
category: DiagnosticCategory3.HTML,
|
|
927
|
+
severity: "error",
|
|
928
|
+
message: `role="${role}" is not permitted on <${tag}>. Allowed alternate role(s): ${allowed}.`,
|
|
929
|
+
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.'
|
|
930
|
+
};
|
|
931
|
+
},
|
|
932
|
+
// Reserved for <input>-specific facts (HTML3101–3199, see codes.ts) — later element families
|
|
933
|
+
// (button, img, table, ...) get their own reserved block and their own namespace here.
|
|
934
|
+
input: {
|
|
935
|
+
unsupportedType(type) {
|
|
936
|
+
return {
|
|
937
|
+
code: DiagnosticCode3.HtmlInputUnsupportedType,
|
|
938
|
+
category: DiagnosticCategory3.HTML,
|
|
939
|
+
severity: "warning",
|
|
940
|
+
message: `type="${type}" is not a value defined by the HTML specification. Browsers silently fall back to type="text".`,
|
|
941
|
+
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).',
|
|
942
|
+
suggestions: [
|
|
943
|
+
{
|
|
944
|
+
title: "Check for a typo in the type value",
|
|
945
|
+
description: `"${type}" does not match any HTML5 input type.`
|
|
946
|
+
}
|
|
947
|
+
]
|
|
948
|
+
};
|
|
949
|
+
},
|
|
950
|
+
// The user-visible problem is that the attribute is ignored — not that it "requires" a type;
|
|
951
|
+
// that's the rule's internal framing, not what the browser actually does.
|
|
952
|
+
attributeIgnoredForType(attribute, type, allowedTypes) {
|
|
953
|
+
const allowed = allowedTypes.map((t) => `"${t}"`).join(", ");
|
|
954
|
+
const code = ATTRIBUTE_IGNORED_CODES[attribute];
|
|
955
|
+
if (!code) throw new Error(`No DiagnosticCode registered for input attribute "${attribute}"`);
|
|
956
|
+
return {
|
|
957
|
+
code,
|
|
958
|
+
category: DiagnosticCategory3.HTML,
|
|
959
|
+
severity: "warning",
|
|
960
|
+
message: `"${attribute}" is ignored on <input type="${type}">.`,
|
|
961
|
+
rationale: `"${attribute}" only has an effect when type is one of: ${allowed}. Browsers silently ignore it on other input types.`,
|
|
962
|
+
suggestions: [
|
|
963
|
+
{
|
|
964
|
+
title: `Remove "${attribute}"`,
|
|
965
|
+
description: `"${attribute}" only affects <input> when type is one of: ${allowed}.`
|
|
966
|
+
}
|
|
967
|
+
]
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
|
|
973
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
974
|
+
import { DiagnosticCategory as DiagnosticCategory4, DiagnosticCode as DiagnosticCode4 } from "../_shared/diagnostics.js";
|
|
975
|
+
function accessibilityFact(input) {
|
|
976
|
+
return { category: DiagnosticCategory4.Accessibility, ...input };
|
|
977
|
+
}
|
|
978
|
+
var InputAccessibilityDiagnostics = {
|
|
979
|
+
missingAccessibleName() {
|
|
980
|
+
return accessibilityFact({
|
|
981
|
+
code: DiagnosticCode4.A11yInputMissingAccessibleName,
|
|
982
|
+
severity: "warning",
|
|
983
|
+
message: "This input has no accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
984
|
+
rationale: "Assistive technology announces a form field by its accessible name; without one, users of screen readers cannot tell what the field is for.",
|
|
985
|
+
suggestions: [
|
|
986
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
987
|
+
{
|
|
988
|
+
title: "Add an associated <label>",
|
|
989
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
990
|
+
}
|
|
991
|
+
]
|
|
992
|
+
});
|
|
993
|
+
},
|
|
994
|
+
placeholderIsNotLabel() {
|
|
995
|
+
return accessibilityFact({
|
|
996
|
+
code: DiagnosticCode4.A11yInputPlaceholderNotLabel,
|
|
997
|
+
severity: "warning",
|
|
998
|
+
message: "Placeholder text does not provide an accessible name. Add an associated <label>, aria-label, or aria-labelledby.",
|
|
999
|
+
rationale: "Placeholder text disappears as users interact with the field and is not treated as the control's accessible name by many assistive technologies.",
|
|
1000
|
+
suggestions: [
|
|
1001
|
+
{ title: "Add aria-label", description: 'Set aria-label="\u2026" directly on the input.' },
|
|
1002
|
+
{
|
|
1003
|
+
title: "Add an associated <label>",
|
|
1004
|
+
description: 'Wrap the input in a <label>, or point a <label for="\u2026"> at its id.'
|
|
1005
|
+
}
|
|
1006
|
+
]
|
|
1007
|
+
});
|
|
1008
|
+
},
|
|
1009
|
+
passwordMissingAutocomplete() {
|
|
1010
|
+
return accessibilityFact({
|
|
1011
|
+
code: DiagnosticCode4.A11yInputPasswordAutocomplete,
|
|
1012
|
+
severity: "warning",
|
|
1013
|
+
message: "Password inputs should specify an autoComplete value.",
|
|
1014
|
+
rationale: "Without an explicit autocomplete hint, password managers and browsers cannot reliably tell a sign-in field apart from a password-creation field.",
|
|
1015
|
+
suggestions: [
|
|
1016
|
+
{
|
|
1017
|
+
title: 'Set autoComplete="current-password"',
|
|
1018
|
+
description: "Use this for sign-in forms."
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
title: 'Set autoComplete="new-password"',
|
|
1022
|
+
description: "Use this for sign-up / change-password forms."
|
|
1023
|
+
}
|
|
1024
|
+
]
|
|
1025
|
+
});
|
|
1026
|
+
},
|
|
1027
|
+
requiredReadOnlyConflict() {
|
|
1028
|
+
return accessibilityFact({
|
|
1029
|
+
code: DiagnosticCode4.A11yInputRequiredReadOnlyConflict,
|
|
1030
|
+
severity: "warning",
|
|
1031
|
+
message: "The required and readOnly attributes are both present. A read-only field cannot satisfy required validation through user interaction.",
|
|
1032
|
+
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."
|
|
1033
|
+
});
|
|
903
1034
|
}
|
|
904
1035
|
};
|
|
905
1036
|
|
|
@@ -958,7 +1089,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
958
1089
|
tag,
|
|
959
1090
|
role: "",
|
|
960
1091
|
attribute: void 0,
|
|
961
|
-
severity:
|
|
1092
|
+
severity: d.severity,
|
|
962
1093
|
phase: "evaluate"
|
|
963
1094
|
}
|
|
964
1095
|
]
|
|
@@ -1217,13 +1348,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1217
1348
|
const role = props.role;
|
|
1218
1349
|
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
|
|
1219
1350
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1351
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1220
1352
|
return [
|
|
1221
1353
|
{
|
|
1222
1354
|
valid: false,
|
|
1223
1355
|
fixable: true,
|
|
1224
|
-
severity:
|
|
1356
|
+
severity: diagnostic.severity,
|
|
1225
1357
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1226
|
-
diagnostic
|
|
1358
|
+
diagnostic
|
|
1227
1359
|
}
|
|
1228
1360
|
];
|
|
1229
1361
|
}
|
|
@@ -1232,13 +1364,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1232
1364
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1233
1365
|
const role = props.role;
|
|
1234
1366
|
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
|
|
1367
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1235
1368
|
return [
|
|
1236
1369
|
{
|
|
1237
1370
|
valid: false,
|
|
1238
1371
|
fixable: true,
|
|
1239
|
-
severity:
|
|
1372
|
+
severity: diagnostic.severity,
|
|
1240
1373
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1241
|
-
diagnostic
|
|
1374
|
+
diagnostic
|
|
1242
1375
|
}
|
|
1243
1376
|
];
|
|
1244
1377
|
}
|
|
@@ -1246,13 +1379,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1246
1379
|
const role = props.role;
|
|
1247
1380
|
if (role !== "region") return NO_VIOLATIONS;
|
|
1248
1381
|
if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
|
|
1382
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1249
1383
|
return [
|
|
1250
1384
|
{
|
|
1251
1385
|
valid: false,
|
|
1252
1386
|
fixable: true,
|
|
1253
|
-
severity:
|
|
1387
|
+
severity: diagnostic.severity,
|
|
1254
1388
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1255
|
-
diagnostic
|
|
1389
|
+
diagnostic
|
|
1256
1390
|
}
|
|
1257
1391
|
];
|
|
1258
1392
|
}
|
|
@@ -1941,6 +2075,331 @@ var readonlyProps = ({
|
|
|
1941
2075
|
// ../core/src/html/evaluators.ts
|
|
1942
2076
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
1943
2077
|
|
|
2078
|
+
// ../core/src/html/input-rules.ts
|
|
2079
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2080
|
+
function omit(props, key) {
|
|
2081
|
+
const next = { ...props };
|
|
2082
|
+
delete next[key];
|
|
2083
|
+
return next;
|
|
2084
|
+
}
|
|
2085
|
+
function removeAttributeFix(attribute) {
|
|
2086
|
+
return {
|
|
2087
|
+
kind: `removeAttribute:${attribute}`,
|
|
2088
|
+
apply: ({ props }) => {
|
|
2089
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2090
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2091
|
+
}
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function inputAttributeRequiresType(attribute, allowedTypes) {
|
|
2095
|
+
const rule = ({ tag, props }) => {
|
|
2096
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2097
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2098
|
+
if (allowedTypes.includes(type)) return [];
|
|
2099
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2100
|
+
return [
|
|
2101
|
+
{
|
|
2102
|
+
valid: false,
|
|
2103
|
+
fixable: true,
|
|
2104
|
+
severity: diagnostic.severity,
|
|
2105
|
+
fix: removeAttributeFix(attribute),
|
|
2106
|
+
diagnostic
|
|
2107
|
+
}
|
|
2108
|
+
];
|
|
2109
|
+
};
|
|
2110
|
+
return Object.assign(rule, { readsProps: ["type", attribute] });
|
|
2111
|
+
}
|
|
2112
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2113
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2114
|
+
"number",
|
|
2115
|
+
"range",
|
|
2116
|
+
"date",
|
|
2117
|
+
"month",
|
|
2118
|
+
"week",
|
|
2119
|
+
"time",
|
|
2120
|
+
"datetime-local"
|
|
2121
|
+
];
|
|
2122
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2123
|
+
...TEXT_INPUT_TYPES,
|
|
2124
|
+
...NUMERIC_INPUT_TYPES,
|
|
2125
|
+
"checkbox",
|
|
2126
|
+
"radio",
|
|
2127
|
+
"file",
|
|
2128
|
+
"color",
|
|
2129
|
+
"hidden",
|
|
2130
|
+
"button",
|
|
2131
|
+
"submit",
|
|
2132
|
+
"reset",
|
|
2133
|
+
"image"
|
|
2134
|
+
]);
|
|
2135
|
+
var supportedInputTypeRule = Object.assign(
|
|
2136
|
+
({ tag, props }) => {
|
|
2137
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2138
|
+
const type = props.type;
|
|
2139
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2140
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2141
|
+
return [
|
|
2142
|
+
{
|
|
2143
|
+
valid: false,
|
|
2144
|
+
fixable: false,
|
|
2145
|
+
severity: diagnostic.severity,
|
|
2146
|
+
diagnostic
|
|
2147
|
+
}
|
|
2148
|
+
];
|
|
2149
|
+
},
|
|
2150
|
+
{ readsProps: ["type"] }
|
|
2151
|
+
);
|
|
2152
|
+
var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
|
|
2153
|
+
"checkbox",
|
|
2154
|
+
"radio"
|
|
2155
|
+
]);
|
|
2156
|
+
var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
|
|
2157
|
+
"email",
|
|
2158
|
+
"file"
|
|
2159
|
+
]);
|
|
2160
|
+
var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2161
|
+
"maxLength",
|
|
2162
|
+
TEXT_INPUT_TYPES
|
|
2163
|
+
);
|
|
2164
|
+
var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2165
|
+
"minLength",
|
|
2166
|
+
TEXT_INPUT_TYPES
|
|
2167
|
+
);
|
|
2168
|
+
var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
|
|
2169
|
+
var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
|
|
2170
|
+
var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
|
|
2171
|
+
var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
|
|
2172
|
+
var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
|
|
2173
|
+
var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
|
|
2174
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2175
|
+
({ tag, props }) => {
|
|
2176
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2177
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2178
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2179
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2180
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2181
|
+
},
|
|
2182
|
+
{ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
|
|
2183
|
+
);
|
|
2184
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2185
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2186
|
+
({ tag, props }) => {
|
|
2187
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2188
|
+
const autoComplete = props.autoComplete;
|
|
2189
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2190
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2191
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2192
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2193
|
+
},
|
|
2194
|
+
{ readsProps: ["type", "autoComplete"] }
|
|
2195
|
+
);
|
|
2196
|
+
var requiredReadOnlyConflictRule = Object.assign(
|
|
2197
|
+
({ tag, props }) => {
|
|
2198
|
+
if (tag !== "input" || !props.required || !props.readOnly) return [];
|
|
2199
|
+
const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
|
|
2200
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2201
|
+
},
|
|
2202
|
+
{ readsProps: ["required", "readOnly"] }
|
|
2203
|
+
);
|
|
2204
|
+
var INPUT_RULES = [
|
|
2205
|
+
supportedInputTypeRule,
|
|
2206
|
+
checkedRequiresCheckableTypeRule,
|
|
2207
|
+
multipleRequiresSupportedTypeRule,
|
|
2208
|
+
maxLengthRequiresTextTypeRule,
|
|
2209
|
+
minLengthRequiresTextTypeRule,
|
|
2210
|
+
patternRequiresTextTypeRule,
|
|
2211
|
+
minRequiresNumericTypeRule,
|
|
2212
|
+
maxRequiresNumericTypeRule,
|
|
2213
|
+
stepRequiresNumericTypeRule,
|
|
2214
|
+
acceptRequiresFileTypeRule,
|
|
2215
|
+
captureRequiresFileTypeRule,
|
|
2216
|
+
inputAccessibleNameRule,
|
|
2217
|
+
passwordAutocompleteRule,
|
|
2218
|
+
requiredReadOnlyConflictRule
|
|
2219
|
+
];
|
|
2220
|
+
|
|
2221
|
+
// ../core/src/html/role-restrictions.ts
|
|
2222
|
+
var ALLOWED_ROLES = {
|
|
2223
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2224
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2225
|
+
footer: ["group", "none", "presentation"],
|
|
2226
|
+
header: ["group", "none", "presentation"],
|
|
2227
|
+
main: [],
|
|
2228
|
+
nav: [],
|
|
2229
|
+
a: [
|
|
2230
|
+
"button",
|
|
2231
|
+
"checkbox",
|
|
2232
|
+
"menuitem",
|
|
2233
|
+
"menuitemcheckbox",
|
|
2234
|
+
"menuitemradio",
|
|
2235
|
+
"option",
|
|
2236
|
+
"radio",
|
|
2237
|
+
"switch",
|
|
2238
|
+
"tab",
|
|
2239
|
+
"treeitem"
|
|
2240
|
+
],
|
|
2241
|
+
button: [
|
|
2242
|
+
"checkbox",
|
|
2243
|
+
"link",
|
|
2244
|
+
"menuitem",
|
|
2245
|
+
"menuitemcheckbox",
|
|
2246
|
+
"menuitemradio",
|
|
2247
|
+
"option",
|
|
2248
|
+
"radio",
|
|
2249
|
+
"switch",
|
|
2250
|
+
"tab"
|
|
2251
|
+
],
|
|
2252
|
+
select: ["menu"],
|
|
2253
|
+
h1: ["tab", "presentation", "none"],
|
|
2254
|
+
h2: ["tab", "presentation", "none"],
|
|
2255
|
+
h3: ["tab", "presentation", "none"],
|
|
2256
|
+
h4: ["tab", "presentation", "none"],
|
|
2257
|
+
h5: ["tab", "presentation", "none"],
|
|
2258
|
+
h6: ["tab", "presentation", "none"],
|
|
2259
|
+
ul: [
|
|
2260
|
+
"directory",
|
|
2261
|
+
"group",
|
|
2262
|
+
"listbox",
|
|
2263
|
+
"menu",
|
|
2264
|
+
"menubar",
|
|
2265
|
+
"radiogroup",
|
|
2266
|
+
"tablist",
|
|
2267
|
+
"toolbar",
|
|
2268
|
+
"tree"
|
|
2269
|
+
],
|
|
2270
|
+
ol: [
|
|
2271
|
+
"directory",
|
|
2272
|
+
"group",
|
|
2273
|
+
"listbox",
|
|
2274
|
+
"menu",
|
|
2275
|
+
"menubar",
|
|
2276
|
+
"radiogroup",
|
|
2277
|
+
"tablist",
|
|
2278
|
+
"toolbar",
|
|
2279
|
+
"tree"
|
|
2280
|
+
],
|
|
2281
|
+
li: [
|
|
2282
|
+
"menuitem",
|
|
2283
|
+
"menuitemcheckbox",
|
|
2284
|
+
"menuitemradio",
|
|
2285
|
+
"option",
|
|
2286
|
+
"none",
|
|
2287
|
+
"presentation",
|
|
2288
|
+
"radio",
|
|
2289
|
+
"separator",
|
|
2290
|
+
"tab",
|
|
2291
|
+
"treeitem"
|
|
2292
|
+
],
|
|
2293
|
+
table: ["grid", "treegrid"],
|
|
2294
|
+
dialog: ["alertdialog"],
|
|
2295
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2296
|
+
};
|
|
2297
|
+
var IMG_NAMED_ROLES = [
|
|
2298
|
+
"button",
|
|
2299
|
+
"checkbox",
|
|
2300
|
+
"link",
|
|
2301
|
+
"menuitem",
|
|
2302
|
+
"menuitemcheckbox",
|
|
2303
|
+
"menuitemradio",
|
|
2304
|
+
"option",
|
|
2305
|
+
"progressbar",
|
|
2306
|
+
"scrollbar",
|
|
2307
|
+
"separator",
|
|
2308
|
+
"slider",
|
|
2309
|
+
"switch",
|
|
2310
|
+
"tab",
|
|
2311
|
+
"treeitem"
|
|
2312
|
+
];
|
|
2313
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2314
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2315
|
+
radio: ["menuitemradio"],
|
|
2316
|
+
range: [],
|
|
2317
|
+
number: [],
|
|
2318
|
+
search: ["combobox"],
|
|
2319
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2320
|
+
email: ["combobox"],
|
|
2321
|
+
tel: ["combobox"],
|
|
2322
|
+
url: ["combobox"],
|
|
2323
|
+
button: [
|
|
2324
|
+
"link",
|
|
2325
|
+
"menuitem",
|
|
2326
|
+
"menuitemcheckbox",
|
|
2327
|
+
"menuitemradio",
|
|
2328
|
+
"option",
|
|
2329
|
+
"radio",
|
|
2330
|
+
"switch",
|
|
2331
|
+
"tab"
|
|
2332
|
+
],
|
|
2333
|
+
submit: [
|
|
2334
|
+
"link",
|
|
2335
|
+
"menuitem",
|
|
2336
|
+
"menuitemcheckbox",
|
|
2337
|
+
"menuitemradio",
|
|
2338
|
+
"option",
|
|
2339
|
+
"radio",
|
|
2340
|
+
"switch",
|
|
2341
|
+
"tab"
|
|
2342
|
+
],
|
|
2343
|
+
reset: [
|
|
2344
|
+
"link",
|
|
2345
|
+
"menuitem",
|
|
2346
|
+
"menuitemcheckbox",
|
|
2347
|
+
"menuitemradio",
|
|
2348
|
+
"option",
|
|
2349
|
+
"radio",
|
|
2350
|
+
"switch",
|
|
2351
|
+
"tab"
|
|
2352
|
+
],
|
|
2353
|
+
image: [
|
|
2354
|
+
"link",
|
|
2355
|
+
"menuitem",
|
|
2356
|
+
"menuitemcheckbox",
|
|
2357
|
+
"menuitemradio",
|
|
2358
|
+
"option",
|
|
2359
|
+
"radio",
|
|
2360
|
+
"switch",
|
|
2361
|
+
"tab"
|
|
2362
|
+
],
|
|
2363
|
+
hidden: []
|
|
2364
|
+
};
|
|
2365
|
+
function getAllowedRoles(tag, props) {
|
|
2366
|
+
if (tag === "input") {
|
|
2367
|
+
const type = typeof props.type === "string" ? props.type : "text";
|
|
2368
|
+
return ALLOWED_INPUT_ROLES[type];
|
|
2369
|
+
}
|
|
2370
|
+
if (tag === "img") {
|
|
2371
|
+
return props.alt === "" ? [] : IMG_NAMED_ROLES;
|
|
2372
|
+
}
|
|
2373
|
+
return ALLOWED_ROLES[tag];
|
|
2374
|
+
}
|
|
2375
|
+
var removeRoleFix = {
|
|
2376
|
+
kind: "removeRole",
|
|
2377
|
+
apply: ({ props }) => {
|
|
2378
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2379
|
+
const { role: _role, ...rest } = props;
|
|
2380
|
+
return { applied: true, next: rest, previous: props };
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
var roleNotPermittedRule = Object.assign(
|
|
2384
|
+
({ tag, props, implicitRole }) => {
|
|
2385
|
+
const role = props.role;
|
|
2386
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2387
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2388
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2389
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2390
|
+
return [
|
|
2391
|
+
{
|
|
2392
|
+
valid: false,
|
|
2393
|
+
fixable: true,
|
|
2394
|
+
severity: diagnostic.severity,
|
|
2395
|
+
fix: removeRoleFix,
|
|
2396
|
+
diagnostic
|
|
2397
|
+
}
|
|
2398
|
+
];
|
|
2399
|
+
},
|
|
2400
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2401
|
+
);
|
|
2402
|
+
|
|
1944
2403
|
// ../core/src/html/aria-rules.ts
|
|
1945
2404
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
1946
2405
|
var removeLandmarkRoleOverride = {
|
|
@@ -1955,13 +2414,14 @@ function landmarkRoleRule({ tag, props, implicitRole }) {
|
|
|
1955
2414
|
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
1956
2415
|
const role = props.role;
|
|
1957
2416
|
if (!role || role === implicitRole) return [];
|
|
2417
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
1958
2418
|
return [
|
|
1959
2419
|
{
|
|
1960
2420
|
valid: false,
|
|
1961
2421
|
fixable: true,
|
|
1962
|
-
severity:
|
|
2422
|
+
severity: diagnostic.severity,
|
|
1963
2423
|
fix: removeLandmarkRoleOverride,
|
|
1964
|
-
diagnostic
|
|
2424
|
+
diagnostic
|
|
1965
2425
|
}
|
|
1966
2426
|
];
|
|
1967
2427
|
}
|
|
@@ -1981,7 +2441,12 @@ function landmarkNameAdvisory(ctx) {
|
|
|
1981
2441
|
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
1982
2442
|
return requireAccessibleName(ctx);
|
|
1983
2443
|
}
|
|
1984
|
-
var HTML_ARIA_RULES = [
|
|
2444
|
+
var HTML_ARIA_RULES = [
|
|
2445
|
+
landmarkRoleRule,
|
|
2446
|
+
landmarkNameAdvisory,
|
|
2447
|
+
roleNotPermittedRule,
|
|
2448
|
+
...INPUT_RULES
|
|
2449
|
+
];
|
|
1985
2450
|
|
|
1986
2451
|
// ../core/src/html/contracts.ts
|
|
1987
2452
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2442,21 +2907,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2442
2907
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2443
2908
|
|
|
2444
2909
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2445
|
-
import { DiagnosticCategory as
|
|
2910
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
2446
2911
|
var PluginDiagnostics = {
|
|
2447
2912
|
invalidShape(received) {
|
|
2448
2913
|
const got = received === null ? "null" : typeof received;
|
|
2449
2914
|
return {
|
|
2450
|
-
code:
|
|
2451
|
-
category:
|
|
2915
|
+
code: DiagnosticCode5.PluginInvalidShape,
|
|
2916
|
+
category: DiagnosticCategory5.Internal,
|
|
2452
2917
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2453
2918
|
};
|
|
2454
2919
|
},
|
|
2455
2920
|
pipelineReturnType(received) {
|
|
2456
2921
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2457
2922
|
return {
|
|
2458
|
-
code:
|
|
2459
|
-
category:
|
|
2923
|
+
code: DiagnosticCode5.PluginPipelineReturnType,
|
|
2924
|
+
category: DiagnosticCategory5.Internal,
|
|
2460
2925
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2461
2926
|
};
|
|
2462
2927
|
}
|