@markuplint/rules 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/CLAUDE.md +19 -19
- package/lib/aria-prop-requires-role/index.js +2 -1
- package/lib/attr-check.js +6 -12
- package/lib/attr-eligibility.d.ts +15 -8
- package/lib/attr-eligibility.js +19 -10
- package/lib/attr-order/index.js +24 -18
- package/lib/case-sensitive-attr-name/index.js +24 -5
- package/lib/class-naming/index.js +6 -0
- package/lib/debug.d.ts +8 -1
- package/lib/debug.js +8 -1
- package/lib/element-supports-aria-prop/index.js +2 -1
- package/lib/form-attr-references-form/index.d.ts +6 -3
- package/lib/form-attr-references-form/index.js +6 -3
- package/lib/helpers.d.ts +111 -1
- package/lib/helpers.js +212 -2
- package/lib/input-list-references-datalist/index.d.ts +8 -6
- package/lib/input-list-references-datalist/index.js +8 -6
- package/lib/label-for-references-labelable/index.d.ts +8 -5
- package/lib/label-for-references-labelable/index.js +8 -5
- package/lib/landmark-roles/roles.js +14 -2
- package/lib/map-id-name-match/index.d.ts +3 -3
- package/lib/map-id-name-match/index.js +3 -3
- package/lib/no-ambiguous-navigable-target-names/index.js +2 -1
- package/lib/no-aria-hidden-on-hidden-until-found/index.d.ts +9 -6
- package/lib/no-aria-hidden-on-hidden-until-found/index.js +9 -6
- package/lib/no-aria-hidden-on-hidden-until-found/meta.d.ts +1 -1
- package/lib/no-aria-hidden-on-hidden-until-found/meta.js +1 -1
- package/lib/no-aria-on-unsupported-element/index.js +2 -2
- package/lib/no-aria-on-unsupported-element/meta.d.ts +1 -1
- package/lib/no-aria-on-unsupported-element/meta.js +1 -1
- package/lib/no-boolean-attr-value/index.js +2 -2
- package/lib/no-content-after-body/index.d.ts +2 -2
- package/lib/no-content-after-body/index.js +2 -2
- package/lib/no-contradictory-aria-prop/index.js +2 -1
- package/lib/no-default-aria-value/index.js +2 -1
- package/lib/no-default-value/index.js +2 -2
- package/lib/no-deprecated-aria-prop/index.js +2 -1
- package/lib/no-deprecated-attr/index.js +2 -1
- package/lib/no-disallowed-attr/index.d.ts +7 -1
- package/lib/no-disallowed-attr/index.js +13 -20
- package/lib/no-duplicate-attr/index.js +1 -2
- package/lib/no-duplicate-autofocus/index.js +2 -2
- package/lib/no-duplicate-h1/index.d.ts +1 -1
- package/lib/no-duplicate-h1/index.js +1 -1
- package/lib/no-empty-table-track/index.d.ts +6 -2
- package/lib/no-empty-table-track/index.js +6 -2
- package/lib/no-experimental-features/index.d.ts +5 -1
- package/lib/no-experimental-features/index.js +6 -8
- package/lib/no-hardcoded-id/index.js +1 -1
- package/lib/no-ineffective-attr/index.js +2 -2
- package/lib/no-input-file-value/index.d.ts +10 -4
- package/lib/no-input-file-value/index.js +10 -4
- package/lib/no-invalid-aria-prop-value/index.js +2 -1
- package/lib/no-invalid-attr-value/index.d.ts +3 -0
- package/lib/no-invalid-attr-value/index.js +9 -24
- package/lib/no-nonstandard-features/index.d.ts +5 -1
- package/lib/no-nonstandard-features/index.js +6 -8
- package/lib/no-obsolete-attr/index.js +3 -3
- package/lib/no-prohibited-naming/index.js +2 -1
- package/lib/no-redundant-accessible-name/index.js +17 -1
- package/lib/no-redundant-aria-prop/index.js +2 -1
- package/lib/no-refer-to-non-existent-id/index.js +4 -4
- package/lib/no-restricted-attr/index.d.ts +4 -0
- package/lib/no-restricted-attr/index.js +5 -22
- package/lib/no-stray-head-or-body-tag/index.d.ts +6 -4
- package/lib/no-stray-head-or-body-tag/index.js +11 -6
- package/lib/no-unknown-attr/index.d.ts +7 -1
- package/lib/no-unknown-attr/index.js +11 -25
- package/lib/no-unpaired-srcset-sizes/index.d.ts +3 -2
- package/lib/no-unpaired-srcset-sizes/index.js +23 -17
- package/lib/no-unsupported-browser-features/compat-data.d.ts +15 -2
- package/lib/no-unsupported-browser-features/compat-data.js +48 -3
- package/lib/no-unsupported-browser-features/index.d.ts +5 -1
- package/lib/no-unsupported-browser-features/index.js +6 -7
- package/lib/permitted-contents/choice.js +19 -8
- package/lib/permitted-contents/content-model.d.ts +24 -1
- package/lib/permitted-contents/content-model.js +56 -9
- package/lib/permitted-contents/count-pattern.js +36 -13
- package/lib/permitted-contents/debug.browser.d.ts +18 -0
- package/lib/permitted-contents/debug.browser.js +27 -0
- package/lib/permitted-contents/debug.d.ts +18 -0
- package/lib/permitted-contents/debug.js +27 -0
- package/lib/permitted-contents/index.js +2 -2
- package/lib/permitted-contents/matches-selector.js +40 -18
- package/lib/permitted-contents/order.js +23 -11
- package/lib/permitted-contents/represent-transparent-nodes.js +3 -1
- package/lib/permitted-contents/transparent.js +6 -2
- package/lib/placeholder-label-option/index.d.ts +3 -3
- package/lib/placeholder-label-option/index.js +163 -37
- package/lib/progress-value-bounds/index.d.ts +1 -1
- package/lib/progress-value-bounds/index.js +1 -1
- package/lib/require-attr/index.d.ts +6 -1
- package/lib/require-attr/index.js +13 -5
- package/lib/require-h1/index.d.ts +1 -1
- package/lib/require-h1/index.js +1 -1
- package/lib/role-supports-aria-prop/index.js +2 -1
- package/lib/sizes-auto-requires-lazy-loading/index.d.ts +3 -1
- package/lib/sizes-auto-requires-lazy-loading/index.js +30 -16
- package/lib/srcset-sizes-constraint/parse-srcset.d.ts +6 -5
- package/lib/srcset-sizes-constraint/parse-srcset.js +8 -7
- package/lib/srcset-sizes-constraint/sibling-helpers.d.ts +31 -0
- package/lib/srcset-sizes-constraint/sibling-helpers.js +58 -0
- package/lib/valid-speculation-rules/index.js +1 -1
- package/lib/wai-aria/checkings/contradictory-aria-prop.js +3 -5
- package/lib/wai-aria/checkings/default-value.js +1 -1
- package/lib/wai-aria/checkings/deprecated-props.js +2 -2
- package/lib/wai-aria/checkings/element-supports-aria-prop.js +5 -4
- package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +1 -1
- package/lib/wai-aria/checkings/interaction-in-hidden.js +1 -1
- package/lib/wai-aria/checkings/no-global-prop.js +1 -1
- package/lib/wai-aria/checkings/presentational-children.d.ts +4 -4
- package/lib/wai-aria/checkings/presentational-children.js +6 -5
- package/lib/wai-aria/checkings/prohibited-naming.js +5 -4
- package/lib/wai-aria/checkings/redundant-aria-prop.js +3 -5
- package/lib/wai-aria/checkings/required-prop.js +1 -4
- package/lib/wai-aria/checkings/role-supports-aria-prop.js +4 -3
- package/lib/wai-aria/checkings/tab-requires-tabpanel.d.ts +3 -2
- package/lib/wai-aria/checkings/tab-requires-tabpanel.js +3 -2
- package/lib/wai-aria/checkings/value.d.ts +1 -1
- package/lib/wai-aria/checkings/value.js +2 -2
- package/package.json +15 -15
|
@@ -13,7 +13,7 @@ export const checkingDefaultValue = ({ attr, propSpecs }) => t => {
|
|
|
13
13
|
if (attr.isDynamicValue) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
16
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
17
17
|
const value = attr.value.trim().toLowerCase();
|
|
18
18
|
if (propSpec?.defaultValue != null && propSpec.defaultValue === value) {
|
|
19
19
|
return {
|
|
@@ -13,9 +13,9 @@ export const checkingDeprecatedProps = ({ attr, role, propSpecs }) => t => {
|
|
|
13
13
|
if (!role) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
const props = role.ownedProperties.find(p => p.name === attr.
|
|
16
|
+
const props = role.ownedProperties.find(p => p.name === attr.nameKey);
|
|
17
17
|
if (props?.deprecated) {
|
|
18
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
18
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
19
19
|
return {
|
|
20
20
|
scope: attr,
|
|
21
21
|
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`), 'deprecated'), t('the "{0*}" {1}', role.name, 'role')),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAriaAttr } from '../../helpers.js';
|
|
1
2
|
import { ARIA_RECOMMENDED_VERSION, getARIA } from '@markuplint/ml-spec';
|
|
2
3
|
/**
|
|
3
4
|
* Checks the ARIA in HTML element-specific `aria-*` restrictions: spec data
|
|
@@ -13,13 +14,13 @@ import { ARIA_RECOMMENDED_VERSION, getARIA } from '@markuplint/ml-spec';
|
|
|
13
14
|
* @returns A violation if the element-specific restriction disallows the property.
|
|
14
15
|
*/
|
|
15
16
|
export const checkingElementSupportsAriaProp = ({ attr, role, propSpecs }) => t => {
|
|
16
|
-
if (
|
|
17
|
+
if (!isAriaAttr(attr)) {
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
const ariaVersion = attr.rule.options?.version ??
|
|
20
21
|
attr.ownerMLDocument.ruleCommonSettings?.ariaVersion ??
|
|
21
22
|
ARIA_RECOMMENDED_VERSION;
|
|
22
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
23
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
23
24
|
const elAriaSpec = getARIA(attr.ownerMLDocument.specs, attr.ownerElement.localName, attr.ownerElement.namespaceURI, ariaVersion, attr.ownerElement.matches.bind(attr.ownerElement));
|
|
24
25
|
// Spec data may set `properties: false` to forbid every aria-* attribute on
|
|
25
26
|
// elements that have no implicit role and accept no explicit role
|
|
@@ -47,7 +48,7 @@ export const checkingElementSupportsAriaProp = ({ attr, role, propSpecs }) => t
|
|
|
47
48
|
// already implemented in the `properties.without` branch below.
|
|
48
49
|
if (elAriaSpec?.properties !== false && elAriaSpec?.properties?.only) {
|
|
49
50
|
const onlyNames = elAriaSpec.properties.only.map(o => (typeof o === 'string' ? o : o.name));
|
|
50
|
-
if (!onlyNames.includes(attr.
|
|
51
|
+
if (!onlyNames.includes(attr.nameKey)) {
|
|
51
52
|
return {
|
|
52
53
|
scope: attr,
|
|
53
54
|
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`), 'disallowed'), t('the "{0*}" {1}', attr.ownerElement.localName, 'element')),
|
|
@@ -56,7 +57,7 @@ export const checkingElementSupportsAriaProp = ({ attr, role, propSpecs }) => t
|
|
|
56
57
|
}
|
|
57
58
|
if (elAriaSpec?.properties !== false && elAriaSpec?.properties?.without) {
|
|
58
59
|
for (const ignore of elAriaSpec.properties.without) {
|
|
59
|
-
if (ignore.name === attr.
|
|
60
|
+
if (ignore.name === attr.nameKey) {
|
|
60
61
|
const hasNativeAttr = ignore.alt?.method === 'set-attr' && attr.ownerElement.hasAttribute(ignore.alt.target);
|
|
61
62
|
return {
|
|
62
63
|
scope: attr,
|
|
@@ -6,7 +6,7 @@ import type { ElementChecker } from '@markuplint/ml-core';
|
|
|
6
6
|
* Including Elements in the Accessibility Tree
|
|
7
7
|
*
|
|
8
8
|
* @see https://w3c.github.io/aria/#tree_inclusion
|
|
9
|
-
* > Elements that are not hidden and
|
|
9
|
+
* > Elements that are not hidden and can fire an accessibility API event, including:
|
|
10
10
|
* > - Elements that are currently focused, even if the element or one of its ancestor elements has its aria-hidden attribute set to true.
|
|
11
11
|
* > - Elements that are a valid target of an aria-activedescendant attribute.
|
|
12
12
|
*
|
|
@@ -5,7 +5,7 @@ import { mayBeFocusable } from '@markuplint/ml-spec';
|
|
|
5
5
|
* Including Elements in the Accessibility Tree
|
|
6
6
|
*
|
|
7
7
|
* @see https://w3c.github.io/aria/#tree_inclusion
|
|
8
|
-
* > Elements that are not hidden and
|
|
8
|
+
* > Elements that are not hidden and can fire an accessibility API event, including:
|
|
9
9
|
* > - Elements that are currently focused, even if the element or one of its ancestor elements has its aria-hidden attribute set to true.
|
|
10
10
|
* > - Elements that are a valid target of an aria-activedescendant attribute.
|
|
11
11
|
*
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @returns A violation if the property is not a global ARIA property.
|
|
10
10
|
*/
|
|
11
11
|
export const checkingNoGlobalProp = ({ attr, propSpecs }) => t => {
|
|
12
|
-
const propSpec = propSpecs.find(prop => prop.name === attr.
|
|
12
|
+
const propSpec = propSpecs.find(prop => prop.name === attr.nameKey);
|
|
13
13
|
if (propSpec && !propSpec.isGlobal) {
|
|
14
14
|
return {
|
|
15
15
|
scope: attr,
|
|
@@ -6,14 +6,14 @@ import type { ElementChecker } from '@markuplint/ml-core';
|
|
|
6
6
|
*
|
|
7
7
|
* Presentational Children
|
|
8
8
|
*
|
|
9
|
-
* @see https://
|
|
9
|
+
* @see https://w3c.github.io/aria/#childrenArePresentational
|
|
10
10
|
* @see https://w3c.github.io/aria/#childrenArePresentational
|
|
11
11
|
*
|
|
12
|
-
* >
|
|
12
|
+
* > Indicates whether DOM descendants are presentational.
|
|
13
13
|
* > user agents SHOULD NOT expose descendants of
|
|
14
14
|
* > this element through the platform accessibility API.
|
|
15
15
|
* > If user agents do not hide the descendant nodes,
|
|
16
|
-
* > some information
|
|
16
|
+
* > some information might be read twice.
|
|
17
17
|
*
|
|
18
18
|
* @see https://w3c.github.io/aria/#tree_exclusion
|
|
19
19
|
*
|
|
@@ -26,7 +26,7 @@ import type { ElementChecker } from '@markuplint/ml-core';
|
|
|
26
26
|
* @see https://w3c.github.io/aria/#tree_inclusion
|
|
27
27
|
*
|
|
28
28
|
* > Text equivalents for hidden referenced objects
|
|
29
|
-
* >
|
|
29
|
+
* > can still be used in the name and description computation
|
|
30
30
|
* > even when not included in the accessibility tree.
|
|
31
31
|
*
|
|
32
32
|
* @param el - The element node to inspect for presentational ancestor context.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAriaAttr } from '../../helpers.js';
|
|
1
2
|
import { ARIA_RECOMMENDED_VERSION, getComputedRole } from '@markuplint/ml-spec';
|
|
2
3
|
/**
|
|
3
4
|
* Checks whether ARIA attributes are applied to descendants of an element whose
|
|
@@ -5,14 +6,14 @@ import { ARIA_RECOMMENDED_VERSION, getComputedRole } from '@markuplint/ml-spec';
|
|
|
5
6
|
*
|
|
6
7
|
* Presentational Children
|
|
7
8
|
*
|
|
8
|
-
* @see https://
|
|
9
|
+
* @see https://w3c.github.io/aria/#childrenArePresentational
|
|
9
10
|
* @see https://w3c.github.io/aria/#childrenArePresentational
|
|
10
11
|
*
|
|
11
|
-
* >
|
|
12
|
+
* > Indicates whether DOM descendants are presentational.
|
|
12
13
|
* > user agents SHOULD NOT expose descendants of
|
|
13
14
|
* > this element through the platform accessibility API.
|
|
14
15
|
* > If user agents do not hide the descendant nodes,
|
|
15
|
-
* > some information
|
|
16
|
+
* > some information might be read twice.
|
|
16
17
|
*
|
|
17
18
|
* @see https://w3c.github.io/aria/#tree_exclusion
|
|
18
19
|
*
|
|
@@ -25,7 +26,7 @@ import { ARIA_RECOMMENDED_VERSION, getComputedRole } from '@markuplint/ml-spec';
|
|
|
25
26
|
* @see https://w3c.github.io/aria/#tree_inclusion
|
|
26
27
|
*
|
|
27
28
|
* > Text equivalents for hidden referenced objects
|
|
28
|
-
* >
|
|
29
|
+
* > can still be used in the name and description computation
|
|
29
30
|
* > even when not included in the accessibility tree.
|
|
30
31
|
*
|
|
31
32
|
* @param el - The element node to inspect for presentational ancestor context.
|
|
@@ -39,7 +40,7 @@ export const checkingPresentationalChildren = ({ el }) => t => {
|
|
|
39
40
|
if (!ancestor.role) {
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
|
-
const hasAriaAttr = [...el.attributes].some(attr =>
|
|
43
|
+
const hasAriaAttr = [...el.attributes].some(attr => isAriaAttr(attr) || attr.nameKey === 'role');
|
|
43
44
|
if (!hasAriaAttr) {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { isAriaAttr } from '../../helpers.js';
|
|
1
2
|
import { ARIA_RECOMMENDED_VERSION, getARIA } from '@markuplint/ml-spec';
|
|
2
3
|
/**
|
|
3
4
|
* ARIA naming attributes subject to the "naming prohibition" constraint
|
|
4
5
|
* defined by ARIA in HTML.
|
|
5
6
|
*
|
|
6
|
-
* @see https://
|
|
7
|
+
* @see https://www.w3.org/TR/html-aria/#dfn-naming-prohibited
|
|
7
8
|
*/
|
|
8
9
|
const NAMING_ATTRS = new Set(['aria-label', 'aria-labelledby', 'aria-braillelabel']);
|
|
9
10
|
/**
|
|
@@ -35,10 +36,10 @@ const NAMING_ATTRS = new Set(['aria-label', 'aria-labelledby', 'aria-braillelabe
|
|
|
35
36
|
* @returns A violation if a naming attribute is used on a naming-prohibited element.
|
|
36
37
|
*/
|
|
37
38
|
export const checkingProhibitedNaming = ({ attr, role, propSpecs }) => t => {
|
|
38
|
-
if (
|
|
39
|
+
if (!isAriaAttr(attr)) {
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
|
-
if (!NAMING_ATTRS.has(attr.
|
|
42
|
+
if (!NAMING_ATTRS.has(attr.nameKey)) {
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
if (role) {
|
|
@@ -56,7 +57,7 @@ export const checkingProhibitedNaming = ({ attr, role, propSpecs }) => t => {
|
|
|
56
57
|
if (elAriaSpec?.namingProhibited !== true && !isAutonomousCustomElement) {
|
|
57
58
|
return;
|
|
58
59
|
}
|
|
59
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
60
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
60
61
|
return {
|
|
61
62
|
scope: attr,
|
|
62
63
|
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`), 'prohibited'), t('the "{0*}" {1}', attr.ownerElement.localName, 'element')),
|
|
@@ -22,20 +22,18 @@ export const checkingRedundantAriaProp = ({ attr, propSpecs, attrSpecs }) => t =
|
|
|
22
22
|
if (attr.isDynamicValue) {
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
25
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
26
26
|
if (!propSpec?.equivalentHtmlAttrs) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
for (const equivalentHtmlAttr of propSpec.equivalentHtmlAttrs) {
|
|
30
30
|
const eligibility = resolveAttrEligibility(equivalentHtmlAttr.htmlAttrName, attr.ownerElement, attrSpecs);
|
|
31
31
|
// Skip only when the native attribute doesn't apply to this element at all
|
|
32
|
-
// (unknown name
|
|
32
|
+
// (unknown name or an unmet condition) — mirrors the old
|
|
33
33
|
// `invalidType === 'non-existent'` check. `noUse` still proceeds: an
|
|
34
34
|
// explicitly-forbidden native attribute can still be "implicit" for
|
|
35
35
|
// equivalence purposes.
|
|
36
|
-
if (eligibility.status === 'unknown' ||
|
|
37
|
-
eligibility.status === 'case-mismatch' ||
|
|
38
|
-
eligibility.status === 'condition-not-met') {
|
|
36
|
+
if (eligibility.status === 'unknown' || eligibility.status === 'condition-not-met') {
|
|
39
37
|
continue;
|
|
40
38
|
}
|
|
41
39
|
if (!attr.ownerElement.hasAttribute(equivalentHtmlAttr.htmlAttrName)) {
|
|
@@ -34,10 +34,7 @@ export const checkingRequiredProp = ({ el, role, propSpecs }) => t => {
|
|
|
34
34
|
return true;
|
|
35
35
|
});
|
|
36
36
|
for (const { name: requiredProp } of requiredProps) {
|
|
37
|
-
const has = el.attributes.some(attr =>
|
|
38
|
-
const attrName = attr.name.toLowerCase();
|
|
39
|
-
return attrName === requiredProp;
|
|
40
|
-
});
|
|
37
|
+
const has = el.attributes.some(attr => attr.nameKey === requiredProp);
|
|
41
38
|
if (!has) {
|
|
42
39
|
const propSpec = propSpecs.find(p => p.name === requiredProp);
|
|
43
40
|
const ariaVersion = el.rule.options?.version ??
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAriaAttr } from '../../helpers.js';
|
|
1
2
|
/**
|
|
2
3
|
* Checks whether an ARIA property or state is supported by the element's
|
|
3
4
|
* computed role, per the role's WAI-ARIA definition (`ownedProperties`).
|
|
@@ -12,17 +13,17 @@
|
|
|
12
13
|
* @returns A violation if the property is not supported by the given role.
|
|
13
14
|
*/
|
|
14
15
|
export const checkingRoleSupportsAriaProp = ({ attr, role, propSpecs }) => t => {
|
|
15
|
-
if (
|
|
16
|
+
if (!isAriaAttr(attr)) {
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
18
19
|
if (!role) {
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
|
-
const statesAndProp = role.ownedProperties.find(p => p.name === attr.
|
|
22
|
+
const statesAndProp = role.ownedProperties.find(p => p.name === attr.nameKey);
|
|
22
23
|
if (statesAndProp) {
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
25
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
26
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
26
27
|
return {
|
|
27
28
|
scope: attr,
|
|
28
29
|
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`), 'disallowed'), t('the "{0*}" {1}', role.name, 'role')),
|
|
@@ -8,8 +8,9 @@ import type { ARIARole } from '@markuplint/ml-spec';
|
|
|
8
8
|
* express it: the tab's `aria-controls` idref list points to a `tabpanel`, or some
|
|
9
9
|
* `tabpanel` element's `aria-labelledby` idref list points back at the tab's own `id`.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* > Authors MUST ensure that if a tab is active, a corresponding tabpanel that represents the active tab is rendered.
|
|
12
|
+
*
|
|
13
|
+
* @see https://w3c.github.io/aria/#tab
|
|
13
14
|
* @param el - The element node to inspect.
|
|
14
15
|
* @param role - The computed ARIA role of the element.
|
|
15
16
|
* @returns A violation if the active tab has no corresponding tabpanel.
|
|
@@ -6,8 +6,9 @@ import { ARIA_RECOMMENDED_VERSION, getComputedRole } from '@markuplint/ml-spec';
|
|
|
6
6
|
* express it: the tab's `aria-controls` idref list points to a `tabpanel`, or some
|
|
7
7
|
* `tabpanel` element's `aria-labelledby` idref list points back at the tab's own `id`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* > Authors MUST ensure that if a tab is active, a corresponding tabpanel that represents the active tab is rendered.
|
|
10
|
+
*
|
|
11
|
+
* @see https://w3c.github.io/aria/#tab
|
|
11
12
|
* @param el - The element node to inspect.
|
|
12
13
|
* @param role - The computed ARIA role of the element.
|
|
13
14
|
* @returns A violation if the active tab has no corresponding tabpanel.
|
|
@@ -26,7 +26,7 @@ export declare const checkingValue: AttrChecker<boolean, Options, {
|
|
|
26
26
|
* Supports token, token list, string, ID reference, true/false, tristate,
|
|
27
27
|
* true/false/undefined, integer, and number value types.
|
|
28
28
|
*
|
|
29
|
-
* @see https://
|
|
29
|
+
* @see https://w3c.github.io/aria/#propcharacteristic_value
|
|
30
30
|
* @param type - The ARIA value type (e.g., `"token"`, `"true/false"`, `"integer"`).
|
|
31
31
|
* @param value - The raw attribute value to validate.
|
|
32
32
|
* @param tokenEnum - The list of allowed token values for token-based types.
|
|
@@ -16,7 +16,7 @@ export const checkingValue = ({ attr, role, propSpecs, booleanish }) => t => {
|
|
|
16
16
|
if (attr.isDynamicValue) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
const propSpec = propSpecs.find(p => p.name === attr.
|
|
19
|
+
const propSpec = propSpecs.find(p => p.name === attr.nameKey);
|
|
20
20
|
const result = checkAria(propSpec, attr.value, role?.name, booleanish);
|
|
21
21
|
if (result.isValid) {
|
|
22
22
|
return;
|
|
@@ -68,7 +68,7 @@ function checkAria(propSpec, currentValue, role, booleanish) {
|
|
|
68
68
|
* Supports token, token list, string, ID reference, true/false, tristate,
|
|
69
69
|
* true/false/undefined, integer, and number value types.
|
|
70
70
|
*
|
|
71
|
-
* @see https://
|
|
71
|
+
* @see https://w3c.github.io/aria/#propcharacteristic_value
|
|
72
72
|
* @param type - The ARIA value type (e.g., `"token"`, `"true/false"`, `"integer"`).
|
|
73
73
|
* @param value - The raw attribute value to validate.
|
|
74
74
|
* @param tokenEnum - The list of allowed token values for token-based types.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/rules",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Built-in rules of markuplint",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"types": "./lib/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
@@ -31,21 +31,21 @@
|
|
|
31
31
|
"./lib/permitted-contents/debug.js": "./lib/permitted-contents/debug.browser.js"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@markuplint/html-spec": "5.
|
|
35
|
-
"@markuplint/ml-config": "5.
|
|
36
|
-
"@markuplint/ml-core": "5.
|
|
37
|
-
"@markuplint/ml-spec": "5.
|
|
38
|
-
"@markuplint/selector": "5.
|
|
39
|
-
"@markuplint/shared": "5.
|
|
40
|
-
"@markuplint/types": "5.
|
|
41
|
-
"@mdn/browser-compat-data": "^
|
|
34
|
+
"@markuplint/html-spec": "5.3.0",
|
|
35
|
+
"@markuplint/ml-config": "5.3.0",
|
|
36
|
+
"@markuplint/ml-core": "5.3.0",
|
|
37
|
+
"@markuplint/ml-spec": "5.3.0",
|
|
38
|
+
"@markuplint/selector": "5.3.0",
|
|
39
|
+
"@markuplint/shared": "5.3.0",
|
|
40
|
+
"@markuplint/types": "5.3.0",
|
|
41
|
+
"@mdn/browser-compat-data": "^8.1.5",
|
|
42
42
|
"@types/debug": "^4.1.13",
|
|
43
43
|
"ansi-colors": "^4.1.3",
|
|
44
|
-
"browserslist": "^4.
|
|
45
|
-
"chrono-node": "^2.10.
|
|
44
|
+
"browserslist": "^4.29.3",
|
|
45
|
+
"chrono-node": "^2.10.3",
|
|
46
46
|
"debug": "^4.4.3",
|
|
47
|
-
"image-size": "^2.0.
|
|
47
|
+
"image-size": "^2.0.4",
|
|
48
48
|
"type-fest": "^5.10.0"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "f587cf0206c8426e57369a48d6d5fb22306eafde"
|
|
51
51
|
}
|