-
-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Core: Significantly improve Storybook's own accessibility #32458
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
Conversation
|
View your CI Pipeline Execution ↗ for commit f9b2a56
☁️ Nx Cloud last updated this comment at |
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 2 | 2 | 0 |
| Self size | 509 KB | 479 KB | 🎉 -31 KB 🎉 |
| Dependency size | 2.97 MB | 2.97 MB | 0 B |
| Bundle Size Analyzer | Link | Link |
@storybook/addon-onboarding
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 0 | 0 | 0 |
| Self size | 333 KB | 368 KB | 🚨 +36 KB 🚨 |
| Dependency size | 670 B | 670 B | 0 B |
| Bundle Size Analyzer | Link | Link |
storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 43 | 44 | 🚨 +1 🚨 |
| Self size | 23.02 MB | 24.16 MB | 🚨 +1.15 MB 🚨 |
| Dependency size | 17.36 MB | 17.39 MB | 🚨 +29 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 124 | 124 | 0 |
| Self size | 3.83 MB | 1.27 MB | 🎉 -2.56 MB 🎉 |
| Dependency size | 21.87 MB | 21.87 MB | 🚨 +18 B 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/cli
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 187 | 188 | 🚨 +1 🚨 |
| Self size | 928 KB | 929 KB | 🚨 +359 B 🚨 |
| Dependency size | 72.96 MB | 74.13 MB | 🚨 +1.17 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/codemod
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 169 | 170 | 🚨 +1 🚨 |
| Self size | 35 KB | 35 KB | 🚨 +42 B 🚨 |
| Dependency size | 69.39 MB | 70.57 MB | 🚨 +1.17 MB 🚨 |
| Bundle Size Analyzer | Link | Link |
create-storybook
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 44 | 45 | 🚨 +1 🚨 |
| Self size | 1.55 MB | 1.55 MB | 0 B |
| Dependency size | 40.38 MB | 41.55 MB | 🚨 +1.17 MB 🚨 |
| Bundle Size Analyzer | node | node |
e940687 to
fa2c259
Compare
This comment was marked as spam.
This comment was marked as spam.
10adeba to
a6cc1c8
Compare
166c0f2 to
d291255
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
code/addons/docs/src/blocks/controls/Files.tsx (1)
8-8: getControlId verified — tests exist; optional sanitization & uniqueness improvements recommendedgetControlId (code/addons/docs/src/blocks/controls/helpers.ts) returns
control-${value.replace(/\s+/g, '-')}and is exercised by helpers.test.ts; it’s used across controls (e.g., Text.tsx, Range.tsx, Files.tsx).
- Issue: only whitespace is replaced — leading/trailing spaces create extra hyphens and other special characters are left untouched (may complicate CSS selectors/querySelector or produce unexpected IDs).
- Recommendation (optional): trim input and slugify/escape unsafe characters (or normalize case); if global uniqueness is required, add scope or a short unique suffix.
code/addons/docs/src/blocks/controls/options/Radio.tsx (1)
48-55: Cursor styling inconsistency with disabled state.While the
cursor: not-allowedstyling for readonly state is good for UX, there's an inconsistency: Line 91 uses thedisabledattribute for the actual input, but the cursor styling only applies to[aria-readonly=true]contexts. Whendisabled=true, browsers automatically applycursor: not-allowed, but the label won't inherit this styling.Consider aligning the cursor behavior for both disabled and readonly states:
'[aria-readonly=true] &': { cursor: 'not-allowed', }, input: { '[aria-readonly=true] &': { cursor: 'not-allowed', }, + '&:disabled': { + cursor: 'not-allowed', + }, margin: 0, marginRight: 6, },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
code/addons/docs/src/blocks/controls/Color.tsx(4 hunks)code/addons/docs/src/blocks/controls/Date.tsx(3 hunks)code/addons/docs/src/blocks/controls/Files.tsx(1 hunks)code/addons/docs/src/blocks/controls/Object.tsx(5 hunks)code/addons/docs/src/blocks/controls/Range.tsx(2 hunks)code/addons/docs/src/blocks/controls/options/Checkbox.tsx(3 hunks)code/addons/docs/src/blocks/controls/options/Radio.tsx(3 hunks)code/addons/docs/src/blocks/controls/options/Select.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- code/addons/docs/src/blocks/controls/Object.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (24)
code/addons/docs/src/blocks/controls/Files.tsx (2)
66-66: Good use of helper function for consistent ID generation.The use of
getControlId(name)ensures consistent ID generation across controls and follows established patterns in the codebase.
69-85: Excellent accessibility improvement with proper labeling.This change significantly improves accessibility by:
- Adding a proper label association between the label and input via
htmlFor={controlId}andid={controlId}- Using
sb-sr-onlyclass to hide the label visually while keeping it accessible to screen readers- Maintaining all existing functionality while enhancing the user experience for assistive technologies
The implementation follows accessibility best practices and aligns with the broader accessibility improvements across the PR.
code/addons/docs/src/blocks/controls/options/Select.tsx (1)
110-112: Solid accessibility improvement using explicit labeling.This change adds a visually-hidden label that properly associates with the select element using
htmlFor={controlId}, which ensures the for attribute exactly matches the id of the form control and provides explicit association. Thesb-sr-onlyclass follows the W3C recommendation for visually hidden labels that remain available to screen readers.The implementation correctly places the label before the select element, ensuring proper reading order for assistive technologies.
code/addons/docs/src/blocks/controls/options/Radio.tsx (4)
12-36: Good accessibility improvement using semantic HTML fieldset!Replacing the
divwith afieldsetelement is a strong accessibility improvement that provides better semantic grouping for radio buttons. The reset styles ensure visual consistency while preserving the semantic benefits.
38-42: LGTM! CSS-driven readonly state management.The CSS-driven approach for handling readonly state is cleaner than runtime prop manipulation and aligns well with the broader ARIA improvements in this PR.
81-82: Excellent screen reader support with visually hidden legend!Adding the visually hidden
<legend>with thesb-sr-onlyclass provides essential context for screen reader users while maintaining the visual design. The conditionalaria-readonlyattribute (using|| undefinedto omit when false) follows best practices.
82-82: sb-sr-only is defined — no change required.
CSS rule present in code/core/src/theming/global.ts ('.sb-sr-only' rule) and injected in code/core/assets/server/base-preview-head.html; class is used across multiple components (e.g., Toolbar, Tool, docs controls).code/addons/docs/src/blocks/controls/options/Checkbox.tsx (3)
12-36: LGTM! Excellent accessibility improvement.The migration from a generic
divto a semanticfieldsetis a significant accessibility enhancement. Thefieldsetandlegendelements should be used when "You have a single multiple choice question (using radio buttons or checkboxes)." This change provides proper semantic grouping for screen readers, which creates "a logical grouping of related form controls, while thelegendserves as a description for that group."The baseline fieldset styles correctly remove browser defaults while maintaining proper layout semantics.
47-58: Improved CSS-based accessibility handling.The transition from dynamic JavaScript-based readonly styling to CSS-based
[aria-readonly=true]selectors is cleaner and more performant. This approach ensures consistent cursor behavior across the component hierarchy without runtime style recalculation.
99-100: Proper ARIA and semantic implementation.The addition of
aria-readonly={readonly || undefined}correctly avoids rendering the attribute when not needed, and the visually-hidden legend provides proper semantic labeling for screen readers. The presence of afieldsetwithout a group label would "typically not be announced to users," so the legend is essential for accessibility.code/addons/docs/src/blocks/controls/Color.tsx (6)
16-20: LGTM! Improved accessibility with proper typing.The change from
aria-readonlystyling to a properly typedreadOnlyprop with transparent opacity handling is a solid improvement. The sr-only class is used to visually hide the information used for screen readers only, and this pattern aligns with modern accessibility best practices.
74-81: Good cursor feedback for readOnly state.The cursor styling appropriately indicates when the input is not editable, providing clear visual feedback to users.
377-378: Clean variable naming and proper control ID generation.The rename from
readonlytoreadOnlyimproves consistency with React conventions, and thecontrolIdgeneration follows the established pattern seen across the codebase.
381-384: Excellent accessibility improvement with screen reader support.Every HTML form control, such as and <textarea>, needs to be labeled accessibly. We need to provide descriptive labels that are also exposed to screen readers. The addition of the screen reader-only label properly associates the label with the input control.
387-387: Proper tooltip disable logic for readOnly state.Setting
trigger={readOnly ? null : undefined}correctly prevents tooltip interaction when the control is in read-only mode, maintaining good UX consistency.
425-432: Complete accessibility wiring with proper input configuration.The input now has proper ID linkage via
controlId, and thereadOnlyprop is correctly passed through. This completes the accessibility improvements for the color control.code/addons/docs/src/blocks/controls/Range.tsx (4)
176-181: Consistent accessibility pattern with proper typing.The wrapper approach mirrors the Color control changes, using a properly typed
readOnlyprop instead ofaria-readonlystyling. This creates consistent behavior across controls.
215-215: Good use of established helper for control ID generation.Using
getControlId(name)maintains consistency with the accessibility patterns being established across the docs controls.
218-221: Proper accessibility labeling follows best practices.Using elements that are correctly associated with form inputs via the for attribute or htmlFor in React. The screen reader-only label ensures proper form control accessibility.
223-230: Complete input configuration with proper prop spreading.The input now has all necessary attributes properly configured: ID linkage, proper prop spreading including
name,min,max,step, event handlers, and a fallback value. This ensures robust form behavior.code/addons/docs/src/blocks/controls/Date.tsx (4)
45-50: LGTM! Proper semantic fieldset with accessibility reset.The transition from a generic wrapper to a semantic
fieldsetelement with appropriate CSS resets (border, marginInline, padding) maintains the visual layout while providing semantic meaning for the grouped date/time inputs.
125-125: LGTM! Screen reader accessible legend.The legend provides essential context to screen readers about what the grouped controls represent, using the
sb-sr-onlyclass to hide it visually while keeping it accessible.
126-128: LGTM! Proper labeling for date and time inputs.The addition of screen-reader-only labels with proper
htmlForattributes ensures that screen readers can identify what each input field represents, improving the overall accessibility of the date control.Also applies to: 139-141
133-134: sb-sr-only is defined and correctly implemented.
Definition found in code/core/src/theming/global.ts (lines 112–122): position:'absolute', width:1, height:1, padding:0, margin:-1, overflow:'hidden', whiteSpace:'nowrap', clip:'rect(0, 0, 0, 0)', clipPath:'inset(50%)', border:0 — matches the expected screen-reader-only pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
code/core/src/components/components/Button/Button.stories.tsx (1)
286-292: AriaDescription story properly uses the new accessibility prop.The story correctly uses
ariaDescription(notdescription) to provide additional context for the button action. This aligns with the Button component's API.
🧹 Nitpick comments (1)
code/core/src/components/components/Button/Button.stories.tsx (1)
217-220: Consider using semantic Button text for the onClick example.While the aria-label is correctly set to false for text buttons, the button text "I am a button using onClick" is unnecessarily verbose and self-referential. Consider using more realistic button text.
- <Button ariaLabel={false} onClick={() => console.log('Hello')}> - I am a button using onClick - </Button> + <Button ariaLabel={false} onClick={() => console.log('Hello')}> + Click me + </Button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
code/core/src/components/components/Button/Button.stories.tsx(6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: normal
- GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (12)
code/core/src/components/components/Button/Button.stories.tsx (12)
7-16: Great addition of the default onClick handler!Adding
fn()as the default onClick handler is a good practice for Storybook stories, as it enables automatic actions logging in the Storybook UI without requiring manual setup in each story.
23-25: LGTM! Good base story setup.The Base story correctly demonstrates the minimal Button usage with
ariaLabel: falsefor text-based buttons that don't need additional labeling.
27-35: Well-documented IconButton story!The IconButton story properly demonstrates the accessibility pattern for icon-only buttons with an appropriate
ariaLabel. The comment clearly explains when this variant should be used.
37-76: Comprehensive variants showcase with proper accessibility patterns.The Variants story effectively demonstrates:
- Text buttons with
ariaLabel={false}(lines 42-61)- Icon-only buttons with proper
ariaLabel="Button"(lines 64-72)This aligns well with the accessibility improvements in the PR.
78-134: Improved pseudo-states testing structure.Good updates to the PseudoStates story:
- Consistent use of
ariaLabel={false}for text buttons- Proper separation of focus-visible state (lines 114-124)
- Correct pseudo selector configuration with
focusVisible(line 131)This will help ensure focus indicators work correctly across different interaction modes.
155-168: Icon-only buttons correctly implement accessibility.The IconOnly story properly sets
ariaLabel: 'Button'for icon-only buttons, ensuring they are accessible to screen reader users.
183-204: Useful padding combinations demonstration.The new Paddings story provides clear examples of how padding interacts with button sizes, which will be helpful for developers implementing the Button component.
271-276: AriaLabel story correctly demonstrates accessible icon button.The story properly shows how to make an icon-only button accessible with an explicit aria-label.
278-284: Tooltip story demonstrates new accessibility feature well.Good example of adding supplementary information via tooltip while keeping the button text as the primary label.
294-300: Shortcut story demonstrates keyboard accessibility feature.Good demonstration of the shortcut prop with a realistic key combination.
302-309: ShortcutAndTooltip combines accessibility features effectively.This story shows how tooltip and shortcut props can work together to provide comprehensive user guidance.
311-317: Verified: default tooltip includes aria-label and shortcut (when shortcuts are enabled)Button computes finalTooltip = tooltip || (ariaLabel !== false ? ariaLabel : undefined), and InteractiveTooltipWrapper joins that tooltip with the human-readable shortcut only if document.body.getAttribute('data-shortcuts-enabled') !== 'false'. See code/core/src/components/components/Button/Button.tsx (finalTooltip at ~L109; wrapper usage ~L112) and code/core/src/components/components/Button/helpers/InteractiveTooltipWrapper.tsx (tooltipLabel logic).
yannbf
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.

The code on this branch is already reviewed in past PRs. The
a11y-consolidationbranch serves as a checkpoint to perform manual tests and limit the day-to-day impact of the a11y project on other project cycles.Feel free to edit this message with checks to perfom / fixes to write.
To do
Sanity checks
Selectonce theAriaToolbarPR is merged too (does it exit the entire toolbar or still go to the sibling)Selectstill announces current value in NVDAWithTooltipto react-aria's overlay stack solves the double tooltip issue onSelecton some Chromatic testsPost-integration tasks
Regressions
Severe issues
code/core/src/use-sync-external-store/shim is still neededSmall cleanups
These are bonus for UI/usability, not regressions or issues.
Summary by CodeRabbit
New Features
Refactor
Accessibility
Documentation
Style
Tests