Skip to content

Conversation

@Sidnioulz
Copy link
Member

@Sidnioulz Sidnioulz commented Sep 15, 2025

The code on this branch is already reviewed in past PRs. The a11y-consolidation branch 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

Post-integration tasks

  • Clean up focus states on all popover roots / listboxes, e.g. tagsfilter, to prevent disgracious outlines
  • Move addon-jest code to new repo

Regressions

Severe issues

  • Check if code/core/src/use-sync-external-store/ shim is still needed
  • Stabilise public component exports that use react-stately and bundle react-stately again

Small cleanups

These are bonus for UI/usability, not regressions or issues.

  • Add kb-only tooltips to toolbar/tabbar advocating for <- and -> nav
  • Visual tests: Remove overflow-y auto and height 100% on visual test addon
  • Interactions: fix vertical positioning of default state
  • Designs: improve vertical positioning of default state

Summary by CodeRabbit

  • New Features

    • New Select and ToggleButton controls, Popover/PopoverProvider, TooltipProvider, revamped Modal and TabsView, plus many new component stories.
  • Refactor

    • Replaced numerous tooltip flows with popovers or Selects; consolidated icon-only actions into accessible Buttons/ToggleButtons; reorganized toolbars, panels, and preview controls.
  • Accessibility

    • Standardized ariaLabel/ariaDescription/shortcut handling, improved ARIA semantics, keyboard navigation, and focus behavior.
  • Documentation

    • Expanded migration guidance and accessibility notes for v10.0.0 → 10.1.0.
  • Style

    • Updated theme palette, form, and typography tokens.
  • Tests

    • Stories and e2e tests adopt ARIA selectors and more robust interaction patterns.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 15, 2025

Fails
🚫 PR is marked with "BREAKING CHANGE" label.
🚫

Please choose only one of these labels: ["BREAKING CHANGE","maintenance"]

Generated by 🚫 dangerJS against f9b2a56

@Sidnioulz Sidnioulz changed the title [A11y] a11y consolidation branch - for CI - do not merge UI: A11y consolidation branch - for CI - do not merge Sep 15, 2025
@nx-cloud
Copy link

nx-cloud bot commented Sep 15, 2025

View your CI Pipeline Execution ↗ for commit f9b2a56

Command Status Duration Result
nx run-many -t build --parallel=3 ✅ Succeeded 50s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-11 17:54:55 UTC

@storybook-app-bot
Copy link

storybook-app-bot bot commented Sep 15, 2025

Package Benchmarks

Commit: f9b2a56, ran on 11 November 2025 at 17:47:57 UTC

The following packages have significant changes to their size or dependencies:

@storybook/addon-a11y

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

@coderabbitai

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

@Sidnioulz Sidnioulz force-pushed the a11y-consolidation branch 2 times, most recently from 10adeba to a6cc1c8 Compare September 18, 2025 16:55
coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 recommended

getControlId (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-allowed styling for readonly state is good for UX, there's an inconsistency: Line 91 uses the disabled attribute for the actual input, but the cursor styling only applies to [aria-readonly=true] contexts. When disabled=true, browsers automatically apply cursor: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 66b0a4c and 6a65ac4.

📒 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} and id={controlId}
  • Using sb-sr-only class 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. The sb-sr-only class 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 div with a fieldset element 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 the sb-sr-only class provides essential context for screen reader users while maintaining the visual design. The conditional aria-readonly attribute (using || undefined to 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 div to a semantic fieldset is a significant accessibility enhancement. The fieldset and legend elements 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 the legend serves 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 a fieldset without 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-readonly styling to a properly typed readOnly prop 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 readonly to readOnly improves consistency with React conventions, and the controlId generation 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 the readOnly prop 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 readOnly prop instead of aria-readonly styling. 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 fieldset element 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-only class 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 htmlFor attributes 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (not description) 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a65ac4 and f5bef34.

📒 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: false for 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).

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

@ndelangen ndelangen self-assigned this Nov 6, 2025
coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

coderabbitai[bot]

This comment was marked as spam.

Copy link
Member

@yannbf yannbf left a comment

Choose a reason for hiding this comment

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

@yannbf yannbf merged commit 81c5086 into next Nov 12, 2025
111 of 113 checks passed
@yannbf yannbf deleted the a11y-consolidation branch November 12, 2025 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BREAKING CHANGE ci:daily Run the CI jobs that normally run in the daily job. maintenance User-facing maintenance tasks needs triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants