Skip to content

microsoft/eslint-plugin-fluentui-jsx-a11y

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

eslint-plugin-fluentui-jsx-a11y npm versionCoverage Status

A set of eslint rules against Fluent UI React v9 to prevent common accessibility issues.

Covers hard-coded string props and variables for props.

<Label htmlFor="some-id">Some Label</Label>
<Input id="some-id" />
<Label htmlFor={someId}>Some Label</Label>
<Input id={someId} />

Installation

You'll first need to install ESLint:

# npm
npm install eslint --save-dev

# yarn
yarn add eslint --dev

Next, install @microsoft/eslint-plugin-fluentui-jsx-a11y:

# npm
npm install @microsoft/eslint-plugin-fluentui-jsx-a11y --save-dev

# yarn
yarn add @microsoft/eslint-plugin-fluentui-jsx-a11y --dev

Or add this package to your package.json file:

"devDependencies": {
    "@microsoft/eslint-plugin-fluentui-jsx-a11y": "1.0.0"
  }

And then you can run

  npm install

Usage

You will need to add the plugin to your .eslintrc configuration file.

Suggested Configuration:

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "plugins": [
    "@typescript-eslint",
    "react-hooks",
    "@microsoft/fluentui-jsx-a11y"
  ],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@microsoft/fluentui-jsx-a11y/recommended"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "react/prop-types": "off",
  },

Why?

This plugin does a static code analysis of the React JSX to spot accessibility issues in apps built with FluentUI. That way, common accessibility issues are detected before the pull request stage and will be prevented from being checked into a code base.

As the plugin can only catch errors in static source code, please use it in combination with @axe-core/react to test the accessibility of the rendered DOM. Consider these tools just as one step of a larger a11y testing process and always test your apps with assistive technology.

Architecture & Development

Rule Factory System

This plugin leverages a powerful rule factory system that provides consistent behavior across accessibility rules. The factory system includes several utility functions for validating accessible labeling:

  • hasAssociatedLabelViaAriaLabelledBy - Validates aria-labelledby references
  • hasAssociatedLabelViaHtmlFor - Validates htmlFor/id label associations
  • hasAssociatedLabelViaAriaDescribedby - Validates aria-describedby references
  • hasLabeledChild - Detects accessible child content (images with alt, icons, labeled elements)
  • hasTextContentChild - Validates text content in child elements
  • isInsideLabelTag - Checks if element is wrapped in a label

Labeled Child Detection

The hasLabeledChild utility is particularly powerful, detecting multiple forms of accessible child content:

// Image elements with alt text
<Button><img alt="Save document" /></Button>

// SVG elements with accessible attributes  
<Button><svg title="Close" /></Button>
<Button><svg aria-label="Menu" /></Button>

// Elements with role="img" and labeling
<Button><span role="img" aria-label="Celebration">πŸŽ‰</span></Button>

// FluentUI Icon components
<Button><SaveIcon /></Button>
<Button><Icon iconName="Save" /></Button>

// Any element with aria-label or title
<Button><div aria-label="Status indicator" /></Button>

// Elements with aria-labelledby (validates references exist)
<Button><span aria-labelledby="save-label" /></Button>
<Label id="save-label">Save Document</Label>

The utility performs source code analysis to validate that aria-labelledby references point to actual elements with matching IDs, ensuring robust accessibility validation.

Creating New Rules

To create a new accessibility rule, use the rule factory system:

import { ruleFactory, LabeledControlConfig } from '../util/ruleFactory';

const rule = ruleFactory({
  component: 'YourComponent', // or /RegexPattern/
  message: 'Your component needs accessible labeling',
  allowTextContentChild: true,
  allowLabeledChild: true,
  allowHtmlFor: true,
  allowLabelledBy: true,
  labelProps: ['aria-label'],
  requiredProps: ['role']
});

See CONTRIBUTING.md for detailed development guidelines.

Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.

Rules

πŸ’Ό Configurations enabled in.
⚠️ Configurations set to warn in.
βœ… Set in the recommended configuration.
πŸ”§ Automatically fixable by the --fix CLI option.

NameΒ Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β  Description πŸ’Ό ⚠️ πŸ”§
accordion-header-needs-labelling The accordion header is a button and it needs an accessibile name e.g. text content, aria-label, aria-labelledby. βœ…
accordion-item-needs-header-and-panel An AccordionItem needs exactly one header and one panel βœ…
avatar-needs-name Accessibility: Avatar must have an accessible labelling: name, aria-label, aria-labelledby βœ…
avoid-using-aria-describedby-for-primary-labelling aria-describedby provides additional context and is not meant for primary labeling. βœ…
badge-needs-accessible-name βœ… πŸ”§
breadcrumb-needs-labelling All interactive elements must have an accessible name βœ…
card-needs-accessible-name Accessibility: Interactive Card must have an accessible name via aria-label, aria-labelledby, etc. βœ…
checkbox-needs-labelling Accessibility: Checkbox without label must have an accessible and visual label: aria-labelledby βœ…
colorswatch-needs-labelling Accessibility: ColorSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. βœ…
combobox-needs-labelling All interactive elements must have an accessible name βœ…
compound-button-needs-labelling Accessibility: Compound buttons must have accessible labelling: title, aria-label, aria-labelledby, aria-describedby βœ…
counter-badge-needs-count βœ… πŸ”§
datagrid-needs-labelling Accessibility: DataGrid must have proper labelling and follow ARIA grid patterns for complex data tables βœ…
dialogbody-needs-title-content-and-actions A DialogBody should have a header(DialogTitle), content(DialogContent), and footer(DialogActions) βœ…
dialogsurface-needs-aria DialogueSurface need accessible labelling: aria-describedby on DialogueSurface and aria-label or aria-labelledby(if DialogueTitle is missing) βœ…
dropdown-needs-labelling Accessibility: Dropdown menu must have an id and it needs to be linked via htmlFor of a Label βœ…
emptyswatch-needs-labelling Accessibility: EmptySwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. βœ…
field-needs-labelling Accessibility: Field must have label βœ…
image-button-missing-aria Accessibility: Image buttons must have accessible labelling: title, aria-label, aria-labelledby, aria-describedby βœ…
image-needs-alt Accessibility: Image must have alt attribute with a meaningful description of the image. If the image is decorative, use alt="". βœ…
imageswatch-needs-labelling Accessibility: ImageSwatch must have an accessible name via aria-label, Tooltip, aria-labelledby, etc.. βœ…
infolabel-needs-labelling Accessibility: InfoLabel must have an accessible name via aria-label, text content, aria-labelledby, etc. βœ…
input-components-require-accessible-name Accessibility: Input fields must have accessible labelling: aria-label, aria-labelledby or an associated label βœ…
link-missing-labelling Accessibility: Image links must have an accessible name. Add either text content, labelling to the image or labelling to the link itself. βœ… πŸ”§
menu-button-needs-labelling Accessibility: MenuButton must have an accessible name via aria-label, text content, aria-labelledby, etc. βœ…
menu-item-needs-labelling Accessibility: MenuItem without label must have an accessible and visual label: aria-labelledby βœ…
no-empty-buttons Accessibility: Button, ToggleButton, SplitButton, MenuButton, CompoundButton must either text content or icon or child component βœ…
no-empty-components FluentUI components should not be empty βœ…
prefer-aria-over-title-attribute The title attribute is not consistently read by screen readers, and its behavior can vary depending on the screen reader and the user's settings. βœ… πŸ”§
prefer-disabledfocusable-over-disabled Prefer 'disabledFocusable' over 'disabled' when component has loading state to maintain keyboard navigation accessibility βœ… πŸ”§
progressbar-needs-labelling Accessibility: Progressbar must have aria-valuemin, aria-valuemax, aria-valuenow, aria-describedby and either aria-label or aria-labelledby attributes βœ…
radio-button-missing-label Accessibility: Radio button without label must have an accessible and visual label: aria-labelledby βœ…
radiogroup-missing-label Accessibility: RadioGroup without label must have an accessible and visual label: aria-labelledby βœ…
rating-needs-name Accessibility: Ratings must have accessible labelling: name, aria-label, aria-labelledby or itemLabel which generates aria-label βœ…
spin-button-needs-labelling Accessibility: SpinButtons must have an accessible label βœ…
spin-button-unrecommended-labelling Accessibility: Unrecommended accessibility labelling - SpinButton βœ…
spinner-needs-labelling Accessibility: Spinner must have either aria-label or label, aria-live and aria-busy attributes βœ…
switch-needs-labelling Accessibility: Switch must have an accessible label βœ…
table-needs-labelling Accessibility: Table must have proper labelling and semantic structure for screen readers βœ…
tablist-and-tabs-need-labelling This rule aims to ensure that Tabs with icons but no text labels have an accessible name and that Tablist is properly labeled. βœ…
tag-dismissible-needs-labelling This rule aims to ensure that dismissible Tag components have proper accessibility labelling: either aria-label on dismissIcon or aria-label on Tag with role on dismissIcon βœ…
tag-needs-name Accessibility: Tag must have an accessible name βœ…
toolbar-missing-aria Accessibility: Toolbars need accessible labelling: aria-label or aria-labelledby βœ…
tooltip-not-recommended Accessibility: Prefer text content or aria over a tooltip for these components MenuItem, SpinButton βœ…
tree-needs-labelling Accessibility: Tree must have proper labelling and follow ARIA tree pattern for hierarchical navigation βœ…
visual-label-better-than-aria-suggestion Visual label is better than an aria-label because sighted users can't read the aria-label text. βœ…

About

ESLint rules for accessibility against FluentUI v9 components.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors 20