-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #453 from github/add-a11y-no-title-attribute
Create rule: a11y-no-title-attribute
- Loading branch information
Showing
8 changed files
with
177 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Guards against developers using the title attribute (`github/a11y-no-title-attribute`) | ||
|
||
💼 This rule is enabled in the ⚛️ `react` config. | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
The title attribute is strongly discouraged. The only exception is on an `<iframe>` element. It is hardly useful and cannot be accessed by multiple groups of users including keyboard-only users and mobile users. | ||
|
||
The `title` attribute is commonly set on links, matching the link text. This is redundant and unnecessary so it can be simply be removed. | ||
|
||
If you are considering the `title` attribute to provide supplementary description, consider whether the text in question can be persisted in the design. Alternatively, if it's important to display supplementary text that is hidden by default, consider using an accessible tooltip implementation that uses the aria-labelledby or aria-describedby semantics. Even so, proceed with caution: tooltips should only be used on interactive elements like links or buttons. See [Tooltip alternatives](https://primer.style/design/guides/accessibility/tooltip-alternatives) for more accessible alternatives. | ||
|
||
### Should I use the title attribute to provide an accessible name for an <svg>? | ||
|
||
Use a <title> element instead of the title attribute, or an aria-label. | ||
|
||
## Rule Details | ||
|
||
👎 Examples of **incorrect** code for this rule: | ||
|
||
```jsx | ||
<a src="https://www.github.com" title="A home for all developers"> | ||
GitHub | ||
</a> | ||
``` | ||
|
||
```jsx | ||
<a href="/" title="github.com"> | ||
GitHub | ||
</a> | ||
``` | ||
|
||
```jsx | ||
<span src="https://www.github.com" title="supercalifragilisticexpialidocious"> | ||
supercali... | ||
</span> | ||
``` | ||
|
||
👍 Examples of **correct** code for this rule: | ||
|
||
```jsx | ||
<iframe src="https://www.github.com" title="Github"></iframe> | ||
``` | ||
|
||
## Version |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
const {getProp, getPropValue} = require('jsx-ast-utils') | ||
const {getElementType} = require('../utils/get-element-type') | ||
|
||
const SEMANTIC_ELEMENTS = [ | ||
'a', | ||
'button', | ||
'summary', | ||
'select', | ||
'option', | ||
'textarea', | ||
'input', | ||
'span', | ||
'div', | ||
'p', | ||
'h1', | ||
'h2', | ||
'h3', | ||
'h4', | ||
'h5', | ||
'h6', | ||
'details', | ||
'summary', | ||
'dialog', | ||
'tr', | ||
'th', | ||
'td', | ||
'label', | ||
] | ||
|
||
const ifSemanticElement = (context, node) => { | ||
const elementType = getElementType(context, node.openingElement, true) | ||
|
||
for (const semanticElement of SEMANTIC_ELEMENTS) { | ||
if (elementType === semanticElement) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: 'Guards against developers using the title attribute', | ||
url: require('../url')(module), | ||
}, | ||
schema: [], | ||
}, | ||
|
||
create(context) { | ||
return { | ||
JSXElement: node => { | ||
const elementType = getElementType(context, node.openingElement) | ||
if (elementType !== `iframe` && ifSemanticElement(context, node)) { | ||
const titleProp = getPropValue(getProp(node.openingElement.attributes, `title`)) | ||
if (titleProp) { | ||
context.report({ | ||
node, | ||
message: 'The title attribute is not accessible and should never be used unless for an `<iframe>`.', | ||
}) | ||
} | ||
} | ||
}, | ||
} | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
const rule = require('../lib/rules/a11y-no-title-attribute') | ||
const RuleTester = require('eslint').RuleTester | ||
|
||
const ruleTester = new RuleTester({ | ||
parserOptions: { | ||
ecmaVersion: 'latest', | ||
sourceType: 'module', | ||
ecmaFeatures: { | ||
jsx: true, | ||
}, | ||
}, | ||
}) | ||
|
||
const errorMessage = 'The title attribute is not accessible and should never be used unless for an `<iframe>`.' | ||
|
||
ruleTester.run('a11y-no-title-attribute', rule, { | ||
valid: [ | ||
{code: '<button>Submit</button>'}, | ||
{code: '<iframe title="an allowed title">GitHub</iframe>'}, | ||
{code: '<span>some information</span>'}, | ||
{code: '<a href="github.com">GitHub</a>'}, | ||
{ | ||
code: '<Component title="some title">Submit</Component>', | ||
settings: { | ||
github: { | ||
components: { | ||
Component: 'iframe', | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
// Note: we are only checking semantic elements. We cannot make assumptions about how a React Components is using the title prop. | ||
code: '<Link title="some title">Submit</Link>', | ||
settings: { | ||
github: { | ||
components: { | ||
Link: 'a', | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
invalid: [ | ||
{code: '<a title="some title" href="github.com">GitHub</a>', errors: [{message: errorMessage}]}, | ||
{code: '<span><button title="some title">submit</button></span>', errors: [{message: errorMessage}]}, | ||
{ | ||
code: '<Component as="a" title="some title">Submit</Component>', | ||
errors: [{message: errorMessage}], | ||
settings: { | ||
github: { | ||
components: { | ||
Component: 'iframe', | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}) |