Skip to content

feat(core): add eslint-plugin-jsx-a11y and fix accessibility violations#930

Open
jensblond wants to merge 10 commits intomainfrom
feat/a11y-linting
Open

feat(core): add eslint-plugin-jsx-a11y and fix accessibility violations#930
jensblond wants to merge 10 commits intomainfrom
feat/a11y-linting

Conversation

@jensblond
Copy link

  • Add eslint-plugin-jsx-a11y with recommended rules to ESLint config
  • Fix cat-select: add tabIndex, aria-expanded, aria-controls, role="option", keyboard handlers
  • Fix cat-tag: add aria-expanded and aria-controls to combobox input
  • Fix cat-radio: remove incorrect role="radio" from label element
  • Fix cat-date-inline: remove redundant onClick from label (htmlFor handles focus)
  • Fix cat-input: add role="presentation" to wrapper div
  • Fix cat-i18n-registry: remove unused variable in catch block

This enables static a11y linting as part of the standard lint workflow.

ref={el => (this.trigger = el)}
id={this.id}
role="combobox"
tabIndex={0}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that tabIndex is assigned programmatically in onClick and in onKeyDown, I'm not sure if this don't break the current implementation, if you are not sure - easier to add eslint-disable

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - removed tabIndex={0} and added ESLint disable comment instead.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not removed

- Add eslint-plugin-jsx-a11y with recommended rules to ESLint config
- Fix cat-select: add tabIndex, aria-expanded, aria-controls, role="option", keyboard handlers
- Fix cat-tag: add aria-expanded and aria-controls to combobox input
- Fix cat-radio: remove incorrect role="radio" from label element
- Fix cat-date-inline: remove redundant onClick from label (htmlFor handles focus)
- Fix cat-input: add role="presentation" to wrapper div
- Fix cat-i18n-registry: remove unused variable in catch block

This enables static a11y linting as part of the standard lint workflow.
- Add axe-core dependency for runtime a11y checks
- Create reusable checkA11y() helper that injects axe-core into Stencil E2E pages
- Add formatViolations() helper for readable test output
- Disable document-level rules by default (title, lang, landmarks) for component testing
- Add a11y test examples to cat-button component

Usage:
  import { checkA11y, formatViolations } from '../../utils/a11y-test';
  const violations = await checkA11y(page);
  expect(violations).toEqual([]);
- cat-input: replace role="presentation" with ESLint disable comment
- cat-select: remove redundant tabIndex, onKeyDown handlers, and duplicate ARIA attributes
- cat-select: use i18n for 'Options' fallback instead of hardcoded string
- cat-tag: remove incorrect combobox role and associated ARIA attributes
- cat-menu: Change <nav role="menu"> to <div role="menu"> (nav is
  non-interactive landmark, menu is interactive role)
- cat-select: Add tabIndex back to combobox for focusability
- cat-select: Add eslint disable for option click handler
- cat-menu.spec: Update selectors from nav to div for role="menu"
- cat-tag.spec: Remove role="combobox" and aria-controls from expected HTML
@jensblond jensblond requested a review from glushkova91 March 11, 2026 15:12
@@ -109,55 +109,16 @@ importers:
version: 5.9.3
vitest:
specifier: ^4.0.16
version: 4.0.18(@types/node@12.20.55)(@vitest/browser-playwright@4.0.18)(@vitest/ui@4.0.16)(jiti@2.6.1)(jsdom@27.4.0)(less@4.6.2)(sass-embedded@1.98.0)(sass@1.97.1)

angular/dist/catalyst:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

angular/dist/catalyst should not be deleted

"babel-loader": "9.2.1",
"eslint": "9.39.2",
"eslint-config-prettier": "9.1.2",
"eslint-plugin-jsx-a11y": "^6.10.0",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we agreed to fix versions for security reasons (same for axe-core)

<div class={{ 'label-container': true, 'label-hidden': this.labelHidden }}>
{(this.hasSlottedLabel || this.label) && (
<label id={`${this.id}-label`} htmlFor={this.id} part="label" onClick={() => this.doFocus()}>
<label id={`${this.id}-label`} htmlFor={this.id} part="label">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we don't have a real case for cat-date-inline with shown label, but we do, in search-ui, and by clicking on label the today day receives focus. That changes were intentional and we should not remove focus on click. Let's just disable the eslint rule

</div>
<div class={{ 'input-color': this.type === 'color', 'input-container': true }}>
<div class="input-outer-wrapper">
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -- click focuses the input, keyboard users can focus it directly */}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a warning
379:14 warning Unused eslint-disable directive (no problems were reported from 'jsx-a11y/click-events-have-key-events' or 'jsx-a11y/no-static-element-interactions')

ref={el => (this.trigger = el)}
id={this.id}
role="combobox"
tabIndex={0}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not removed

- Pin exact versions for axe-core and eslint-plugin-jsx-a11y
- Restore onClick handler on cat-date-inline label with ESLint disable
- Update ESLint disable comment in cat-input for clarity
- Regenerate pnpm-lock.yaml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants