Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lint rule for inaccessible disabled Button #62080

Merged
merged 26 commits into from
Jun 3, 2024
Merged

Conversation

mirka
Copy link
Member

@mirka mirka commented May 28, 2024

Alternative to #59518
Part of #56547

What?

Adds an eslint error for when the disabled prop is used without the __experimentalIsFocusable prop in the Button component.

Why?

We have long been discussing how to prevent accessibility issues stemming from disabled controls (#56547). Although there are situations where you do want to completely disable a control, we more often find cases where this was not a conscious decision but simply not considered at all. In these cases, we often end up with inaccessible disabled controls, which are not perceivable by screen readers, or cause focus loss after closing a popover.

Next steps

  • Stabilize the __experimentalIsFocusable prop. We should consider renaming it to accessibleWhenDisabled, which is clear and matches the naming in Ariakit. (Button: Stabilize __experimentalIsFocusable prop #62282)
  • Add a accessibleWhenDisabled prop to other relevant components, and add those components to the eslint rule.

How?

Other strategies considered include:

  • Changing the behavior of the disabled prop to be accessible disabled by default. This is not ideal because it goes against the standard behavior of the disabled HTML attribute.
  • Changing the name of the disabled prop to isDisabled, and then make that accessible disabled by default. This is not ideal because it will require a lot of API changes, and the behavioral difference between the two props would be unclear.
  • Adding a blanket lint rule for disabled, and nudge people toward using aria-disabled instead. This is not ideal because using aria-disabled is not necessarily going to address all issues, depending on the element/component.
  • Automatically enable accessible disabled when a control is dynamically disabled while focused. This is not ideal because it isn't foolproof, and also does not address the screen reader perceivability problem.

Testing Instructions

✅ The linter should error in places where a Button has a disabled prop without a __experimentalIsFocusable prop.
✅ The error message should be clear and actionable.

@mirka mirka added [Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). [Type] Code Quality Issues or PRs that relate to code quality labels May 28, 2024
@mirka mirka requested review from afercia, alexstine and a team May 28, 2024 21:00
@mirka
Copy link
Member Author

mirka commented May 28, 2024

When you run npm run lint:js -- --quiet, we can see that there are currently 26 violations of this error.

Is anyone available to do a quick sweep of these and fix or ignore as appropriate? If not, I can go in and ignore them with some kind of TODO comment to fix when possible.

@mirka mirka self-assigned this May 28, 2024
Copy link

github-actions bot commented May 28, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: DaniGuardiola <daniguardiola@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link

github-actions bot commented May 28, 2024

Size Change: +3.06 kB (+0.18%)

Total Size: 1.74 MB

Filename Size Change
build/block-directory/index.min.js 7.3 kB +4 B (+0.05%)
build/block-editor/index.min.js 262 kB +1.88 kB (+0.72%)
build/block-editor/style-rtl.css 15.6 kB +61 B (+0.39%)
build/block-editor/style.css 15.6 kB +56 B (+0.36%)
build/block-library/blocks/html/editor-rtl.css 346 B +10 B (+2.98%)
build/block-library/blocks/html/editor.css 347 B +10 B (+2.97%)
build/block-library/blocks/list/style-rtl.css 102 B +14 B (+15.91%) ⚠️
build/block-library/blocks/list/style.css 102 B +14 B (+15.91%) ⚠️
build/block-library/blocks/media-text/style-rtl.css 507 B +2 B (+0.4%)
build/block-library/blocks/media-text/style.css 505 B +2 B (+0.4%)
build/block-library/blocks/paragraph/style-rtl.css 341 B +6 B (+1.79%)
build/block-library/blocks/paragraph/style.css 341 B +6 B (+1.79%)
build/block-library/editor-rtl.css 12 kB +6 B (+0.05%)
build/block-library/editor.css 12 kB +6 B (+0.05%)
build/block-library/index.min.js 219 kB +497 B (+0.23%)
build/block-library/style-rtl.css 14.6 kB -1 B (-0.01%)
build/block-library/style.css 14.6 kB -1 B (-0.01%)
build/blocks/index.min.js 51.8 kB +67 B (+0.13%)
build/components/index.min.js 222 kB -41 B (-0.02%)
build/core-data/index.min.js 72.5 kB -1 B (0%)
build/edit-post/index.min.js 12.5 kB -2 kB (-13.8%) 👏
build/edit-post/style-rtl.css 2.32 kB -41 B (-1.74%)
build/edit-post/style.css 2.31 kB -42 B (-1.78%)
build/edit-site/index.min.js 208 kB -2.08 kB (-0.99%)
build/edit-site/style-rtl.css 11.9 kB -126 B (-1.05%)
build/edit-site/style.css 11.9 kB -125 B (-1.04%)
build/edit-widgets/index.min.js 17.6 kB +21 B (+0.12%)
build/edit-widgets/style-rtl.css 4.21 kB +24 B (+0.57%)
build/edit-widgets/style.css 4.21 kB +28 B (+0.67%)
build/editor/index.min.js 96.3 kB +3.91 kB (+4.24%)
build/editor/style-rtl.css 9.27 kB +449 B (+5.09%) 🔍
build/editor/style.css 9.28 kB +455 B (+5.16%) 🔍
build/format-library/index.min.js 8.05 kB -31 B (-0.38%)
build/interactivity/debug.min.js 16.5 kB +15 B (+0.09%)
build/interactivity/index.min.js 13.4 kB +22 B (+0.16%)
build/list-reusable-blocks/index.min.js 2.15 kB +15 B (+0.7%)
build/patterns/index.min.js 6.49 kB -19 B (-0.29%)
build/server-side-render/index.min.js 1.96 kB -8 B (-0.41%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 955 B
build/annotations/index.min.js 2.27 kB
build/api-fetch/index.min.js 2.32 kB
build/autop/index.min.js 2.1 kB
build/blob/index.min.js 578 B
build/block-directory/style-rtl.css 1.03 kB
build/block-directory/style.css 1.03 kB
build/block-editor/content-rtl.css 4.58 kB
build/block-editor/content.css 4.57 kB
build/block-editor/default-editor-styles-rtl.css 395 B
build/block-editor/default-editor-styles.css 395 B
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 126 B
build/block-library/blocks/audio/theme.css 126 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/button/editor-rtl.css 307 B
build/block-library/blocks/button/editor.css 307 B
build/block-library/blocks/button/style-rtl.css 539 B
build/block-library/blocks/button/style.css 539 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 421 B
build/block-library/blocks/columns/style.css 421 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 667 B
build/block-library/blocks/cover/editor.css 670 B
build/block-library/blocks/cover/style-rtl.css 1.62 kB
build/block-library/blocks/cover/style.css 1.61 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 86 B
build/block-library/blocks/details/style.css 86 B
build/block-library/blocks/embed/editor-rtl.css 312 B
build/block-library/blocks/embed/editor.css 312 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 126 B
build/block-library/blocks/embed/theme.css 126 B
build/block-library/blocks/file/editor-rtl.css 326 B
build/block-library/blocks/file/editor.css 327 B
build/block-library/blocks/file/style-rtl.css 280 B
build/block-library/blocks/file/style.css 281 B
build/block-library/blocks/file/view.min.js 324 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 227 B
build/block-library/blocks/form-input/editor.css 227 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 340 B
build/block-library/blocks/form-submission-notification/editor.css 340 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 471 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 962 B
build/block-library/blocks/gallery/editor.css 965 B
build/block-library/blocks/gallery/style-rtl.css 1.72 kB
build/block-library/blocks/gallery/style.css 1.72 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 403 B
build/block-library/blocks/group/editor.css 403 B
build/block-library/blocks/group/style-rtl.css 103 B
build/block-library/blocks/group/style.css 103 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/image/editor-rtl.css 891 B
build/block-library/blocks/image/editor.css 891 B
build/block-library/blocks/image/style-rtl.css 1.52 kB
build/block-library/blocks/image/style.css 1.52 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 1.54 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 205 B
build/block-library/blocks/latest-posts/editor.css 205 B
build/block-library/blocks/latest-posts/style-rtl.css 512 B
build/block-library/blocks/latest-posts/style.css 512 B
build/block-library/blocks/media-text/editor-rtl.css 306 B
build/block-library/blocks/media-text/editor.css 305 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 668 B
build/block-library/blocks/navigation-link/editor.css 669 B
build/block-library/blocks/navigation-link/style-rtl.css 193 B
build/block-library/blocks/navigation-link/style.css 192 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 296 B
build/block-library/blocks/navigation-submenu/editor.css 295 B
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.26 kB
build/block-library/blocks/navigation/style.css 2.25 kB
build/block-library/blocks/navigation/view.min.js 1.03 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 377 B
build/block-library/blocks/page-list/editor.css 377 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-content/editor-rtl.css 74 B
build/block-library/blocks/post-content/editor.css 74 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 734 B
build/block-library/blocks/post-featured-image/editor.css 732 B
build/block-library/blocks/post-featured-image/style-rtl.css 342 B
build/block-library/blocks/post-featured-image/style.css 342 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 397 B
build/block-library/blocks/post-template/style.css 396 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 344 B
build/block-library/blocks/pullquote/style.css 343 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/view.min.js 958 B
build/block-library/blocks/quote/style-rtl.css 237 B
build/block-library/blocks/quote/style.css 237 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 140 B
build/block-library/blocks/read-more/style.css 140 B
build/block-library/blocks/rss/editor-rtl.css 101 B
build/block-library/blocks/rss/editor.css 101 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/style-rtl.css 690 B
build/block-library/blocks/search/style.css 689 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 478 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 248 B
build/block-library/blocks/separator/style.css 248 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 286 B
build/block-library/blocks/shortcode/editor.css 286 B
build/block-library/blocks/site-logo/editor-rtl.css 805 B
build/block-library/blocks/site-logo/editor.css 805 B
build/block-library/blocks/site-logo/style-rtl.css 218 B
build/block-library/blocks/site-logo/style.css 218 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 124 B
build/block-library/blocks/site-title/editor.css 124 B
build/block-library/blocks/site-title/style-rtl.css 70 B
build/block-library/blocks/site-title/style.css 70 B
build/block-library/blocks/social-link/editor-rtl.css 335 B
build/block-library/blocks/social-link/editor.css 335 B
build/block-library/blocks/social-links/editor-rtl.css 683 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.51 kB
build/block-library/blocks/social-links/style.css 1.51 kB
build/block-library/blocks/spacer/editor-rtl.css 350 B
build/block-library/blocks/spacer/editor.css 350 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 395 B
build/block-library/blocks/table/editor.css 395 B
build/block-library/blocks/table/style-rtl.css 639 B
build/block-library/blocks/table/style.css 639 B
build/block-library/blocks/table/theme-rtl.css 146 B
build/block-library/blocks/table/theme.css 146 B
build/block-library/blocks/tag-cloud/style-rtl.css 265 B
build/block-library/blocks/tag-cloud/style.css 266 B
build/block-library/blocks/template-part/editor-rtl.css 393 B
build/block-library/blocks/template-part/editor.css 393 B
build/block-library/blocks/template-part/theme-rtl.css 112 B
build/block-library/blocks/template-part/theme.css 112 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 185 B
build/block-library/blocks/video/style.css 185 B
build/block-library/blocks/video/theme-rtl.css 126 B
build/block-library/blocks/video/theme.css 126 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/theme-rtl.css 703 B
build/block-library/theme.css 706 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/commands/index.min.js 15.2 kB
build/commands/style-rtl.css 953 B
build/commands/style.css 951 B
build/components/style-rtl.css 12 kB
build/components/style.css 12 kB
build/compose/index.min.js 12.8 kB
build/core-commands/index.min.js 2.71 kB
build/customize-widgets/index.min.js 10.9 kB
build/customize-widgets/style-rtl.css 1.36 kB
build/customize-widgets/style.css 1.36 kB
build/data-controls/index.min.js 640 B
build/data/index.min.js 9.01 kB
build/date/index.min.js 17.9 kB
build/deprecated/index.min.js 451 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.65 kB
build/edit-post/classic-rtl.css 578 B
build/edit-post/classic.css 578 B
build/element/index.min.js 4.83 kB
build/escape-html/index.min.js 537 B
build/format-library/style-rtl.css 493 B
build/format-library/style.css 492 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 448 B
build/i18n/index.min.js 3.58 kB
build/interactivity/file.min.js 447 B
build/interactivity/image.min.js 1.67 kB
build/interactivity/navigation.min.js 1.17 kB
build/interactivity/query.min.js 740 B
build/interactivity/router.min.js 2.81 kB
build/interactivity/search.min.js 618 B
build/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.31 kB
build/keycodes/index.min.js 1.46 kB
build/list-reusable-blocks/style-rtl.css 851 B
build/list-reusable-blocks/style.css 851 B
build/media-utils/index.min.js 2.92 kB
build/modules/importmap-polyfill.min.js 12.2 kB
build/notices/index.min.js 948 B
build/nux/index.min.js 1.58 kB
build/nux/style-rtl.css 748 B
build/nux/style.css 744 B
build/patterns/style-rtl.css 595 B
build/patterns/style.css 595 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 2.06 kB
build/preferences/index.min.js 2.9 kB
build/preferences/style-rtl.css 719 B
build/preferences/style.css 721 B
build/primitives/index.min.js 831 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 1 kB
build/react-i18n/index.min.js 629 B
build/react-refresh-entry/index.min.js 9.47 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/redux-routine/index.min.js 2.7 kB
build/reusable-blocks/index.min.js 2.72 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 10.1 kB
build/router/index.min.js 1.96 kB
build/shortcode/index.min.js 1.39 kB
build/style-engine/index.min.js 2.02 kB
build/token-list/index.min.js 582 B
build/url/index.min.js 3.74 kB
build/vendors/react-dom.min.js 42.8 kB
build/vendors/react-jsx-runtime.min.js 554 B
build/vendors/react.min.js 2.65 kB
build/viewport/index.min.js 964 B
build/warning/index.min.js 249 B
build/widgets/index.min.js 7.13 kB
build/widgets/style-rtl.css 1.17 kB
build/widgets/style.css 1.17 kB
build/wordcount/index.min.js 1.02 kB

compressed-size-action

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

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

Nice. Haven't approved yet as we still need to deal with the failures.

'error',
{
selector:
'JSXOpeningElement[name.name="Button"]:not(:has(JSXAttribute[name.name="__experimentalIsFocusable"])) JSXAttribute[name.name="disabled"]',
Copy link
Member

Choose a reason for hiding this comment

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

Confirmed that the rule works well 👍

.eslintrc.js Outdated Show resolved Hide resolved
@DaniGuardiola
Copy link
Contributor

FWIW, Ariakit handles a lot of this out of the box. I feel like before doing something like this we should just consider re-writing the internals to use Ariakit, keeping the same API. Then, it should be easier to figure out a strategy to nudge users to keep things accessible. This includes some ideas that I have explored in the past like making buttons with tooltips or binded as popover disclosure triggers automatically accessibleWhenDisabled, which could take care of most, if not all of this issue, while remaining simpler.

@mirka
Copy link
Member Author

mirka commented May 30, 2024

FWIW, Ariakit handles a lot of this out of the box. I feel like before doing something like this we should just consider re-writing the internals to use Ariakit, keeping the same API. Then, it should be easier to figure out a strategy to nudge users to keep things accessible. This includes some ideas that I have explored in the past like making buttons with tooltips or binded as popover disclosure triggers automatically accessibleWhenDisabled, which could take care of most, if not all of this issue, while remaining simpler.

I have suggested this type of automatic mitigation as a preferred first step as well, but apparently there have already been a couple cases where this would not have been sufficient. Your idea of targeting all buttons bound as popover triggers sounds good, and would work in certain integrated components, but probably not all possible instances of a popover trigger.

My feeling now is that these linting and automating strategies are not mutually exclusive, and would complement each other. We should continue to look into automating strategies, since that would benefit all consumers and not just Gutenberg where this lint rule would be in effect.

People are already growing weary of the repeated accessibility issues that this introduces, so I'm hoping we can at least prevent new problematic instances and improve developer awareness, while working on some automated strategies in parallel. If they work well enough, maybe we can remove the lint rule. Does that sound reasonable?

@mirka mirka requested a review from getdave as a code owner May 30, 2024 15:56
@mirka mirka requested a review from ellatrix as a code owner May 30, 2024 18:57
Copy link
Member Author

@mirka mirka left a comment

Choose a reason for hiding this comment

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

I went ahead and looked through all of the errors myself, since I realized it would help me get a better overview of our problems.

For most of them I made a decision based on looking at the code only. Some of them, I looked into the original PR, blames, or actual app UI to gather more context. None of them are tested in app. I believe this is sufficient because technically this shouldn't be introducing "regressions" — there is little damage that adding a __experimentalIsFocusable could do. The only potentially regressive change is #62080 (comment), but the logic looks clear enough to me.

I wrote brief reasoning comments for each change to leave a trail in case somebody needs to blame.

Comment on lines +147 to +150
expect( screen.getByRole( 'button' ) ).toHaveAttribute(
'aria-disabled',
'true'
);
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the recommended way to test for aria-disabled (see testing-library/jest-dom#144 (comment)).

Copy link
Member

Choose a reason for hiding this comment

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

Or maybe we can keep both assertions as they check for different things?

Copy link
Member Author

Choose a reason for hiding this comment

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

That makes a lot of sense, actually. Addressed in bca37a2.

@@ -42,6 +42,7 @@ export default function InstallButton( { attributes, block, clientId } ) {
}
} )
}
__experimentalIsFocusable
Copy link
Member Author

Choose a reason for hiding this comment

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

Clearly a busy state, which can cause focus loss.

Comment on lines 63 to 64
// TODO: Investigate whether this button should be accessible when disabled.
// eslint-disable-next-line no-restricted-syntax
Copy link
Member Author

Choose a reason for hiding this comment

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

This was the only instance I didn't feel confident enough to make a decision without deeper investigation. This is a publicly exported component with both a tabIndex and disabled prop exposed. (But no usages of these props within the Gutenberg repo.)

Copy link
Member

Choose a reason for hiding this comment

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

If I did the archeology correctly, it was added here: #9815

It can now be seen when inserting buttons in the Buttons block:

Screenshot 2024-05-31 at 14 03 26

Judging by the functionality and the initial intent, my expectation is that this disabled was left there unintentionally and there's no reason for it to be there in the first place. I expect that the inserter appender will not be visible at all, rather than stay visible and disabled. With that in mind, I don't think it needs to be accessible if it ends up disabled.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you so much for investigating! I added a disable reason in d16d2b4.

@@ -149,6 +149,7 @@ export default function LinkPreview( {
isEmptyURL || showIconLabels ? '' : ': ' + value.url
) }
ref={ ref }
__experimentalIsFocusable
Copy link
Member Author

Choose a reason for hiding this comment

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

Alleviates confusion, since this is a "standard" button usually present in the view.

Comment on lines 224 to 236
aria-disabled={ isFirstItem }
// Disable reason: Truly disable when image is not selected.
// eslint-disable-next-line no-restricted-syntax
disabled={ ! isSelected }
/>
<Button
icon={ chevronRight }
onClick={ isLastItem ? undefined : onMoveForward }
label={ __( 'Move image forward' ) }
aria-disabled={ isLastItem }
// Disable reason: Truly disable when image is not selected.
// eslint-disable-next-line no-restricted-syntax
disabled={ ! isSelected }
Copy link
Member Author

Choose a reason for hiding this comment

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

I was wondering whether a component having both disabled and aria-disabled props was something to flag with another lint, but this I think is a legitimate use case. (Though the same logic can also be expressed with a combination of the disabled and the __experimentalIsFocusable prop.)

@@ -43,6 +43,7 @@ export default function RenameModal( { menuTitle, onClose, onSave } ) {

<Button
__next40pxDefaultSize
__experimentalIsFocusable
Copy link
Member Author

Choose a reason for hiding this comment

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

Should be perceivable to signal that the user input is invalid.

@@ -168,6 +168,7 @@ export default function PostPreviewButton( {
className={ className || 'editor-post-preview' }
href={ href }
target={ targetId }
__experimentalIsFocusable
Copy link
Member Author

Choose a reason for hiding this comment

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

Should be perceivable, because the button is expected to be there.

@@ -93,6 +93,7 @@ export class PostPublishPanel extends Component {
</div>
<div className="editor-post-publish-panel__header-cancel-button">
<Button
__experimentalIsFocusable
Copy link
Member Author

Choose a reason for hiding this comment

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

Can cause focus loss due to a dynamic disable.

@@ -86,6 +86,7 @@ function ImportForm( { instanceId, onUpload } ) {
<Button
type="submit"
isBusy={ isLoading }
__experimentalIsFocusable
Copy link
Member Author

Choose a reason for hiding this comment

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

Should be perceivable, and can cause focus loss.

Copy link
Member Author

Choose a reason for hiding this comment

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

Accessible disabled matches the behavior of the undo/redo buttons in the actual WP block editor.

@mirka
Copy link
Member Author

mirka commented May 30, 2024

My feeling now is that these linting and automating strategies are not mutually exclusive, and would complement each other.

After going through all these errors manually, I am now more convinced that we should be approaching this with a combination of automated mitigation and human decision-making (prompted by lints). Automated mitigation can prevent a baseline number of accessibility issues, but a human making context-aware design decisions for each instance can further enhance the UX for screen reader users.

@WordPress WordPress deleted a comment from mirka May 31, 2024
@@ -219,7 +220,6 @@ function RevisionsButtons( {
</p>
) : (
<Button
disabled={ areStylesEqual }
Copy link
Member

Choose a reason for hiding this comment

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

Confirmed that this can and should be removed because areStylesEqual will never be true since it's already false based on the ternary logic this is nested in.

Copy link
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

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

Thanks for taking the time to audit and fix them one by one! 👍

I spent some time going through them all with your comments and I think they all make sense. I've left a few comments where there was doubt or uncertainty.

I think it can be shipped - do you think we should ask folks from #59518 for some feedback just in case?

@mirka
Copy link
Member Author

mirka commented Jun 3, 2024

I think it can be shipped - do you think we should ask folks from #59518 for some feedback just in case?

Thanks for the review, I appreciate it 🙏

Since this touches many files, I'll go ahead and merge today. It should be easy enough to revert or tweak, whether in full or in part.

@mirka mirka merged commit 0bfd56b into trunk Jun 3, 2024
63 checks passed
@mirka mirka deleted the eslint-focusable-disabled branch June 3, 2024 20:20
@github-actions github-actions bot added this to the Gutenberg 18.6 milestone Jun 3, 2024
@afercia
Copy link
Contributor

afercia commented Jun 4, 2024

Thanks everyone for all your effort ❤️ A linting rule seems the best approach to prevent such a11y issues and, more importantly, to improve knowledge and education.
If needed, all the cases investigated here can be tweaked but it's very good there's now a solid base to catch violations.

carstingaxion pushed a commit to carstingaxion/gutenberg that referenced this pull request Jun 4, 2024
* Add lint rule for inaccessible disabled `Button`

* Exclude react native files

* Include files in root `storybook` folder

* Fix in Storybook editor playground (matches actual behavior)

* Fix in install block button (is clearly a busy state)

* Ignore in gallery image reordering buttons

* Fix in LinkControl copy link button (aleviates confusion)

* Ignore in edit-site pagination buttons (not confusing, and useful)

* Fix in enable custom fields (is clearly a busy state)

* Fix in DataViews list view

Empty action menu should still be perceivable to aleviate confusion, and does not clutter tab order due to Composite use.

* Fix in DataViews CompactItemActions (is dropdown trigger)

* Fix in template part title modal

disabled and aria-disabled are set with an identical condition, which doesn't make sense but signals the intent to keep it focusable.

* Fix in PageList block (should be perceivable, esp. because the description is always there)

* Fix in ConvertToLinksModal button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site "Apply globally" button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site nav menu rename modal (should be perceivable to signal that input is invalid)

* Fix in RevisionsButtons (button is never visible when `areStylesEqual`)

* Fix in RevisionsButtons (contains important info and should be perceivable)

* Fix in GlobalStylesSidebar (should be perceivable)

* Fix in PostPublishPanel cancel button (can cause focus loss)

* Fix in PostPreviewButton (should be perceivable)

* Defer decision in ButtonBlockAppender

* Fix in reusable blocks import form (should be perceivable, can cause focus loss)

* Adapt test for PostPreviewButton

* Improve rigidity of accessible disabled detection in test

* Add disable reason for ButtonBlockAppender

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: DaniGuardiola <daniguardiola@git.wordpress.org>
@ellatrix
Copy link
Member

This is a bug fix no? Looks like a bunch of buttons were made more accessible.

@ellatrix ellatrix added [Type] Bug An existing feature does not function as intended Backport to WP 6.6 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta and removed [Type] Code Quality Issues or PRs that relate to code quality labels Jun 10, 2024
ellatrix pushed a commit that referenced this pull request Jun 11, 2024
* Add lint rule for inaccessible disabled `Button`

* Exclude react native files

* Include files in root `storybook` folder

* Fix in Storybook editor playground (matches actual behavior)

* Fix in install block button (is clearly a busy state)

* Ignore in gallery image reordering buttons

* Fix in LinkControl copy link button (aleviates confusion)

* Ignore in edit-site pagination buttons (not confusing, and useful)

* Fix in enable custom fields (is clearly a busy state)

* Fix in DataViews list view

Empty action menu should still be perceivable to aleviate confusion, and does not clutter tab order due to Composite use.

* Fix in DataViews CompactItemActions (is dropdown trigger)

* Fix in template part title modal

disabled and aria-disabled are set with an identical condition, which doesn't make sense but signals the intent to keep it focusable.

* Fix in PageList block (should be perceivable, esp. because the description is always there)

* Fix in ConvertToLinksModal button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site "Apply globally" button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site nav menu rename modal (should be perceivable to signal that input is invalid)

* Fix in RevisionsButtons (button is never visible when `areStylesEqual`)

* Fix in RevisionsButtons (contains important info and should be perceivable)

* Fix in GlobalStylesSidebar (should be perceivable)

* Fix in PostPublishPanel cancel button (can cause focus loss)

* Fix in PostPreviewButton (should be perceivable)

* Defer decision in ButtonBlockAppender

* Fix in reusable blocks import form (should be perceivable, can cause focus loss)

* Adapt test for PostPreviewButton

* Improve rigidity of accessible disabled detection in test

* Add disable reason for ButtonBlockAppender

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: DaniGuardiola <daniguardiola@git.wordpress.org>
ellatrix pushed a commit that referenced this pull request Jun 11, 2024
* Add lint rule for inaccessible disabled `Button`

* Exclude react native files

* Include files in root `storybook` folder

* Fix in Storybook editor playground (matches actual behavior)

* Fix in install block button (is clearly a busy state)

* Ignore in gallery image reordering buttons

* Fix in LinkControl copy link button (aleviates confusion)

* Ignore in edit-site pagination buttons (not confusing, and useful)

* Fix in enable custom fields (is clearly a busy state)

* Fix in DataViews list view

Empty action menu should still be perceivable to aleviate confusion, and does not clutter tab order due to Composite use.

* Fix in DataViews CompactItemActions (is dropdown trigger)

* Fix in template part title modal

disabled and aria-disabled are set with an identical condition, which doesn't make sense but signals the intent to keep it focusable.

* Fix in PageList block (should be perceivable, esp. because the description is always there)

* Fix in ConvertToLinksModal button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site "Apply globally" button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site nav menu rename modal (should be perceivable to signal that input is invalid)

* Fix in RevisionsButtons (button is never visible when `areStylesEqual`)

* Fix in RevisionsButtons (contains important info and should be perceivable)

* Fix in GlobalStylesSidebar (should be perceivable)

* Fix in PostPublishPanel cancel button (can cause focus loss)

* Fix in PostPreviewButton (should be perceivable)

* Defer decision in ButtonBlockAppender

* Fix in reusable blocks import form (should be perceivable, can cause focus loss)

* Adapt test for PostPreviewButton

* Improve rigidity of accessible disabled detection in test

* Add disable reason for ButtonBlockAppender

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: DaniGuardiola <daniguardiola@git.wordpress.org>
patil-vipul pushed a commit to patil-vipul/gutenberg that referenced this pull request Jun 17, 2024
* Add lint rule for inaccessible disabled `Button`

* Exclude react native files

* Include files in root `storybook` folder

* Fix in Storybook editor playground (matches actual behavior)

* Fix in install block button (is clearly a busy state)

* Ignore in gallery image reordering buttons

* Fix in LinkControl copy link button (aleviates confusion)

* Ignore in edit-site pagination buttons (not confusing, and useful)

* Fix in enable custom fields (is clearly a busy state)

* Fix in DataViews list view

Empty action menu should still be perceivable to aleviate confusion, and does not clutter tab order due to Composite use.

* Fix in DataViews CompactItemActions (is dropdown trigger)

* Fix in template part title modal

disabled and aria-disabled are set with an identical condition, which doesn't make sense but signals the intent to keep it focusable.

* Fix in PageList block (should be perceivable, esp. because the description is always there)

* Fix in ConvertToLinksModal button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site "Apply globally" button (should be perceivable, and doesn't clutter tab order)

* Fix in edit-site nav menu rename modal (should be perceivable to signal that input is invalid)

* Fix in RevisionsButtons (button is never visible when `areStylesEqual`)

* Fix in RevisionsButtons (contains important info and should be perceivable)

* Fix in GlobalStylesSidebar (should be perceivable)

* Fix in PostPublishPanel cancel button (can cause focus loss)

* Fix in PostPreviewButton (should be perceivable)

* Defer decision in ButtonBlockAppender

* Fix in reusable blocks import form (should be perceivable, can cause focus loss)

* Adapt test for PostPreviewButton

* Improve rigidity of accessible disabled detection in test

* Add disable reason for ButtonBlockAppender

Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: DaniGuardiola <daniguardiola@git.wordpress.org>
@ellatrix ellatrix removed the Backport to WP 6.6 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Jun 18, 2024
@ellatrix
Copy link
Member

This was cherry-picked to the wp/6.6 branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants