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/svelte/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,34 +915,148 @@ 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
|
+
}
|
|
903
970
|
}
|
|
904
971
|
};
|
|
905
972
|
|
|
906
|
-
// ../../lib/contract/src/diagnostics/
|
|
973
|
+
// ../../lib/contract/src/diagnostics/input-accessibility.ts
|
|
907
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
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
// ../../lib/contract/src/diagnostics/slot.ts
|
|
1038
|
+
import { DiagnosticCategory as DiagnosticCategory5, DiagnosticCode as DiagnosticCode5 } from "../_shared/diagnostics.js";
|
|
908
1039
|
var SlotDiagnostics = {
|
|
909
1040
|
exclusive(name) {
|
|
910
1041
|
return {
|
|
911
|
-
code:
|
|
912
|
-
category:
|
|
1042
|
+
code: DiagnosticCode5.SlotExclusive,
|
|
1043
|
+
category: DiagnosticCategory5.Contract,
|
|
913
1044
|
component: name,
|
|
914
1045
|
message: `${name}: "as" and "asChild" are mutually exclusive`
|
|
915
1046
|
};
|
|
916
1047
|
},
|
|
917
1048
|
singleChildRequired(name, elementTerm) {
|
|
918
1049
|
return {
|
|
919
|
-
code:
|
|
920
|
-
category:
|
|
1050
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1051
|
+
category: DiagnosticCategory5.Contract,
|
|
921
1052
|
component: name,
|
|
922
1053
|
message: `${name}: asChild requires a ${elementTerm} child`
|
|
923
1054
|
};
|
|
924
1055
|
},
|
|
925
1056
|
singleChildExceeded(name, elementTerm, count) {
|
|
926
1057
|
return {
|
|
927
|
-
code:
|
|
928
|
-
category:
|
|
1058
|
+
code: DiagnosticCode5.SlotSingleChild,
|
|
1059
|
+
category: DiagnosticCategory5.Contract,
|
|
929
1060
|
component: name,
|
|
930
1061
|
message: `${name}: asChild requires exactly one ${elementTerm} child, got ${count}`
|
|
931
1062
|
};
|
|
@@ -933,16 +1064,16 @@ var SlotDiagnostics = {
|
|
|
933
1064
|
discardedChildren(name, elementTerm, count) {
|
|
934
1065
|
const suffix = count === 1 ? "" : "ren";
|
|
935
1066
|
return {
|
|
936
|
-
code:
|
|
937
|
-
category:
|
|
1067
|
+
code: DiagnosticCode5.SlotDiscardedChildren,
|
|
1068
|
+
category: DiagnosticCategory5.Contract,
|
|
938
1069
|
component: name,
|
|
939
1070
|
message: `${name}: asChild discarded ${count} non-element child${suffix} \u2014 only ${elementTerm}s are valid asChild children.`
|
|
940
1071
|
};
|
|
941
1072
|
},
|
|
942
1073
|
renderFnRequired(name, received) {
|
|
943
1074
|
return {
|
|
944
|
-
code:
|
|
945
|
-
category:
|
|
1075
|
+
code: DiagnosticCode5.SlotRenderFn,
|
|
1076
|
+
category: DiagnosticCategory5.Contract,
|
|
946
1077
|
component: name,
|
|
947
1078
|
message: `${name}: asChild requires a render function as children, got ${received}`
|
|
948
1079
|
};
|
|
@@ -1004,7 +1135,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1004
1135
|
tag,
|
|
1005
1136
|
role: "",
|
|
1006
1137
|
attribute: void 0,
|
|
1007
|
-
severity:
|
|
1138
|
+
severity: d.severity,
|
|
1008
1139
|
phase: "evaluate"
|
|
1009
1140
|
}
|
|
1010
1141
|
]
|
|
@@ -1263,13 +1394,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1263
1394
|
const role = props.role;
|
|
1264
1395
|
if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
|
|
1265
1396
|
if (isStrongImplicitRole(tag) && role === "region") {
|
|
1397
|
+
const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
|
|
1266
1398
|
return [
|
|
1267
1399
|
{
|
|
1268
1400
|
valid: false,
|
|
1269
1401
|
fixable: true,
|
|
1270
|
-
severity:
|
|
1402
|
+
severity: diagnostic.severity,
|
|
1271
1403
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1272
|
-
diagnostic
|
|
1404
|
+
diagnostic
|
|
1273
1405
|
}
|
|
1274
1406
|
];
|
|
1275
1407
|
}
|
|
@@ -1278,13 +1410,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1278
1410
|
static #checkRedundantRole({ tag, props, implicitRole }) {
|
|
1279
1411
|
const role = props.role;
|
|
1280
1412
|
if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
|
|
1413
|
+
const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
|
|
1281
1414
|
return [
|
|
1282
1415
|
{
|
|
1283
1416
|
valid: false,
|
|
1284
1417
|
fixable: true,
|
|
1285
|
-
severity:
|
|
1418
|
+
severity: diagnostic.severity,
|
|
1286
1419
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1287
|
-
diagnostic
|
|
1420
|
+
diagnostic
|
|
1288
1421
|
}
|
|
1289
1422
|
];
|
|
1290
1423
|
}
|
|
@@ -1292,13 +1425,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
|
|
|
1292
1425
|
const role = props.role;
|
|
1293
1426
|
if (role !== "region") return NO_VIOLATIONS;
|
|
1294
1427
|
if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
|
|
1428
|
+
const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
|
|
1295
1429
|
return [
|
|
1296
1430
|
{
|
|
1297
1431
|
valid: false,
|
|
1298
1432
|
fixable: true,
|
|
1299
|
-
severity:
|
|
1433
|
+
severity: diagnostic.severity,
|
|
1300
1434
|
fix: _AriaPolicyEngine.#removeRole,
|
|
1301
|
-
diagnostic
|
|
1435
|
+
diagnostic
|
|
1302
1436
|
}
|
|
1303
1437
|
];
|
|
1304
1438
|
}
|
|
@@ -1987,6 +2121,331 @@ var readonlyProps = ({
|
|
|
1987
2121
|
// ../core/src/html/evaluators.ts
|
|
1988
2122
|
import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
|
|
1989
2123
|
|
|
2124
|
+
// ../core/src/html/input-rules.ts
|
|
2125
|
+
var DEFAULT_INPUT_TYPE = "text";
|
|
2126
|
+
function omit(props, key) {
|
|
2127
|
+
const next = { ...props };
|
|
2128
|
+
delete next[key];
|
|
2129
|
+
return next;
|
|
2130
|
+
}
|
|
2131
|
+
function removeAttributeFix(attribute) {
|
|
2132
|
+
return {
|
|
2133
|
+
kind: `removeAttribute:${attribute}`,
|
|
2134
|
+
apply: ({ props }) => {
|
|
2135
|
+
if (!(attribute in props)) return { applied: false, next: props };
|
|
2136
|
+
return { applied: true, next: omit(props, attribute), previous: props };
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
function inputAttributeRequiresType(attribute, allowedTypes) {
|
|
2141
|
+
const rule = ({ tag, props }) => {
|
|
2142
|
+
if (tag !== "input" || !(attribute in props)) return [];
|
|
2143
|
+
const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
|
|
2144
|
+
if (allowedTypes.includes(type)) return [];
|
|
2145
|
+
const diagnostic = HtmlDiagnostics.input.attributeIgnoredForType(attribute, type, allowedTypes);
|
|
2146
|
+
return [
|
|
2147
|
+
{
|
|
2148
|
+
valid: false,
|
|
2149
|
+
fixable: true,
|
|
2150
|
+
severity: diagnostic.severity,
|
|
2151
|
+
fix: removeAttributeFix(attribute),
|
|
2152
|
+
diagnostic
|
|
2153
|
+
}
|
|
2154
|
+
];
|
|
2155
|
+
};
|
|
2156
|
+
return Object.assign(rule, { readsProps: ["type", attribute] });
|
|
2157
|
+
}
|
|
2158
|
+
var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
|
|
2159
|
+
var NUMERIC_INPUT_TYPES = [
|
|
2160
|
+
"number",
|
|
2161
|
+
"range",
|
|
2162
|
+
"date",
|
|
2163
|
+
"month",
|
|
2164
|
+
"week",
|
|
2165
|
+
"time",
|
|
2166
|
+
"datetime-local"
|
|
2167
|
+
];
|
|
2168
|
+
var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
2169
|
+
...TEXT_INPUT_TYPES,
|
|
2170
|
+
...NUMERIC_INPUT_TYPES,
|
|
2171
|
+
"checkbox",
|
|
2172
|
+
"radio",
|
|
2173
|
+
"file",
|
|
2174
|
+
"color",
|
|
2175
|
+
"hidden",
|
|
2176
|
+
"button",
|
|
2177
|
+
"submit",
|
|
2178
|
+
"reset",
|
|
2179
|
+
"image"
|
|
2180
|
+
]);
|
|
2181
|
+
var supportedInputTypeRule = Object.assign(
|
|
2182
|
+
({ tag, props }) => {
|
|
2183
|
+
if (tag !== "input" || typeof props.type !== "string") return [];
|
|
2184
|
+
const type = props.type;
|
|
2185
|
+
if (HTML_INPUT_TYPES.has(type)) return [];
|
|
2186
|
+
const diagnostic = HtmlDiagnostics.input.unsupportedType(type);
|
|
2187
|
+
return [
|
|
2188
|
+
{
|
|
2189
|
+
valid: false,
|
|
2190
|
+
fixable: false,
|
|
2191
|
+
severity: diagnostic.severity,
|
|
2192
|
+
diagnostic
|
|
2193
|
+
}
|
|
2194
|
+
];
|
|
2195
|
+
},
|
|
2196
|
+
{ readsProps: ["type"] }
|
|
2197
|
+
);
|
|
2198
|
+
var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
|
|
2199
|
+
"checkbox",
|
|
2200
|
+
"radio"
|
|
2201
|
+
]);
|
|
2202
|
+
var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
|
|
2203
|
+
"email",
|
|
2204
|
+
"file"
|
|
2205
|
+
]);
|
|
2206
|
+
var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2207
|
+
"maxLength",
|
|
2208
|
+
TEXT_INPUT_TYPES
|
|
2209
|
+
);
|
|
2210
|
+
var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
|
|
2211
|
+
"minLength",
|
|
2212
|
+
TEXT_INPUT_TYPES
|
|
2213
|
+
);
|
|
2214
|
+
var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
|
|
2215
|
+
var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
|
|
2216
|
+
var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
|
|
2217
|
+
var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
|
|
2218
|
+
var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
|
|
2219
|
+
var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
|
|
2220
|
+
var inputAccessibleNameRule = Object.assign(
|
|
2221
|
+
({ tag, props }) => {
|
|
2222
|
+
if (tag !== "input" || props.type === "hidden") return [];
|
|
2223
|
+
if ("aria-label" in props || "aria-labelledby" in props) return [];
|
|
2224
|
+
const hasPlaceholder = typeof props.placeholder === "string" && props.placeholder.length > 0;
|
|
2225
|
+
const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
|
|
2226
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2227
|
+
},
|
|
2228
|
+
{ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
|
|
2229
|
+
);
|
|
2230
|
+
var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
|
|
2231
|
+
var passwordAutocompleteRule = Object.assign(
|
|
2232
|
+
({ tag, props }) => {
|
|
2233
|
+
if (tag !== "input" || props.type !== "password") return [];
|
|
2234
|
+
const autoComplete = props.autoComplete;
|
|
2235
|
+
const tokens = typeof autoComplete === "string" ? autoComplete.split(" ") : [];
|
|
2236
|
+
if (PASSWORD_AUTOCOMPLETE_VALUES.some((value) => tokens.includes(value))) return [];
|
|
2237
|
+
const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
|
|
2238
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2239
|
+
},
|
|
2240
|
+
{ readsProps: ["type", "autoComplete"] }
|
|
2241
|
+
);
|
|
2242
|
+
var requiredReadOnlyConflictRule = Object.assign(
|
|
2243
|
+
({ tag, props }) => {
|
|
2244
|
+
if (tag !== "input" || !props.required || !props.readOnly) return [];
|
|
2245
|
+
const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
|
|
2246
|
+
return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
|
|
2247
|
+
},
|
|
2248
|
+
{ readsProps: ["required", "readOnly"] }
|
|
2249
|
+
);
|
|
2250
|
+
var INPUT_RULES = [
|
|
2251
|
+
supportedInputTypeRule,
|
|
2252
|
+
checkedRequiresCheckableTypeRule,
|
|
2253
|
+
multipleRequiresSupportedTypeRule,
|
|
2254
|
+
maxLengthRequiresTextTypeRule,
|
|
2255
|
+
minLengthRequiresTextTypeRule,
|
|
2256
|
+
patternRequiresTextTypeRule,
|
|
2257
|
+
minRequiresNumericTypeRule,
|
|
2258
|
+
maxRequiresNumericTypeRule,
|
|
2259
|
+
stepRequiresNumericTypeRule,
|
|
2260
|
+
acceptRequiresFileTypeRule,
|
|
2261
|
+
captureRequiresFileTypeRule,
|
|
2262
|
+
inputAccessibleNameRule,
|
|
2263
|
+
passwordAutocompleteRule,
|
|
2264
|
+
requiredReadOnlyConflictRule
|
|
2265
|
+
];
|
|
2266
|
+
|
|
2267
|
+
// ../core/src/html/role-restrictions.ts
|
|
2268
|
+
var ALLOWED_ROLES = {
|
|
2269
|
+
article: ["application", "document", "feed", "main", "none", "presentation", "region"],
|
|
2270
|
+
aside: ["feed", "none", "presentation", "region", "search"],
|
|
2271
|
+
footer: ["group", "none", "presentation"],
|
|
2272
|
+
header: ["group", "none", "presentation"],
|
|
2273
|
+
main: [],
|
|
2274
|
+
nav: [],
|
|
2275
|
+
a: [
|
|
2276
|
+
"button",
|
|
2277
|
+
"checkbox",
|
|
2278
|
+
"menuitem",
|
|
2279
|
+
"menuitemcheckbox",
|
|
2280
|
+
"menuitemradio",
|
|
2281
|
+
"option",
|
|
2282
|
+
"radio",
|
|
2283
|
+
"switch",
|
|
2284
|
+
"tab",
|
|
2285
|
+
"treeitem"
|
|
2286
|
+
],
|
|
2287
|
+
button: [
|
|
2288
|
+
"checkbox",
|
|
2289
|
+
"link",
|
|
2290
|
+
"menuitem",
|
|
2291
|
+
"menuitemcheckbox",
|
|
2292
|
+
"menuitemradio",
|
|
2293
|
+
"option",
|
|
2294
|
+
"radio",
|
|
2295
|
+
"switch",
|
|
2296
|
+
"tab"
|
|
2297
|
+
],
|
|
2298
|
+
select: ["menu"],
|
|
2299
|
+
h1: ["tab", "presentation", "none"],
|
|
2300
|
+
h2: ["tab", "presentation", "none"],
|
|
2301
|
+
h3: ["tab", "presentation", "none"],
|
|
2302
|
+
h4: ["tab", "presentation", "none"],
|
|
2303
|
+
h5: ["tab", "presentation", "none"],
|
|
2304
|
+
h6: ["tab", "presentation", "none"],
|
|
2305
|
+
ul: [
|
|
2306
|
+
"directory",
|
|
2307
|
+
"group",
|
|
2308
|
+
"listbox",
|
|
2309
|
+
"menu",
|
|
2310
|
+
"menubar",
|
|
2311
|
+
"radiogroup",
|
|
2312
|
+
"tablist",
|
|
2313
|
+
"toolbar",
|
|
2314
|
+
"tree"
|
|
2315
|
+
],
|
|
2316
|
+
ol: [
|
|
2317
|
+
"directory",
|
|
2318
|
+
"group",
|
|
2319
|
+
"listbox",
|
|
2320
|
+
"menu",
|
|
2321
|
+
"menubar",
|
|
2322
|
+
"radiogroup",
|
|
2323
|
+
"tablist",
|
|
2324
|
+
"toolbar",
|
|
2325
|
+
"tree"
|
|
2326
|
+
],
|
|
2327
|
+
li: [
|
|
2328
|
+
"menuitem",
|
|
2329
|
+
"menuitemcheckbox",
|
|
2330
|
+
"menuitemradio",
|
|
2331
|
+
"option",
|
|
2332
|
+
"none",
|
|
2333
|
+
"presentation",
|
|
2334
|
+
"radio",
|
|
2335
|
+
"separator",
|
|
2336
|
+
"tab",
|
|
2337
|
+
"treeitem"
|
|
2338
|
+
],
|
|
2339
|
+
table: ["grid", "treegrid"],
|
|
2340
|
+
dialog: ["alertdialog"],
|
|
2341
|
+
fieldset: ["none", "presentation", "radiogroup"]
|
|
2342
|
+
};
|
|
2343
|
+
var IMG_NAMED_ROLES = [
|
|
2344
|
+
"button",
|
|
2345
|
+
"checkbox",
|
|
2346
|
+
"link",
|
|
2347
|
+
"menuitem",
|
|
2348
|
+
"menuitemcheckbox",
|
|
2349
|
+
"menuitemradio",
|
|
2350
|
+
"option",
|
|
2351
|
+
"progressbar",
|
|
2352
|
+
"scrollbar",
|
|
2353
|
+
"separator",
|
|
2354
|
+
"slider",
|
|
2355
|
+
"switch",
|
|
2356
|
+
"tab",
|
|
2357
|
+
"treeitem"
|
|
2358
|
+
];
|
|
2359
|
+
var ALLOWED_INPUT_ROLES = {
|
|
2360
|
+
checkbox: ["menuitemcheckbox", "option", "switch", "button"],
|
|
2361
|
+
radio: ["menuitemradio"],
|
|
2362
|
+
range: [],
|
|
2363
|
+
number: [],
|
|
2364
|
+
search: ["combobox"],
|
|
2365
|
+
text: ["combobox", "searchbox", "spinbutton"],
|
|
2366
|
+
email: ["combobox"],
|
|
2367
|
+
tel: ["combobox"],
|
|
2368
|
+
url: ["combobox"],
|
|
2369
|
+
button: [
|
|
2370
|
+
"link",
|
|
2371
|
+
"menuitem",
|
|
2372
|
+
"menuitemcheckbox",
|
|
2373
|
+
"menuitemradio",
|
|
2374
|
+
"option",
|
|
2375
|
+
"radio",
|
|
2376
|
+
"switch",
|
|
2377
|
+
"tab"
|
|
2378
|
+
],
|
|
2379
|
+
submit: [
|
|
2380
|
+
"link",
|
|
2381
|
+
"menuitem",
|
|
2382
|
+
"menuitemcheckbox",
|
|
2383
|
+
"menuitemradio",
|
|
2384
|
+
"option",
|
|
2385
|
+
"radio",
|
|
2386
|
+
"switch",
|
|
2387
|
+
"tab"
|
|
2388
|
+
],
|
|
2389
|
+
reset: [
|
|
2390
|
+
"link",
|
|
2391
|
+
"menuitem",
|
|
2392
|
+
"menuitemcheckbox",
|
|
2393
|
+
"menuitemradio",
|
|
2394
|
+
"option",
|
|
2395
|
+
"radio",
|
|
2396
|
+
"switch",
|
|
2397
|
+
"tab"
|
|
2398
|
+
],
|
|
2399
|
+
image: [
|
|
2400
|
+
"link",
|
|
2401
|
+
"menuitem",
|
|
2402
|
+
"menuitemcheckbox",
|
|
2403
|
+
"menuitemradio",
|
|
2404
|
+
"option",
|
|
2405
|
+
"radio",
|
|
2406
|
+
"switch",
|
|
2407
|
+
"tab"
|
|
2408
|
+
],
|
|
2409
|
+
hidden: []
|
|
2410
|
+
};
|
|
2411
|
+
function getAllowedRoles(tag, props) {
|
|
2412
|
+
if (tag === "input") {
|
|
2413
|
+
const type = typeof props.type === "string" ? props.type : "text";
|
|
2414
|
+
return ALLOWED_INPUT_ROLES[type];
|
|
2415
|
+
}
|
|
2416
|
+
if (tag === "img") {
|
|
2417
|
+
return props.alt === "" ? [] : IMG_NAMED_ROLES;
|
|
2418
|
+
}
|
|
2419
|
+
return ALLOWED_ROLES[tag];
|
|
2420
|
+
}
|
|
2421
|
+
var removeRoleFix = {
|
|
2422
|
+
kind: "removeRole",
|
|
2423
|
+
apply: ({ props }) => {
|
|
2424
|
+
if (!("role" in props)) return { applied: false, next: props };
|
|
2425
|
+
const { role: _role, ...rest } = props;
|
|
2426
|
+
return { applied: true, next: rest, previous: props };
|
|
2427
|
+
}
|
|
2428
|
+
};
|
|
2429
|
+
var roleNotPermittedRule = Object.assign(
|
|
2430
|
+
({ tag, props, implicitRole }) => {
|
|
2431
|
+
const role = props.role;
|
|
2432
|
+
if (typeof role !== "string" || role.length === 0 || role === implicitRole) return [];
|
|
2433
|
+
const allowed = getAllowedRoles(tag, props);
|
|
2434
|
+
if (allowed === void 0 || allowed.includes(role)) return [];
|
|
2435
|
+
const diagnostic = HtmlDiagnostics.roleNotPermitted(tag, role, allowed);
|
|
2436
|
+
return [
|
|
2437
|
+
{
|
|
2438
|
+
valid: false,
|
|
2439
|
+
fixable: true,
|
|
2440
|
+
severity: diagnostic.severity,
|
|
2441
|
+
fix: removeRoleFix,
|
|
2442
|
+
diagnostic
|
|
2443
|
+
}
|
|
2444
|
+
];
|
|
2445
|
+
},
|
|
2446
|
+
{ readsProps: ["role", "type", "alt"] }
|
|
2447
|
+
);
|
|
2448
|
+
|
|
1990
2449
|
// ../core/src/html/aria-rules.ts
|
|
1991
2450
|
var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
|
|
1992
2451
|
var removeLandmarkRoleOverride = {
|
|
@@ -2001,13 +2460,14 @@ function landmarkRoleRule({ tag, props, implicitRole }) {
|
|
|
2001
2460
|
if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
|
|
2002
2461
|
const role = props.role;
|
|
2003
2462
|
if (!role || role === implicitRole) return [];
|
|
2463
|
+
const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
|
|
2004
2464
|
return [
|
|
2005
2465
|
{
|
|
2006
2466
|
valid: false,
|
|
2007
2467
|
fixable: true,
|
|
2008
|
-
severity:
|
|
2468
|
+
severity: diagnostic.severity,
|
|
2009
2469
|
fix: removeLandmarkRoleOverride,
|
|
2010
|
-
diagnostic
|
|
2470
|
+
diagnostic
|
|
2011
2471
|
}
|
|
2012
2472
|
];
|
|
2013
2473
|
}
|
|
@@ -2027,7 +2487,12 @@ function landmarkNameAdvisory(ctx) {
|
|
|
2027
2487
|
if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
|
|
2028
2488
|
return requireAccessibleName(ctx);
|
|
2029
2489
|
}
|
|
2030
|
-
var HTML_ARIA_RULES = [
|
|
2490
|
+
var HTML_ARIA_RULES = [
|
|
2491
|
+
landmarkRoleRule,
|
|
2492
|
+
landmarkNameAdvisory,
|
|
2493
|
+
roleNotPermittedRule,
|
|
2494
|
+
...INPUT_RULES
|
|
2495
|
+
];
|
|
2031
2496
|
|
|
2032
2497
|
// ../core/src/html/contracts.ts
|
|
2033
2498
|
import { warnDiagnostics } from "../_shared/diagnostics.js";
|
|
@@ -2488,21 +2953,21 @@ function validateRenderProps(diagnostics, options, props, recipeKey) {
|
|
|
2488
2953
|
import { throwDiagnostics } from "../_shared/diagnostics.js";
|
|
2489
2954
|
|
|
2490
2955
|
// ../core/src/factory/plugin-diagnostics.ts
|
|
2491
|
-
import { DiagnosticCategory as
|
|
2956
|
+
import { DiagnosticCategory as DiagnosticCategory6, DiagnosticCode as DiagnosticCode6 } from "../_shared/diagnostics.js";
|
|
2492
2957
|
var PluginDiagnostics = {
|
|
2493
2958
|
invalidShape(received) {
|
|
2494
2959
|
const got = received === null ? "null" : typeof received;
|
|
2495
2960
|
return {
|
|
2496
|
-
code:
|
|
2497
|
-
category:
|
|
2961
|
+
code: DiagnosticCode6.PluginInvalidShape,
|
|
2962
|
+
category: DiagnosticCategory6.Internal,
|
|
2498
2963
|
message: `[praxis-kit] Plugin factory must return an object with a 'pipeline' function. Got: ${got}.`
|
|
2499
2964
|
};
|
|
2500
2965
|
},
|
|
2501
2966
|
pipelineReturnType(received) {
|
|
2502
2967
|
const got = received === null ? "null" : Array.isArray(received) ? "array" : typeof received;
|
|
2503
2968
|
return {
|
|
2504
|
-
code:
|
|
2505
|
-
category:
|
|
2969
|
+
code: DiagnosticCode6.PluginPipelineReturnType,
|
|
2970
|
+
category: DiagnosticCategory6.Internal,
|
|
2506
2971
|
message: `[praxis-kit] Plugin pipeline must return a string. Got: ${got}.`
|
|
2507
2972
|
};
|
|
2508
2973
|
}
|
package/dist/tailwind/index.js
CHANGED