|
|
|
@ -33,7 +33,7 @@ export default function a11y(
|
|
|
|
|
if (name.startsWith('aria-')) {
|
|
|
|
|
if (invisibleElements.has(node.name)) {
|
|
|
|
|
// aria-unsupported-elements
|
|
|
|
|
validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute.start);
|
|
|
|
|
validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const type = name.slice(5);
|
|
|
|
@ -42,7 +42,7 @@ export default function a11y(
|
|
|
|
|
let message = `A11y: Unknown aria attribute 'aria-${type}'`;
|
|
|
|
|
if (match) message += ` (did you mean '${match}'?)`;
|
|
|
|
|
|
|
|
|
|
validator.warn(message, attribute.start);
|
|
|
|
|
validator.warn(message, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -50,7 +50,7 @@ export default function a11y(
|
|
|
|
|
if (name === 'role') {
|
|
|
|
|
if (invisibleElements.has(node.name)) {
|
|
|
|
|
// aria-unsupported-elements
|
|
|
|
|
validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute.start);
|
|
|
|
|
validator.warn(`A11y: <${node.name}> should not have role attribute`, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const value = getStaticAttributeValue(node, 'role');
|
|
|
|
@ -59,30 +59,30 @@ export default function a11y(
|
|
|
|
|
let message = `A11y: Unknown role '${value}'`;
|
|
|
|
|
if (match) message += ` (did you mean '${match}'?)`;
|
|
|
|
|
|
|
|
|
|
validator.warn(message, attribute.start);
|
|
|
|
|
validator.warn(message, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no-access-key
|
|
|
|
|
if (name === 'accesskey') {
|
|
|
|
|
validator.warn(`A11y: Avoid using accesskey`, attribute.start);
|
|
|
|
|
validator.warn(`A11y: Avoid using accesskey`, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no-autofocus
|
|
|
|
|
if (name === 'autofocus') {
|
|
|
|
|
validator.warn(`A11y: Avoid using autofocus`, attribute.start);
|
|
|
|
|
validator.warn(`A11y: Avoid using autofocus`, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scope
|
|
|
|
|
if (name === 'scope' && node.name !== 'th') {
|
|
|
|
|
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, attribute.start);
|
|
|
|
|
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// tabindex-no-positive
|
|
|
|
|
if (name === 'tabindex') {
|
|
|
|
|
const value = getStaticAttributeValue(node, 'tabindex');
|
|
|
|
|
if (!isNaN(value) && +value > 0) {
|
|
|
|
|
validator.warn(`A11y: avoid tabindex values above zero`, attribute.start);
|
|
|
|
|
validator.warn(`A11y: avoid tabindex values above zero`, { start: attribute.start, end: attribute.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -96,13 +96,13 @@ export default function a11y(
|
|
|
|
|
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` :
|
|
|
|
|
attributes[0];
|
|
|
|
|
|
|
|
|
|
validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node.start);
|
|
|
|
|
validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, { start: node.start, end: node.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldHaveContent() {
|
|
|
|
|
if (node.children.length === 0) {
|
|
|
|
|
validator.warn(`A11y: <${node.name}> element should have child content`, node.start);
|
|
|
|
|
validator.warn(`A11y: <${node.name}> element should have child content`, { start: node.start, end: node.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -110,7 +110,7 @@ export default function a11y(
|
|
|
|
|
const href = attributeMap.get(attribute);
|
|
|
|
|
const value = getStaticAttributeValue(node, attribute);
|
|
|
|
|
if (value === '' || value === '#') {
|
|
|
|
|
validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href.start);
|
|
|
|
|
validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, { start: href.start, end: href.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -122,7 +122,7 @@ export default function a11y(
|
|
|
|
|
// anchor-in-svg-is-valid
|
|
|
|
|
shouldHaveValidHref('xlink:href')
|
|
|
|
|
} else {
|
|
|
|
|
validator.warn(`A11y: <a> element should have an href attribute`, node.start);
|
|
|
|
|
validator.warn(`A11y: <a> element should have an href attribute`, { start: node.start, end: node.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// anchor-has-content
|
|
|
|
@ -141,7 +141,8 @@ export default function a11y(
|
|
|
|
|
shouldHaveContent();
|
|
|
|
|
|
|
|
|
|
if (attributeMap.has('aria-hidden')) {
|
|
|
|
|
validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden').start);
|
|
|
|
|
const attr = attributeMap.get('aria-hidden');
|
|
|
|
|
validator.warn(`A11y: <${node.name}> element should not be hidden`, { start: attr.start, end: attr.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -157,14 +158,14 @@ export default function a11y(
|
|
|
|
|
|
|
|
|
|
// no-distracting-elements
|
|
|
|
|
if (node.name === 'marquee' || node.name === 'blink') {
|
|
|
|
|
validator.warn(`A11y: Avoid <${node.name}> elements`, node.start);
|
|
|
|
|
validator.warn(`A11y: Avoid <${node.name}> elements`, { start: node.start, end: node.end });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node.name === 'figcaption') {
|
|
|
|
|
const parent = elementStack[elementStack.length - 1];
|
|
|
|
|
if (parent) {
|
|
|
|
|
if (parent.name !== 'figure') {
|
|
|
|
|
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, node.start);
|
|
|
|
|
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, { start: node.start, end: node.end });
|
|
|
|
|
} else {
|
|
|
|
|
const children = parent.children.filter(node => {
|
|
|
|
|
if (node.type === 'Comment') return false;
|
|
|
|
@ -175,7 +176,7 @@ export default function a11y(
|
|
|
|
|
const index = children.indexOf(node);
|
|
|
|
|
|
|
|
|
|
if (index !== 0 && index !== children.length - 1) {
|
|
|
|
|
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, node.start);
|
|
|
|
|
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, { start: node.start, end: node.end });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|