Skip to content

feat: add keyboard accessibility to TOC toggle#4973

Open
rajeshuchil wants to merge 7 commits into
asyncapi:masterfrom
rajeshuchil:fix/toc-keyboard-accessibility
Open

feat: add keyboard accessibility to TOC toggle#4973
rajeshuchil wants to merge 7 commits into
asyncapi:masterfrom
rajeshuchil:fix/toc-keyboard-accessibility

Conversation

@rajeshuchil

@rajeshuchil rajeshuchil commented Jan 17, 2026

Copy link
Copy Markdown

Description

Fixed keyboard accessibility issue in the TOC component by replacing non-semantic

with a element for the toggle control
Added aria-expanded={open} attribute to communicate toggle state to screen readers
Added aria-label Toggle table of contents to provide context for assistive technology users
Moved onClick handler from outer div to the button element for proper semantic structure
Maintained all existing visual styles and responsive behavior

Fixed issue

Fixes #4972

Summary by CodeRabbit

  • Refactor
    • Updated the table-of-contents header to use a proper toggle button with aria-expanded and aria-label for clearer keyboard and screen-reader interaction; header text markup adjusted and chevron activation moved to the button. Content visibility is now controlled by the button toggle for more predictable, accessible behavior.

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

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our contributors guide useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

@coderabbitai

coderabbitai Bot commented Jan 17, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Replaced a non-semantic clickable container with a semantic <button> in the TOC header, moved the toggle onClick to that button, and added aria-expanded and aria-label; content visibility remains controlled by the component's open state. (48 words)

Changes

Cohort / File(s) Summary
Accessibility refactor
components/TOC.tsx
Replaced a div-based toggle with a semantic button; moved onClick to the button; added type="button", aria-expanded, and aria-label; changed header text container from h5 to span; adjusted surrounding markup to wrap header and chevron in the button; preserved content visibility logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I swapped a div for a button bright,
Now keyboard hops feel just right,
aria labels sing,
Chevrons dance and ring,
Accessibility takes flight ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add keyboard accessibility to TOC toggle' clearly and concisely summarizes the main change—implementing keyboard accessibility for the TOC toggle component.
Linked Issues check ✅ Passed The changes fully address all coding requirements from issue #4972: replaced div with semantic button element, made it focusable, added aria-expanded and aria-label attributes, and maintained existing styles.
Out of Scope Changes check ✅ Passed All modifications in TOC.tsx are directly related to implementing keyboard accessibility for the TOC toggle, with no unrelated or out-of-scope changes present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@netlify

netlify Bot commented Jan 17, 2026

Copy link
Copy Markdown

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit e875be1
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/697b7ee26d7d1f00087a81b7
😎 Deploy Preview https://deploy-preview-4973--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@rajeshuchil rajeshuchil changed the title add keyboard accessibility to TOC toggle feat: add keyboard accessibility to TOC toggle Jan 17, 2026
@asyncapi-bot

asyncapi-bot commented Jan 17, 2026

Copy link
Copy Markdown
Contributor

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 37
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-4973--asyncapi-website.netlify.app/

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
components/TOC.tsx (1)

28-121: Fix Prettier/ESLint violations blocking CI.

The pipeline reports multiple formatter/lint errors in this file (trailing commas, JSX quote style, and line wrapping). Please run the formatter or apply changes like below to unblock CI.

🧹 Proposed formatting fix (representative)
-export default function TOC({
-  className,
-  cssBreakingPoint = 'xl',
-  toc,
-  contentSelector,
-  depth = 2,
-}: ITOCProps) {
+export default function TOC({ className, cssBreakingPoint = 'xl', toc, contentSelector, depth = 2 }: ITOCProps) {
   const [open, setOpen] = useState(false);

   if (!toc || !toc.length) return null;
-  const minLevel = toc.reduce(
-    (mLevel, item) => (!mLevel || item.lvl < mLevel ? item.lvl : mLevel),
-    0,
-  );
+  const minLevel = toc.reduce((mLevel, item) => (!mLevel || item.lvl < mLevel ? item.lvl : mLevel), 0);

   const tocItems = toc
     .filter((item) => item.lvl <= minLevel + depth)
     .map((item) => ({
       ...item,
       content: item.content
-        .replace(/[\s]?\{#[\w\d\-_]+\}$/, '')
+        .replace(/[\s]?\{#[\w\d\-_]+\}$/, '')
         .replace(/(<([^>]+)>)/gi, ''),
       // ...
-      slugWithATag: (() => {
+      slugWithATag: (() => {
         // ...
-      })(),
+      })(),
     }));

   return (
     <div
       className={twMerge(
-        `${className} ${tocItems.length ? '' : 'hidden'} 
-      ${cssBreakingPoint === 'xl' ? 'xl:block' : 'lg:block'} md:top-24 md:max-h-(screen-14) mb-4 z-20`,
+        `${className} ${tocItems.length ? '' : 'hidden'} ${cssBreakingPoint === 'xl' ? 'xl:block' : 'lg:block'} md:top-24 md:max-h-(screen-14) mb-4 z-20`,
       )}
     >
       <button
         className={`flex cursor-pointer ${tocItems.length ? '' : 'hidden'}
         ${cssBreakingPoint === 'xl' ? 'xl:cursor-auto' : 'lg:cursor-auto'} xl:mt-2`}
         onClick={() => setOpen(!open)}
         aria-expanded={open}
-        aria-label="Toggle table of contents"
+        aria-label='Toggle table of contents'
       >
         <h5
           className={twMerge(
             `${open && 'mb-4'} flex-1 text-primary-500 font-medium uppercase tracking-wide text-sm font-sans antialiased ${
               cssBreakingPoint === 'xl'
                 ? 'xl:mb-4 xl:text-xs xl:text-gray-900 xl:font-bold'
                 : 'lg:mb-4 lg:text-xs lg:text-gray-900 lg:font-bold'
             }`,
           )}
-          data-testid="TOC-Heading"
+          data-testid='TOC-Heading'
         >
           On this page
         </h5>
-        <div
-          className={`text-underline p4 text-center ${cssBreakingPoint === 'xl' ? 'xl:hidden' : 'lg:hidden'}`}
-        >
+        <div className={`text-underline p4 text-center ${cssBreakingPoint === 'xl' ? 'xl:hidden' : 'lg:hidden'}`}>
           <ArrowRight
             className={`${open ? '-rotate-90' : 'rotate-90'} -mt-0.5 h-6 
             text-primary-500 transition duration-200 ease-in-out`}
           />
         </div>
       </button>
       <div
-        className={`${!open && 'hidden'} ${cssBreakingPoint === 'xl' ? 'xl:block' : 'lg:block'}`}
+        className={`${!open && 'hidden'} ${cssBreakingPoint === 'xl' ? 'xl:block' : 'lg:block'}`}
       >
         <Scrollspy
-          items={tocItems.map((item) =>
-            item.slug ? item.slug : item.slugWithATag,
-          )}
-          currentClassName="text-primary-500 font-bold"
-          componentTag="div"
+          items={tocItems.map((item) => (item.slug ? item.slug : item.slugWithATag))}
+          currentClassName='text-primary-500 font-bold'
+          componentTag='div'
           rootEl={contentSelector}
           offset={-120}
         >
           {tocItems.map((item, index) => (
             <a
               className={`pl-${2 ** (item.lvl - 1)} font-normal mb-1 block font-sans text-sm 
                   text-gray-900 antialiased transition duration-100 ease-in-out hover:underline`}
               href={`#${item.slug ? item.slug : item.slugWithATag}`}
               key={index}
-              data-testid="TOC-Link"
+              data-testid='TOC-Link'
             >
               {item.content.replaceAll('`', '')}
             </a>
           ))}
🤖 Fix all issues with AI agents
In `@components/TOC.tsx`:
- Around line 75-101: The button in TOC.tsx contains non-phrasing elements and
lacks an explicit type; update the toggle to include type="button" and ensure
only phrasing content is inside the button: either move the <h5
data-testid="TOC-Heading">On this page</h5> out of the <button> (placing it
immediately before the button) or replace that <h5> with a <span> for inline
phrasing, and replace the inner <div> used for the ArrowRight icon with a
<span>; keep the existing onClick handler (setOpen), aria attributes, open state
usage, and ArrowRight classNames intact so behavior and styling remain
unchanged.

Comment thread components/TOC.tsx Outdated
@codecov

codecov Bot commented Jan 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (35abd54) to head (e875be1).

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #4973   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           22        22           
  Lines          796       796           
  Branches       146       146           
=========================================
  Hits           796       796           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rajeshuchil

Copy link
Copy Markdown
Author

@princerajpoot20 sir can you please have a look

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To Be Triaged

Development

Successfully merging this pull request may close these issues.

[BUG] TOC component lacks keyboard accessibility - uses div instead of button for interactive element

3 participants