Skip to content

UI: Improve landmark navigation#33457

Merged
ndelangen merged 8 commits into
nextfrom
sidnioulz/issue-32271
Jan 13, 2026
Merged

UI: Improve landmark navigation#33457
ndelangen merged 8 commits into
nextfrom
sidnioulz/issue-32271

Conversation

@Sidnioulz
Copy link
Copy Markdown
Member

@Sidnioulz Sidnioulz commented Jan 2, 2026

Closes #32271

What I did

Peek.2026-01-09.19-27.webm
  • Aligned site structure with feedback from accessibility experts who audited our recent a11y changes
  • Applied landmarks to
  • Sidebar (header element, banner role, was previously a nav)
  • Search (search role)
  • Explorer (nav role)
  • Preview (main role)
  • Toolbar (region role)
  • Addon panel (aside role)
  • In mobile, applied header/banner to the MobileNavigation bottom bar
  • Added global keyboard shortcuts for landmark nav (F6 / Shift+F6) based on React Aria, so that everyone can keyboard nav like a pro
  • Added focus outline specific to landmark regions so they can be spotted (reviewed with @MichaelArestad)
  • Fixed focus outline visibility on preview region
  • Fix issue with nav being unreachable when Search is focused
  • Fix preview iframe eating up F6 key events (can't find a way)

Still gotta handle a few focus / keyboard handling edge cases.

Checklist for Contributors

Testing

Not tested

Manual testing

With screen reader
  1. Run Storybook with NVDA or VoiceOver on
  2. Use your screen reader's landmark navigation shortcuts (NVDA: D and Shift+D, when in browse mode)
  3. NVDA will show a decorative outline (NVDA baked-in feature), VoiceOver won't
Without screen reader
  1. Run Storybook in your favourite browser
  2. Press F6 and Shift+F6 to quick nav between landmarks, using React Aria's hook
  3. Notice the highlight that helps you spot in which region you are
  4. Notice the quick nav remembers which element of the landmark region you were last on, and returns to that element
  5. This quick nav also does not suffer from limitations between browse mode and focus mode in screen readers

Limitations

  • Can't quick nav from within the preview iframe with React Aria shortcut
  • Can't change the keyboard shortcut, it is hardcoded by React Aria, so we can't show it in the shortcuts UI for it to be customised
  • Quicknav feature is undocumented at the moment

Documentation

@kylegach @jonniebigodes @MichaelArestad I'd like to get your opinions on how to make this feature discoverable to users. Should we add it in the shortcuts page, even though it can't be edited, just for visibility? Is there a best page to document it in the docs website?

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

  • New Features

    • Keyboard landmark navigation (F6 / Shift+F6) with temporary visual border highlighting; new shortcut labels and defaults added.
    • Card accepts custom outline attributes.
    • Semantic landmarks added across sidebar, toolbar, preview, panel, mobile nav, and widgets to improve accessibility.
  • Bug Fixes

    • Skip link label changed from "Skip to canvas" to "Skip to content" for clarity.
  • Style

    • Global styles added to support landmark focus indicators.
  • Documentation

    • Getting-started guide updated to document landmark navigation and sidebar/preview changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 6, 2026

View your CI Pipeline Execution ↗ for commit 276e724

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 10m 6s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-12 18:29:27 UTC

@Sidnioulz Sidnioulz force-pushed the sidnioulz/issue-32271 branch 2 times, most recently from aaf875d to e37e586 Compare January 9, 2026 17:49
@Sidnioulz Sidnioulz changed the title WIP LANDMARKS UI: Improve landmark navigation Jan 9, 2026
@Sidnioulz Sidnioulz added feature request accessibility ci:normal a11y: keyboard Accessibility issues related to keyboard navigation or shortcuts labels Jan 9, 2026
@Sidnioulz Sidnioulz marked this pull request as ready for review January 9, 2026 18:35
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 9, 2026

📝 Walkthrough

Walkthrough

Adds landmark-based ARIA support and keyboard landmark navigation. Adds dependency @react-aria/landmark, new hooks useLandmark and useLandmarkIndicator, wires landmark props/refs across manager UI (sidebar, panel, preview, toolbar, mobile nav, testing widget), updates shortcuts and styles, and adds Card.outlineAttrs.

Changes

Cohort / File(s) Change Summary
Dependency
package.json
Added @react-aria/landmark ^3.0.8 to dependencies and devDependencies.
Landmark hooks
src/manager/hooks/useLandmark.ts, src/manager/components/layout/useLandmarkIndicator.ts
New useLandmark wrapper that returns landmarkProps with data-sb-landmark, and useLandmarkIndicator that listens for F6/Shift+F6, finds nearest landmark, and runs a 1.5s ::after outline animation using theme primary color.
Layout
src/manager/components/layout/Layout.tsx
Installs useLandmarkIndicator() in Layout to enable global landmark indicator listener.
Sidebar group
src/manager/components/sidebar/*, src/manager/components/sidebar/types.ts, stories under sidebar/*
Integrated useLandmark across Sidebar, Explorer, Search, Tree, TestingWidget; added landmarkProps/refs, semantic element changes (nav/header/aside), ExplorerProps gains className? and isHidden: boolean; replaced isBrowsing with isNavVisible/isNavReachable/isSearchResultRendered. Adjusted story skip-link text and IconSymbols usage in stories.
Panel & Mobile nav
src/manager/components/panel/Panel.tsx, src/manager/components/mobile/navigation/MobileNavigation.tsx
Switched containers to semantic aside/section/header, added landmark refs and spread landmarkProps onto containers.
Preview & Toolbar
src/manager/components/preview/Preview.tsx, src/manager/components/preview/FramesRenderer.tsx, src/manager/components/preview/Toolbar.tsx, src/manager/components/preview/utils/components.ts
Added useLandmark to Preview and Toolbar (dynamic landmarkProps/ref), swapped some styled element types (main/section/div) for landmarking, and removed title from skip-to-sidebar link.
Card
src/components/components/Card/Card.tsx
Added outlineAttrs?: DOMAttributes<HTMLDivElement> prop and forwards it to CardOutline.
Shortcuts & settings
src/manager-api/modules/shortcuts.ts, src/manager/settings/*, src/manager/settings/defaultShortcuts.tsx
Added goToPreviousLandmark/goToNextLandmark to API and default shortcuts; updated shortcuts UI/state shape to support hardcoded shortcuts and render read-only inputs with tooltip for them.
Theming / Global styles
src/theming/global.ts
Added CSS rules for [data-sb-landmark] and its ::after pseudo-element to render a focus-visible outline without layout shift.
Forms / Misc styles
src/components/components/Form/styles.ts
Extended disabled selector to include aria-disabled="true" and added cursor: not-allowed.
Docs
docs/get-started/browse-stories.mdx
Updated section title/content and added callout describing F6/Shift+F6 landmark navigation.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Listener as useLandmarkIndicator
    participant DOM as Document/DOM
    participant Theme as Theme System
    participant AnimAPI as Web Animations API

    User->>Listener: Presses F6 / Shift+F6
    Listener->>DOM: Capture keydown, get activeElement
    Listener->>DOM: Traverse ancestors to find [data-sb-landmark]
    alt landmark found
        Listener->>AnimAPI: Cancel existing animation on landmark (if any)
        Listener->>Theme: Read theme.color.primary
        Theme-->>Listener: Return primary color
        Listener->>AnimAPI: Start ::after border highlight (2px, 1.5s)
        AnimAPI->>DOM: Render highlight on landmark ::after
        AnimAPI->>Listener: On finish, clear animation state
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
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: 2

🤖 Fix all issues with AI agents
In @code/core/src/manager/components/sidebar/Explorer.tsx:
- Line 27: Update the ref type for containerRef to match the rendered element:
change useRef<HTMLDivElement>(null) to useRef<HTMLElement | null>(null)
(referencing the containerRef variable in Explorer.tsx) so the ref can point to
the <nav> element and satisfy type safety wherever containerRef.current is used.

In @code/core/src/manager/components/sidebar/TestingWidget.tsx:
- Around line 257-264: The hooks cardRef = useRef<HTMLDivElement>(null) and
const { landmarkProps } = useLandmark(...) are being invoked after an early
return, violating the Rules of Hooks; move those hook calls (cardRef and
useLandmark invocation that provides landmarkProps) to execute unconditionally
before any early return or conditional logic in the TestingWidget component so
they run in the same order on every render, then use landmarkProps and cardRef
where needed after the early-return check.
🧹 Nitpick comments (5)
code/core/src/manager/components/sidebar/Heading.stories.tsx (1)

237-240: Text update correctly reflects the new skip link label.

The change from "Skip to canvas" to "Skip to content" aligns with the PR's accessibility improvements. The play function will correctly focus the updated skip link elements for Chromatic testing.

Optional: Consider renaming the story export for consistency.

The story is named SkipToCanvasLinkFocused (line 229) but now targets "Skip to content" text. For clarity, you might rename it to SkipToContentLinkFocused, though story names primarily serve as identifiers and the current name is still functional.

code/core/src/components/components/Card/Card.tsx (1)

100-107: Consider placing ref after outlineProps spread.

While DOMAttributes doesn't include ref (TypeScript prevents passing it), React convention places ref last to prevent accidental override and provide defense in depth.

♻️ Suggested prop order
-      <CardOutline animation={outlineAnimation} color={outlineColor} ref={ref} {...outlineProps}>
+      <CardOutline animation={outlineAnimation} color={outlineColor} {...outlineProps} ref={ref}>
code/core/src/manager/components/preview/FramesRenderer.tsx (1)

99-103: Removing the anchor title here is fine; consider dropping redundant tabIndex={0}.

Since the <a> has an href, it’s already tabbable; keeping tabIndex={0} is harmless but unnecessary.

code/core/src/manager/components/layout/useLandmarkIndicator.ts (1)

37-43: Consider browser compatibility for the Animation API.

The Web Animations API is well-supported in modern browsers but may fail in older environments. Consider adding a try-catch or feature detection for maximum robustness.

♻️ Optional defensive coding enhancement
-         const animation = landmarkElement.animate(
-           [{ border: `2px solid ${theme.color.primary}` }, { border: `2px solid transparent` }],
-           {
-             duration: 1500,
-             pseudoElement: '::after',
-           }
-         );
+         try {
+           const animation = landmarkElement.animate(
+             [{ border: `2px solid ${theme.color.primary}` }, { border: `2px solid transparent` }],
+             {
+               duration: 1500,
+               pseudoElement: '::after',
+             }
+           );
 
-         currentAnimationRef.current = animation;
+           currentAnimationRef.current = animation;
 
-         animation.onfinish = () => {
-           currentAnimationRef.current = null;
-         };
+           animation.onfinish = () => {
+             currentAnimationRef.current = null;
+           };
+         } catch (e) {
+           // Animation API not supported or failed, silently continue
+         }
code/core/src/manager/components/sidebar/Search.tsx (1)

316-322: Consider adding an explicit label to the search landmark.

The search landmark lacks an aria-label or aria-labelledby attribute. While the input field has an associated label, the landmark region itself would benefit from explicit labeling for better accessibility.

💡 Suggested enhancement
  const searchLandmarkRef = useRef<HTMLDivElement>(null);
  const { landmarkProps } = useLandmark(
    {
+     'aria-label': 'Search components',
      role: 'search',
    },
    searchLandmarkRef
  );
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9511a9c and 7feac0b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (22)
  • code/core/package.json
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/theming/global.ts
💤 Files with no reviewable changes (1)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/package.json
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/theming/global.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/package.json
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/theming/global.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/theming/global.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/theming/global.ts
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/theming/global.ts
🧠 Learnings (13)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/package.json
  • code/core/src/theming/global.ts
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/theming/global.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/panel/Panel.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/panel/Panel.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
📚 Learning: 2025-10-01T15:24:01.060Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.

Applied to files:

  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/package.json
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{renderers}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/client-logger` for client-side logging in browser code

Applied to files:

  • code/core/src/manager/components/sidebar/Tree.stories.tsx
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code

Applied to files:

  • code/core/src/manager/components/sidebar/Tree.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required dependencies that the test subject uses

Applied to files:

  • code/core/src/manager/components/sidebar/Tree.stories.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/manager/components/sidebar/Tree.stories.tsx
🧬 Code graph analysis (13)
code/core/src/manager/components/sidebar/TestingWidget.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/sidebar/Explorer.tsx (1)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/components/components/Card/Card.tsx (1)
code/core/src/components/index.ts (1)
  • Card (46-46)
code/core/src/manager/components/sidebar/Sidebar.tsx (4)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/sidebar/Explorer.tsx (1)
  • Explorer (19-72)
code/core/src/manager/components/sidebar/SearchResults.tsx (1)
  • SearchResults (224-345)
code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/sidebar/Tree.stories.tsx (1)
code/core/src/manager-api/root.tsx (1)
  • ManagerContext (79-79)
code/core/src/manager/components/preview/Toolbar.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/sidebar/Search.tsx (1)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/preview/Preview.tsx (1)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/components/panel/Panel.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/layout/useLandmarkIndicator.ts (2)
code/core/src/theming/index.ts (1)
  • useTheme (18-18)
code/core/src/preview-api/index.ts (2)
  • useRef (13-13)
  • useEffect (8-8)
code/core/src/manager/components/layout/Layout.tsx (1)
code/core/src/manager/components/layout/useLandmarkIndicator.ts (1)
  • useLandmarkIndicator (9-59)
code/core/src/theming/global.ts (1)
code/core/src/theming/base.ts (1)
  • color (1-42)
🪛 Biome (2.1.2)
code/core/src/manager/components/sidebar/TestingWidget.tsx

[error] 257-257: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 258-258: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ 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). (4)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: nx
  • GitHub Check: nx
🔇 Additional comments (28)
code/core/src/components/components/Card/Card.tsx (2)

1-1: LGTM: Import addition is appropriate.

The DOMAttributes import is necessary for typing the new outlineProps and is correctly imported from React.


95-95: LGTM: Type definition is appropriate.

The outlineProps type correctly uses DOMAttributes<HTMLDivElement> to accept standard DOM attributes, ARIA attributes, and event handlers for the outline element.

code/core/src/manager/components/sidebar/Heading.tsx (1)

90-96: Skip link text change looks good and consistent with the new landmark framing.

ariaLabel={false} is the right choice here since the anchor text provides the accessible name (as per learnings).

code/core/src/manager/components/sidebar/Tree.stories.tsx (2)

15-17: Story decorator update to include IconSymbols matches the component-level change.

This keeps the Tree stories self-contained now that Tree no longer injects the SVG symbols.

Also applies to: 79-85


277-285: Updated play assertion for “Skip to content” matches the new label.

code/core/src/manager/components/sidebar/Tree.tsx (2)

304-306: “Skip to content” label updates are consistent across node types.

Also: using ariaLabel={false} is correct since the link text is the accessible name (as per learnings).

Also applies to: 445-447, 503-505


753-755: No action needed — IconSymbols is properly mounted in the parent Sidebar component.

The verification confirms that <IconSymbols /> is rendered at line 156 of Sidebar.tsx, which is the parent component of Tree. This global mount ensures that symbol-based icons (via <UseSymbol />) remain functional across all child components, including those that depend on it like TreeNode, ContextMenu, and SearchResults. The refactoring correctly moved the mount to the appropriate parent level.

code/core/package.json (1)

228-235: No action needed. @react-aria/landmark is correctly placed in devDependencies because it is bundled into the manager output, following Storybook's established pattern for bundled UI component libraries (similar to open and fast-printf). All @react-aria/* packages are grouped in devDependencies and are not listed in the esbuild global externals configuration, confirming they are bundled rather than externalized.

code/core/src/theming/global.ts (1)

137-155: LGTM! Landmark focus styling implementation is sound.

The landmark focus indication using ::after pseudo-element is well-executed:

  • Avoids layout shift by using an overlay approach
  • Properly isolates pointer events
  • Uses theme primary color for brand consistency

Per the PR objectives, ensure this styling has been tested in high-contrast mode to verify the focus outline remains visible.

code/core/src/manager/components/sidebar/types.ts (1)

54-66: LGTM! Improved state granularity for accessibility.

The replacement of isBrowsing with three distinct flags provides clearer semantics and enables better accessibility control. The inline documentation effectively explains each flag's purpose and the distinction between visibility and DOM presence is crucial for maintaining keyboard navigation (per PR objectives).

code/core/src/manager/components/layout/Layout.tsx (1)

162-163: LGTM! Landmark indicator integration is correctly placed.

The useLandmarkIndicator() hook is appropriately called in the root Layout component, which serves as the parent container for all landmarks. The hook's implementation (per code snippets) properly manages the F6 keydown listener lifecycle with cleanup, and the animation mechanism prevents concurrent highlights.

The PR acknowledges the known limitation where the preview iframe can intercept F6 events, preventing navigation from within rendered stories—an acceptable trade-off given iframe constraints.

code/core/src/manager/components/preview/utils/components.ts (1)

5-5: LGTM! Landmark element restructuring is semantically correct.

Moving the main landmark from PreviewContainer to FrameWrap properly aligns semantic roles with actual content:

  • PreviewContainer as div: appropriate for layout wrapper
  • FrameWrap as main: correct for the primary preview content region

This change aligns with the PR objective of marking Preview as the main landmark. The useLandmark hook properly integrates the landmark with React Aria, registering the region with appropriate ARIA attributes (aria-labelledby and role='main').

Verification confirms only one <main> landmark exists in the manager components (the FrameWrap in Preview), with no conflicting landmarks across the codebase.

code/core/src/manager/hooks/useLandmark.ts (1)

1-22: LGTM! Clean wrapper implementation.

The hook properly wraps React Aria's useLandmark and augments it with the Storybook-specific data-sb-landmark attribute that enables the focus styling defined in global.ts. The implementation is minimal, type-safe, and actively used across eight manager components (Preview, Toolbar, Panel, Sidebar, Search, Explorer, TestingWidget, and MobileNavigation) with consistent patterns.

code/core/src/manager/components/preview/Preview.tsx (2)

115-119: LGTM! Landmark integration correctly implemented.

The landmark hook is properly configured with role='main' and references the visually-hidden heading, aligning with the PR objectives to mark the Preview as the main landmark region.


138-138: LGTM! Landmark props correctly applied.

The ref and landmarkProps are properly spread onto the FrameWrap element, enabling both landmark navigation and visual highlighting.

code/core/src/manager/components/preview/Toolbar.tsx (2)

87-94: LGTM! Landmark integration correctly implemented.

The landmark hook properly configures the toolbar as a region with appropriate labeling via the visually-hidden heading.


101-102: LGTM! Landmark props correctly applied.

The ref and landmarkProps are properly spread onto the StyledSection, enabling landmark navigation and visual highlighting.

code/core/src/manager/components/panel/Panel.tsx (2)

156-160: Verify ARIA role matches semantic intent.

The <aside> element has an implicit ARIA role of complementary, but the code explicitly sets role='region', overriding this. The PR objectives specify marking the "Addon panel as aside", suggesting the complementary role should be used.

Consider using role='complementary' to align with the semantic <aside> element, or omit the role attribute entirely to use the implicit role:

♻️ Proposed fix
  const { landmarkProps } = useLandmark(
-   { 'aria-labelledby': 'storybook-panel-heading', role: 'region' },
+   { 'aria-labelledby': 'storybook-panel-heading', role: 'complementary' },
    asideRef
  );

Alternatively, if the React Aria landmark hook properly handles implicit roles:

  const { landmarkProps } = useLandmark(
-   { 'aria-labelledby': 'storybook-panel-heading', role: 'region' },
+   { 'aria-labelledby': 'storybook-panel-heading' },
    asideRef
  );

163-163: LGTM! Landmark props correctly applied.

The ref and landmarkProps are properly spread onto the Aside element, assuming the role mismatch is addressed.

code/core/src/manager/components/layout/useLandmarkIndicator.ts (1)

9-58: LGTM! Landmark indicator implementation is sound.

The F6 keydown handler correctly traverses the DOM to locate landmark regions and applies a visual border animation. The cleanup and dependency on theme.color.primary are appropriate.

Note: The PR objectives mention a known limitation where the preview iframe intercepts F6 events, preventing landmark navigation from within the iframe. This is an expected constraint.

code/core/src/manager/components/sidebar/TestingWidget.tsx (1)

274-279: LGTM! Landmark props correctly applied.

The ref and outlineProps with landmarkProps are properly applied to the HoverCard, and the visually-hidden heading provides appropriate labeling. This will work correctly once the hook placement issue is resolved.

code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (2)

81-88: LGTM: Landmark integration correctly implemented.

The banner landmark is properly configured with aria-labelledby referencing the "Navigation controls" heading. The ref type matches the header element.


145-174: LGTM: Semantic element updates align with landmark strategy.

Changing Container to styled.section and MobileBottomBar to styled.header provides the appropriate semantic structure for the banner landmark.

code/core/src/manager/components/sidebar/Explorer.tsx (1)

37-56: LGTM: Navigation landmark properly configured.

The landmark is correctly labeled via the "Stories" heading, and the semantic nav element with role="navigation" provides appropriate structure for assistive technologies.

code/core/src/manager/components/sidebar/Search.tsx (1)

445-447: LGTM: Render props API updated to support landmark-aware navigation.

The new isNavVisible, isNavRendered, and isSearchResultRendered flags provide clearer semantics for controlling visibility and rendering of navigation elements, aligning with the broader landmark navigation strategy.

code/core/src/manager/components/sidebar/Sidebar.tsx (3)

142-156: LGTM: Banner landmark and document structure correctly implemented.

The addition of a visually-hidden h1 for "Storybook" and the banner landmark on the header element provides proper document structure and navigation semantics. The IconSymbols component placement is appropriate.


213-222: Verify screen reader behavior with conditionally hidden Explorer.

When isNavVisible is false, the Explorer remains in the DOM with sb-sr-only class applied. Confirm that this approach doesn't cause screen readers to announce hidden navigation content while users are interacting with search results.

This pattern is likely intentional to preserve keyboard navigation state, but validation with actual screen reader testing would be beneficial to ensure it doesn't create confusion for AT users.


31-47: LGTM: Semantic container updated to header for banner landmark.

The change from styled.div to styled.header provides the appropriate semantic foundation for the banner landmark while preserving all styling.

Comment thread code/core/src/manager/components/sidebar/Explorer.tsx Outdated
Comment thread code/core/src/manager/components/sidebar/TestingWidget.tsx
@Sidnioulz Sidnioulz added patch:yes Bugfix & documentation PR that need to be picked to main branch bug and removed feature request labels Jan 12, 2026
Comment thread code/core/src/components/components/Card/Card.tsx Outdated
Comment thread code/core/src/manager/components/layout/useLandmarkIndicator.ts Outdated
Comment thread code/core/src/manager/components/layout/useLandmarkIndicator.ts Outdated
Comment thread code/core/src/manager/components/layout/useLandmarkIndicator.ts Outdated
const animation = landmarkElement.animate(
[{ border: `2px solid ${theme.color.primary}` }, { border: `2px solid transparent` }],
{
duration: 1500,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use 200ms in most other places, so 1500ms seems like a lot.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This animation doesn't fulfill the same role, as we want to leave enough time for the region to be noticed by users.

See how it looks with 200ms:

Peek.2026-01-12.13-23.webm

Comment thread code/core/src/manager/components/preview/Toolbar.tsx Outdated
Comment thread code/core/src/manager/components/sidebar/Search.tsx Outdated
Comment on lines +213 to +235
{isNavRendered && (
<Explorer
className={isNavVisible ? undefined : 'sb-sr-only'}
dataset={dataset}
selected={selected}
isLoading={isLoading}
isBrowsing={isNavVisible}
hasEntries={hasEntries}
/>
)}
{isSearchResultRendered && (
<SearchResults
query={query}
results={results}
closeMenu={closeMenu}
getMenuProps={getMenuProps}
getItemProps={getItemProps}
highlightedIndex={highlightedIndex}
enableShortcuts={enableShortcuts}
isLoading={isLoading}
clearLastViewed={lastViewedProps.clearLastViewed}
/>
)}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What I don't like about this is that it suggests that both Explorer and SearchResults might be rendered at the same time. The Swap component was there to make it very explicit that that would never be the case. An if/else ternary on a single condition seems more appropriate.

Also, I worry that by unmounting rather than hiding, these components will lose important internal state when switching between explorer and search. Have you checked that?

Copy link
Copy Markdown
Member Author

@Sidnioulz Sidnioulz Jan 12, 2026

Choose a reason for hiding this comment

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

The Swap component forced both components to always render, but be unreachable. This leads to landmark elements being findable in the DOM, but not focusable.

Depending on the landmark navigation algorithm in use (react-aria, NVDA, VO, JAWS, Narrator, etc), this can lead to bugs where navigating to the next landmark does nothing (it finds the landmark, but fails to focus it). So we need to distinguish states where nav is kb-reachable (we are in the input but there's no query) and states where nav is entirely hidden (we have a query and results).

Note that the "recently opened" view and the "search results" view have never been kb-reachable. This does not change here as it's a Tree 2.0 issue to be solved.

You were right about loss of state in the tree though, so I've reworked the code to keep rendering the Explorer (still with a distinction between visually hidden and not reachable).

The SearchResults view is necessarily stateless, as it relies on the query, and it can only change/disappear when the query changes or disappears. I prefer to render it only when necessary if that's ok with you.

--

@MichaelArestad we should probably jam on the UX surrounding "recently viewed" for Tree 2.0, as it's both hard to discover and I wouldn't be able to make it keyboard-accessible with the current UX logic.

Comment thread code/core/src/manager/components/sidebar/Tree.tsx
Copy link
Copy Markdown
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/core/src/manager/components/sidebar/Explorer.tsx (1)

10-18: className from ...restProps can override the computed sb-sr-only style

The component declares className in ExplorerProps but does not explicitly destructure it, so it ends up in restProps. Since {...restProps} is spread after the className attribute, a caller passing className would override the computed value and break the sr-only gating when isBrowsing is false.

While current call sites (Sidebar.tsx, Explorer.stories.tsx) don't pass className, this is a defensive programming concern—explicitly destructuring className and merging it prevents accidental overrides.

Proposed fix (merge className, prevent override)
 export interface ExplorerProps {
   className?: string;
   isLoading: boolean;
   isBrowsing: boolean;
   isHidden: boolean;
   hasEntries: boolean;
   dataset: CombinedDataset;
   selected: Selection;
 }

 export const Explorer: FC<ExplorerProps> = React.memo(function Explorer({
   hasEntries,
   isLoading,
   isBrowsing,
   isHidden,
   dataset,
   selected,
+  className,
   ...restProps
 }) {
   const containerRef = useRef<HTMLElement>(null);
@@
   return (
     <nav
       hidden={isHidden || undefined}
       aria-hidden={isHidden || undefined}
-      className={isBrowsing ? undefined : 'sb-sr-only'}
+      className={[className, isBrowsing ? null : 'sb-sr-only'].filter(Boolean).join(' ')}
       ref={containerRef}
       id="storybook-explorer-tree"
       data-highlighted-ref-id={highlighted?.refId}
       data-highlighted-item-id={highlighted?.itemId}
       {...landmarkProps}
       {...restProps}
     >

Also applies to: 20-28, 45-55

🧹 Nitpick comments (2)
code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (1)

142-149: Section/header tag changes are fine; consider whether <header> at the bottom is desired semantics
If the bottom bar is intentionally treated as a “banner” landmark (per PR goals), this is OK; otherwise, a neutral element with role="banner" would avoid “header at the bottom” semantics.

Also applies to: 151-171

code/core/src/manager/components/sidebar/Search.tsx (1)

316-318: Search landmark integration LGTM; consider adding an explicit landmark label
If landmark quick-nav announces multiple “search” regions (now or in the future), adding aria-label/aria-labelledby would reduce ambiguity.

Also applies to: 392-399

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7feac0b and 074fd4c.

📒 Files selected for processing (9)
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/types.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/types.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
🧬 Code graph analysis (4)
code/core/src/manager/components/sidebar/TestingWidget.tsx (1)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/sidebar/Search.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/sidebar/Explorer.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
⏰ 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). (3)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (5)
code/core/src/manager/components/sidebar/Sidebar.tsx (2)

198-234: New isNavVisible/isNavReachable/isSearchResultRendered split is a good simplification; please sanity-check focus flows
Specifically: empty-search focus, non-empty-search focus, and closing results (Esc) should leave Explorer reachable again.


31-47: Banner landmark + sr-only H1 correctly implemented; no duplicate banner or IconSymbols instances

Verified:

  • Sidebar and MobileNavigation render exclusively based on viewport (isMobile boolean); only one role="banner" renders at a time
  • IconSymbols mounted only once in Sidebar.tsx production code; story files are test code and expected to include it
code/core/src/manager/components/sidebar/Search.tsx (1)

437-447: New nav state flags look reasonable; please verify the “Search focused → Explorer unreachable” fix in keyboard-only mode
Worth confirming for: empty input, non-empty input, Esc-to-close, and Shift+F6/F6 cycles.

code/core/src/manager/components/sidebar/TestingWidget.tsx (1)

253-258: The landmark integration is already correct. The ref={cardRef} and outlineAttrs={landmarkProps} both apply to the same CardOutline DOM element (the outer wrapper) because Card's implementation spreads outlineAttrs directly onto the element that receives the ref at line 104 of Card.tsx. The aria-labelledby reference is properly set up. No additional hardening is needed.

Likely an incorrect or invalid review comment.

code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (1)

81-86: Banner uniqueness confirmed in mobile layout

The landmark wiring is correct. In mobile mode, only the MobileNavigation component renders (lines 183–189 in Layout.tsx), while the Sidebar with its own banner renders only in desktop mode (lines 177–182). The conditional rendering ensures no duplicate role="banner" landmarks. The aria-labelledby={headingId} pattern correctly connects to the sr-only heading, and spreading landmarkProps onto MobileBottomBar matches proper hook usage.

@Sidnioulz Sidnioulz force-pushed the sidnioulz/issue-32271 branch from 2ff19a2 to f9dc0a8 Compare January 12, 2026 16:07
Copy link
Copy Markdown
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 (6)
code/core/src/manager/components/sidebar/Heading.tsx (1)

90-96: LGTM! The skip link text change improves accessibility.

"Skip to content" is a more universally recognized skip link phrase than "Skip to canvas", which aligns well with standard accessibility patterns.

Minor nit: the styled component is still named SkipToCanvasLink (line 50) while the text now says "Skip to content". Consider renaming to SkipToContentLink for consistency.

♻️ Optional: Rename styled component for consistency
-const SkipToCanvasLink = styled(Button)(({ theme }) => ({
+const SkipToContentLink = styled(Button)(({ theme }) => ({

And update the usage:

-      <SkipToCanvasLink ariaLabel={false} asChild>
+      <SkipToContentLink ariaLabel={false} asChild>
code/core/src/components/components/Card/Card.tsx (1)

100-100: Simplify redundant destructuring syntax.

The outlineAttrs: outlineAttrs = {} pattern renames the property to the same name. This can be simplified.

♻️ Suggested simplification
-    { outlineAnimation = 'none', outlineColor, outlineAttrs: outlineAttrs = {}, ...props },
+    { outlineAnimation = 'none', outlineColor, outlineAttrs = {}, ...props },
docs/get-started/browse-stories.mdx (1)

23-26: Consider clarifying that F6 is separate from the shortcuts menu.

The callout is well-structured and uses appropriate <kbd> tags for accessibility. However, given that line 19 references the Storybook shortcuts menu, users may look for F6 there and not find it.

Per the PR objectives, the F6 shortcut is hardcoded by React Aria and won't appear in the shortcuts UI. Consider adding a brief note to manage expectations, such as:

 <Callout variant="info" icon="♿">
-  Storybook supports fast keyboard navigation between landmark regions. Press <kbd>F6</kbd> and <kbd>Shift+F6</kbd> to navigate between the sidebar, toolbar, preview, and addons panel.
+  Storybook supports fast keyboard navigation between landmark regions. Press <kbd>F6</kbd> and <kbd>Shift+F6</kbd> to navigate between the sidebar, toolbar, preview, and addons panel. This is a browser-standard shortcut and does not appear in the shortcuts menu.
 </Callout>
code/core/src/manager/components/sidebar/Sidebar.tsx (1)

198-234: LGTM! Clear separation of visibility and reachability states.

The rendering logic correctly maps the new UI state flags:

  • isNavVisible → controls visual display (isBrowsing)
  • isNavReachable → controls DOM reachability (isHidden)
  • isSearchResultRendered → gates SearchResults rendering

This ensures the nav remains keyboard-accessible (for F6 navigation) even when visually replaced by search results.

Minor nit: The fragment wrapper and braces around Explorer (lines 210-219) could be simplified since it's no longer conditional, but this is purely cosmetic.

code/core/src/manager/settings/shortcuts.tsx (2)

174-179: Update type for consistency with ConfiguredShortcut.

The shortcutKeys property type is Record<Feature, any>, but toShortcutState returns Record<Feature, ConfiguredShortcut>. Updating this to Record<Feature, ConfiguredShortcut> would improve type safety and consistency.

♻️ Suggested fix
 export interface ShortcutsScreenState {
   activeFeature: Feature;
   successField: Feature;
-  shortcutKeys: Record<Feature, any>;
+  shortcutKeys: Record<Feature, ConfiguredShortcut>;
   addonsShortcutLabels?: Record<string, string>;
 }

320-333: Remove unnecessary fragment wrapper.

The empty fragment (<>...</>) around TooltipProvider is unnecessary since it's the only element in this branch.

♻️ Suggested fix
           {hardcoded ? (
-            <>
-              <TooltipProvider
-                tooltip={<Tooltip hasChrome>This shortcut cannot be changed.</Tooltip>}
-                placement="right"
-              >
-                <TextInput
-                  aria-disabled
-                  readOnly
-                  valid={undefined}
-                  value={shortcut ? shortcutToHumanString(shortcut) : ''}
-                />
-              </TooltipProvider>
-            </>
+            <TooltipProvider
+              tooltip={<Tooltip hasChrome>This shortcut cannot be changed.</Tooltip>}
+              placement="right"
+            >
+              <TextInput
+                aria-disabled
+                readOnly
+                valid={undefined}
+                value={shortcut ? shortcutToHumanString(shortcut) : ''}
+              />
+            </TooltipProvider>
           ) : (
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 074fd4c and f9dc0a8.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (25)
  • code/core/package.json
  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/src/manager/settings/shortcuts.tsx
  • code/core/src/theming/global.ts
  • docs/get-started/browse-stories.mdx
💤 Files with no reviewable changes (1)
  • code/core/src/manager/components/sidebar/Sidebar.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
  • code/core/src/manager/components/panel/Panel.tsx
  • code/core/src/manager/components/layout/Layout.tsx
  • code/core/src/manager/components/sidebar/TestingWidget.tsx
  • code/core/src/manager/hooks/useLandmark.ts
  • code/core/package.json
  • code/core/src/manager/components/sidebar/Heading.stories.tsx
  • code/core/src/manager/components/preview/Preview.tsx
  • code/core/src/theming/global.ts
  • code/core/src/manager/components/sidebar/Tree.tsx
  • code/core/src/manager/components/sidebar/Search.tsx
  • code/core/src/manager/components/sidebar/Tree.stories.tsx
  • code/core/src/manager/components/layout/useLandmarkIndicator.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/types.ts
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/components/components/Card/Card.tsx
  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/components/components/Card/Card.tsx
  • docs/get-started/browse-stories.mdx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/components/components/Card/Card.tsx
  • docs/get-started/browse-stories.mdx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/components/sidebar/Heading.tsx
  • code/core/src/manager/components/sidebar/Explorer.tsx
  • code/core/src/manager/components/preview/utils/components.ts
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
  • code/core/src/manager/components/sidebar/Sidebar.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • docs/get-started/browse-stories.mdx
  • code/core/src/manager/components/preview/FramesRenderer.tsx
  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
  • code/core/src/manager/components/preview/Toolbar.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: In viewport tool code, when using the `useGlobals` hook from storybook/manager-api, the third returned value `storyGlobals` is guaranteed by TypeScript to be defined (not undefined/null), making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
📚 Learning: 2025-09-18T20:51:06.618Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/viewport/components/Tool.tsx:38-39
Timestamp: 2025-09-18T20:51:06.618Z
Learning: The useGlobals hook from storybook/manager-api returns a tuple where the third element (storyGlobals) is typed as Globals, not Globals | undefined. This means TypeScript guarantees it's always defined, making the `in` operator safe to use without additional null checks.

Applied to files:

  • code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx
🧬 Code graph analysis (3)
code/core/src/manager/components/sidebar/Explorer.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (2)
code/core/src/preview-api/index.ts (1)
  • useRef (13-13)
code/core/src/manager/hooks/useLandmark.ts (1)
  • useLandmark (10-22)
code/core/src/manager/settings/shortcuts.tsx (1)
code/core/src/manager-api/modules/shortcuts.ts (1)
  • API_KeyCollection (95-95)
⏰ 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). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (26)
code/core/src/components/components/Form/styles.ts (1)

110-113: LGTM! Good accessibility enhancement.

Expanding the disabled selector to include aria-disabled="true" ensures consistent styling for elements that use ARIA-based disabled states (which remain focusable for screen reader accessibility). The cursor: 'not-allowed' addition provides clear visual feedback.

code/core/src/manager/components/preview/FramesRenderer.tsx (1)

99-103: LGTM!

Removing the title attribute is appropriate here since the anchor text "Skip to sidebar" already provides a clear accessible name. The ariaLabel={false} on the Button correctly indicates that the children serve as the accessible name per project conventions.

code/core/src/manager/components/preview/utils/components.ts (1)

5-19: LGTM! Landmark restructuring aligns with PR objectives.

The changes correctly flatten the landmark hierarchy:

  • PreviewContainer demoted from main to div removes the outer landmark
  • FrameWrap promoted from section to main makes it the primary main landmark

This aligns with the PR objective to have a single main landmark at the preview level, with landmarkProps applied via Preview.tsx for proper ARIA labeling.

code/core/src/manager/components/preview/Toolbar.tsx (3)

1-1: LGTM!

The imports for useRef and useLandmark are correctly added to support the new landmark functionality.

Also applies to: 21-21


87-91: LGTM! Landmark integration is well-implemented.

The useLandmark hook is correctly used with:

  • role: 'region' matching the PR objective for the Toolbar landmark
  • aria-labelledby correctly referencing the existing sr-only heading (sb-preview-toolbar-title at line 101)

This approach aligns with the established pattern where the enclosing section is labeled via the sr-only heading, while the inner StyledToolbar intentionally remains without its own aria-label. Based on learnings, this is the intended design.


98-99: LGTM!

The sectionRef and spread landmarkProps are correctly applied to StyledSection, enabling F6/Shift+F6 landmark navigation for this region.

code/core/src/components/components/Card/Card.tsx (1)

1-1: LGTM!

The DOMAttributes import and the new outlineAttrs prop are well-typed and appropriately optional. This cleanly supports the landmark focus outline attributes needed for accessibility.

Also applies to: 92-96

docs/get-started/browse-stories.mdx (3)

11-13: LGTM!

The section title update to "Sidebar and Preview" accurately reflects the expanded content, and the description correctly explains the relationship between story files, sidebar items, and the isolated preview iframe.


17-17: LGTM!

The description of the three sidebar areas (search, explorer, component test widget) accurately reflects the landmark structure implemented in this PR and provides clear guidance for users.


19-19: LGTM!

Clear and concise instruction for accessing keyboard shortcuts.

code/core/src/manager/components/sidebar/types.ts (1)

54-71: LGTM! Well-documented type expansion for granular UI state control.

The separation of isBrowsing into three distinct flags (isNavVisible, isNavReachable, isSearchResultRendered) provides clearer semantics for accessibility-driven rendering decisions. The inline documentation concisely explains each flag's purpose.

code/core/src/manager/components/mobile/navigation/MobileNavigation.tsx (3)

81-85: LGTM! Proper landmark setup for the mobile navigation bar.

The useLandmark hook is correctly integrated with role="banner" and aria-labelledby referencing the heading. This aligns with the PR objective to make MobileNavigation a proper header/banner landmark.


106-109: LGTM! Landmark props correctly applied with accessible labeling.

The landmarkProps and ref are properly wired to the MobileBottomBar, and the visually hidden <h2> provides a clear accessible name for the banner region.


151-151: Semantic element aligns with landmark role.

Using <header> for MobileBottomBar correctly reflects its role="banner" semantics. The explicit role attribute is technically redundant since <header> has an implicit banner role (when not nested in sectioning content), but it doesn't cause issues and maintains consistency with other landmark integrations in this PR.

code/core/src/manager/components/sidebar/Sidebar.tsx (3)

1-1: LGTM!

Import additions are appropriate for the landmark integration.


31-47: Semantic structure correctly updated.

Changing Container from <nav> to <header> aligns with the PR objective to make the Sidebar a banner landmark. The navigation role is now appropriately handled by the Explorer component's <nav> element.


142-153: LGTM! Proper landmark structure with accessible H1.

The landmark integration correctly:

  • Uses role="banner" for the sidebar header
  • Provides a visually hidden <h1> with "Storybook" as the main page heading, matching the accessibility audit recommendation from the linked issue
code/core/src/manager/components/sidebar/Explorer.tsx (4)

10-18: LGTM! Props interface properly extended.

The new className and isHidden props support the landmark integration and provide necessary control over visibility state for accessibility.


29-42: LGTM! Correct landmark configuration for navigation.

The useLandmark hook is properly configured with role="navigation" and aria-labelledby referencing the heading. The ref type change to HTMLElement accommodates the semantic element change.


44-58: LGTM! Semantic nav element with proper hidden state handling.

The implementation correctly handles two distinct hidden states:

  • isHidden=true → fully hidden (hidden + aria-hidden) but still in DOM for landmark registration
  • isBrowsing=false → visually hidden via sb-sr-only but still accessible to screen readers and keyboard navigation

This aligns with the PR objective to keep the nav reachable when Search is focused.


59-73: LGTM!

The existing rendering logic for HighlightStyles and Ref components is preserved within the new <nav> structure.

code/core/src/manager/settings/shortcuts.tsx (5)

4-14: LGTM!

The import additions are appropriate: Tooltip and TooltipProvider are used for the hardcoded shortcut UI, and API_KeyCollection properly types the shortcut arrays.


139-140: LGTM!

The new landmark shortcut labels are clear and align well with the accessibility objectives.


147-155: LGTM!

The ConfiguredShortcut type cleanly encapsulates the shortcut state, and the hardcoded landmark shortcuts correctly reflect React Aria's default F6/Shift+F6 keybindings as noted in the PR objectives.


157-172: LGTM!

The function correctly merges configurable shortcuts from props with hardcoded shortcuts, ensuring hardcoded entries always take precedence. The defensive filtering against fixedShortcuts in the second loop is acceptable for future-proofing.


305-356: LGTM!

The renderKeyInput method correctly distinguishes between editable and hardcoded shortcuts. Hardcoded shortcuts are rendered as read-only with an informative tooltip, while configurable shortcuts retain their full editing functionality. The implementation aligns well with the landmark navigation accessibility objectives.

@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Jan 12, 2026

Package Benchmarks

Commit: 276e724, ran on 12 January 2026 at 18:35:20 UTC

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

storybook

Before After Difference
Dependency count 49 49 0
Self size 20.27 MB 20.25 MB 🎉 -24 KB 🎉
Dependency size 16.52 MB 16.52 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 183 183 0
Self size 775 KB 775 KB 🎉 -84 B 🎉
Dependency size 67.35 MB 67.33 MB 🎉 -24 KB 🎉
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 176 176 0
Self size 30 KB 30 KB 🎉 -36 B 🎉
Dependency size 65.92 MB 65.90 MB 🎉 -24 KB 🎉
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 50 50 0
Self size 999 KB 999 KB 0 B
Dependency size 36.79 MB 36.77 MB 🎉 -24 KB 🎉
Bundle Size Analyzer node node

@Sidnioulz Sidnioulz force-pushed the sidnioulz/issue-32271 branch from 277893a to 49f0abc Compare January 12, 2026 16:55
Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (2)
docs/get-started/browse-stories.mdx (1)

23-26: Consider adding a brief note about the preview iframe limitation.

The Callout is clear and uses proper semantic markup with <kbd> tags. However, the PR notes that F6 navigation cannot escape from within the preview iframe. For completeness, consider adding a brief caveat, though this may be too detailed for a getting-started guide.

📝 Optional: Add iframe caveat
 <Callout variant="info" icon="♿">
-  Storybook supports fast keyboard navigation between landmark regions. Press <kbd>F6</kbd> and <kbd>Shift+F6</kbd> to navigate between the sidebar, toolbar, preview, and addons panel.
+  Storybook supports fast keyboard navigation between landmark regions. Press <kbd>F6</kbd> and <kbd>Shift+F6</kbd> to navigate between the sidebar, toolbar, preview, and addons panel. Note that this shortcut does not work from within the preview iframe.
 </Callout>
code/core/src/manager/settings/shortcuts.tsx (1)

316-329: Consider removing the unnecessary fragment wrapper.

The empty <>...</> around TooltipProvider serves no purpose since there's only one child element.

Proposed simplification
           {hardcoded ? (
-            <>
-              <TooltipProvider
-                tooltip={<Tooltip hasChrome>This shortcut cannot be changed.</Tooltip>}
-                placement="right"
-              >
-                <TextInput
-                  aria-disabled
-                  readOnly
-                  valid={undefined}
-                  value={shortcut ? shortcutToHumanString(shortcut) : ''}
-                />
-              </TooltipProvider>
-            </>
+            <TooltipProvider
+              tooltip={<Tooltip hasChrome>This shortcut cannot be changed.</Tooltip>}
+              placement="right"
+            >
+              <TextInput
+                aria-disabled
+                readOnly
+                valid={undefined}
+                value={shortcut ? shortcutToHumanString(shortcut) : ''}
+              />
+            </TooltipProvider>
           ) : (
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9dc0a8 and 49f0abc.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • code/core/src/components/components/Form/styles.ts
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager/settings/shortcuts.tsx
  • docs/get-started/browse-stories.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/core/src/components/components/Form/styles.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/settings/shortcuts.tsx
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/settings/shortcuts.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • code/core/src/manager/settings/defaultShortcuts.tsx
  • code/core/src/manager-api/modules/shortcuts.ts
  • code/core/src/manager/settings/shortcuts.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • docs/get-started/browse-stories.mdx
  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • docs/get-started/browse-stories.mdx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/manager/components/sidebar/Explorer.stories.tsx
  • docs/get-started/browse-stories.mdx
  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/manager/settings/shortcuts.tsx
🧬 Code graph analysis (1)
code/core/src/manager/settings/shortcuts.tsx (1)
code/core/src/manager-api/modules/shortcuts.ts (1)
  • API_KeyCollection (95-95)
⏰ 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). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (10)
code/core/src/manager/components/sidebar/Explorer.stories.tsx (1)

76-101: LGTM!

The addition of isHidden={false} to both story components correctly satisfies the updated ExplorerProps interface. Setting it to false ensures the Explorer remains visible in these story previews, which is the expected behavior for testing/demonstration purposes.

docs/get-started/browse-stories.mdx (2)

11-13: LGTM!

The updated heading and description clearly introduce the sidebar and preview concepts.


17-19: LGTM!

The description of the three sidebar areas aligns well with the landmark structure introduced in this PR, and the keyboard shortcuts guidance is appropriately concise.

code/core/src/manager-api/modules/shortcuts.ts (3)

120-121: LGTM!

The new landmark navigation shortcuts are correctly added to the API_Shortcuts interface, maintaining consistency with the existing pattern.


162-163: LGTM!

Default shortcuts correctly match react-aria's hardcoded F6/Shift+F6 landmark navigation keys. The inline comments documenting this constraint are helpful.


280-283: LGTM!

Appropriate no-op handling since these shortcuts are managed by @react-aria/landmark outside the standard shortcut flow. The comment clearly explains the delegation.

code/core/src/manager/settings/defaultShortcuts.tsx (1)

26-27: LGTM!

Shortcuts are consistent with the API module defaults. The hardcoded comments appropriately document the react-aria constraint.

code/core/src/manager/settings/shortcuts.tsx (3)

139-140: LGTM!

Labels are clear and follow the existing naming convention for shortcut descriptions.


155-168: LGTM!

The toShortcutState function correctly transforms shortcut data and applies the hardcoded flag based on array membership.


330-343: LGTM!

The editable shortcut input maintains the existing interaction pattern with proper focus/blur handlers and validation display.

Comment thread code/core/src/manager/settings/shortcuts.tsx Outdated
Comment thread docs/get-started/browse-stories.mdx Outdated
Sidnioulz and others added 2 commits January 12, 2026 19:17
Co-authored-by: Kyle Gach <kyle.gach@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

![Storybook keyboard shortcuts examples](../_assets/get-started/storybook-keyboard-shortcuts.png)

<Callout variant="info" icon="♿">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❤️

Copy link
Copy Markdown
Contributor

@kylegach kylegach left a comment

Choose a reason for hiding this comment

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

Docs updates are good! Thanks, @Sidnioulz!

Copy link
Copy Markdown
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 (1)
code/core/src/manager/settings/shortcuts.tsx (1)

315-328: Consider minor cleanup: remove unnecessary fragment and refine accessibility attributes.

Two small suggestions for this block:

  1. The fragment wrapper (<>...</>) on lines 316/328 is unnecessary since TooltipProvider is the only child element.
  2. The hardcoded input can still receive focus via tab navigation but offers no interaction. Consider adding tabIndex={-1} to remove it from the tab order, or alternatively use disabled if the component supports it (which would also visually indicate the disabled state).
♻️ Suggested cleanup
           {hardcoded ? (
-            <>
-              <TooltipProvider
-                tooltip={<Tooltip hasChrome>This shortcut cannot be changed.</Tooltip>}
-                placement="right"
-              >
-                <TextInput
-                  aria-disabled
-                  readOnly
-                  valid={undefined}
-                  value={shortcut ? shortcutToHumanString(shortcut) : ''}
-                />
-              </TooltipProvider>
-            </>
+            <TooltipProvider
+              tooltip={<Tooltip hasChrome>This shortcut cannot be changed.</Tooltip>}
+              placement="right"
+            >
+              <TextInput
+                aria-disabled="true"
+                tabIndex={-1}
+                readOnly
+                valid={undefined}
+                value={shortcut ? shortcutToHumanString(shortcut) : ''}
+              />
+            </TooltipProvider>
           ) : (
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49f0abc and 276e724.

📒 Files selected for processing (2)
  • code/core/src/manager/settings/shortcuts.tsx
  • docs/get-started/browse-stories.mdx
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/get-started/browse-stories.mdx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/core/src/manager/settings/shortcuts.tsx
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/core/src/manager/settings/shortcuts.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/manager/settings/shortcuts.tsx
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/core/src/manager/settings/shortcuts.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32594
File: code/core/src/components/components/Popover/WithPopover.tsx:7-9
Timestamp: 2025-10-01T15:24:01.060Z
Learning: In the Storybook repository, "react-aria-components/patched-dist/*" (e.g., "react-aria-components/patched-dist/Dialog", "react-aria-components/patched-dist/Popover", "react-aria-components/patched-dist/Tooltip") are valid import paths created by a patch applied to the react-aria-components package. These imports should not be flagged as broken or invalid until a maintainer explicitly states they are no longer acceptable.
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-10-03T07:55:42.639Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/manager/components/preview/Toolbar.tsx:102-105
Timestamp: 2025-10-03T07:55:42.639Z
Learning: In code/core/src/manager/components/preview/Toolbar.tsx, we intentionally do not add aria-label/aria-labelledby to StyledToolbar (AbstractToolbar) because the enclosing section is already labeled via an sr-only heading and the toolbar is the only content. Revisit only if real user testing indicates a need.

Applied to files:

  • code/core/src/manager/settings/shortcuts.tsx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/manager/settings/shortcuts.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). (3)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (3)
code/core/src/manager/settings/shortcuts.tsx (3)

4-4: LGTM!

The new imports for Tooltip, TooltipProvider, and API_KeyCollection are appropriate for the hardcoded shortcut functionality being added.

Also applies to: 8-9


139-140: LGTM!

The new landmark navigation shortcut labels are descriptive and consistent with the existing naming conventions.


147-167: LGTM!

The ConfiguredShortcut type cleanly captures the shortcut state including the hardcoded flag. The toShortcutState function correctly filters fixed shortcuts and marks hardcoded ones. The comment on lines 152-153 provides helpful context about the React Aria constraint.

@ndelangen ndelangen merged commit 283d093 into next Jan 13, 2026
69 checks passed
@ndelangen ndelangen deleted the sidnioulz/issue-32271 branch January 13, 2026 10:19
valentinpalkovic pushed a commit that referenced this pull request Jan 26, 2026
UI: Improve landmark navigation
(cherry picked from commit 283d093)
@ndelangen ndelangen removed the patch:yes Bugfix & documentation PR that need to be picked to main branch label Jan 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y: keyboard Accessibility issues related to keyboard navigation or shortcuts accessibility bug ci:normal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[A11y]: Harmonise landmarks and heading levels

5 participants