Skip to content

feat(navigation): add dropdown menu component with keyboard nav and animations#446

Open
KeenIsHere wants to merge 4 commits intocodse:mainfrom
KeenIsHere:feat/dropdown-menu
Open

feat(navigation): add dropdown menu component with keyboard nav and animations#446
KeenIsHere wants to merge 4 commits intocodse:mainfrom
KeenIsHere:feat/dropdown-menu

Conversation

@KeenIsHere
Copy link
Copy Markdown

@KeenIsHere KeenIsHere commented Apr 26, 2026

What this PR does

Adds a new Dropdown Menu navigation primitive.

Features

  • Trigger button + animated dropdown panel
  • Icon + label support per item
  • Full keyboard navigation (arrows, enter, escape)
  • Click outside to close
  • Smooth framer-motion animation (transform + opacity only)
  • prefers-reduced-motion support
  • aria-expanded, aria-haspopup, role="menu" accessibility

Screenshots

image image

Checklist

  • Responsive
  • Dark mode tested
  • Keyboard accessible
  • Reduced motion supported
  • Stories file included
  • Docs updated

Summary by CodeRabbit

  • New Features

    • Added a Dropdown Menu component with customizable items, trigger label, and left/right alignment.
    • Supports keyboard navigation (Arrow Up/Down, Enter, Escape), click-outside-to-close, hover selection highlighting, focus return to trigger, and reduced-motion preference with animated open/close.
  • Documentation

    • Added documentation and Storybook examples showing usage, props, alignment options, icons, accessibility guidance, and interactive previews.

Copilot AI review requested due to automatic review settings April 26, 2026 10:36
@KeenIsHere KeenIsHere temporarily deployed to preview-deployment April 26, 2026 10:36 — with GitHub Actions Inactive
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

📝 Walkthrough

Walkthrough

Adds a client-side DropdownMenu React component with keyboard navigation, click-outside handling, ARIA roles, framer-motion entry/exit animations (respects prefers-reduced-motion), plus Storybook stories and an MDX documentation page describing usage, props, and accessibility.

Changes

Cohort / File(s) Summary
DropdownMenu component
animata/navigation/dropdown-menu.tsx
New "use client" React component. Exports MenuItem and DropdownMenuProps. Implements open state, selectedIndex keyboard navigation (ArrowUp/ArrowDown, Enter, Escape), global keydown & mousedown listeners for click-outside detection, and framer-motion entry/exit animations with reduced-motion support.
Stories
animata/navigation/dropdown-menu.stories.tsx
Adds Storybook registration under Navigation/Dropdown Menu with centered layout and autodocs. Adds controllable align and triggerLabel args and two stories: Primary (left align) and RightAlign (right align).
Documentation
content/docs/navigation/dropdown-menu.mdx
New MDX docs page with frontmatter, component preview, usage examples, MenuItem interface, props table (items, triggerLabel, align), keyboard interactions, click-outside behavior, animation notes (prefers-reduced-motion), and accessibility checklist.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Trigger as "Trigger Button"
    participant Menu as "DropdownMenu"
    participant Motion as "Framer Motion"
    participant Listeners as "Global Listeners"

    User->>Trigger: click
    Trigger->>Menu: toggle open
    Menu->>Motion: start entry animation
    Menu->>Listeners: register keydown & mousedown

    Note over User,Menu: Menu open, items rendered

    User->>Listeners: press ArrowDown
    Listeners->>Menu: update selectedIndex
    Menu->>Menu: highlight item

    User->>Listeners: press Enter
    Listeners->>Menu: invoke selected item's onClick
    Menu->>Motion: start exit animation
    Motion->>Menu: animation complete
    Menu->>Listeners: unregister listeners
    Menu->>Trigger: return focus & close
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • hari

Poem

🐇 I hopped to open, soft and light,
Arrow keys twitched, selecting right,
Escape brings focus back to me,
Motion bows where users plea,
A tiny menu, snug and bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a dropdown menu component with keyboard navigation and animations, which aligns perfectly with the PR's primary objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

Copy link
Copy Markdown

@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

🧹 Nitpick comments (4)
animata/navigation/dropdown-menu.stories.tsx (1)

32-37: Deduplicate the shared menu items array.

Primary and RightAlign use identical items. Consider hoisting to a module-level constant so the stories only diverge by the align/triggerLabel they exercise.

♻️ Suggested refactor
+const sampleItems = [
+  { label: "Profile", icon: <User className="h-4 w-4" /> },
+  { label: "Settings", icon: <Settings className="h-4 w-4" /> },
+  { label: "Help", icon: <HelpCircle className="h-4 w-4" /> },
+  { label: "Sign Out", icon: <LogOut className="h-4 w-4" /> },
+];
+
 export const Primary: Story = {
   args: {
     triggerLabel: "Options",
     align: "left",
-    items: [
-      { label: "Profile", icon: <User className="h-4 w-4" /> },
-      { label: "Settings", icon: <Settings className="h-4 w-4" /> },
-      { label: "Help", icon: <HelpCircle className="h-4 w-4" /> },
-      { label: "Sign Out", icon: <LogOut className="h-4 w-4" /> },
-    ],
+    items: sampleItems,
   },
   ...
 };

 export const RightAlign: Story = {
   args: {
     triggerLabel: "Menu",
     align: "right",
-    items: [
-      { label: "Profile", icon: <User className="h-4 w-4" /> },
-      { label: "Settings", icon: <Settings className="h-4 w-4" /> },
-      { label: "Help", icon: <HelpCircle className="h-4 w-4" /> },
-      { label: "Sign Out", icon: <LogOut className="h-4 w-4" /> },
-    ],
+    items: sampleItems,
   },
   ...
 };

Also applies to: 50-55

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@animata/navigation/dropdown-menu.stories.tsx` around lines 32 - 37, Hoist the
repeated menu items array into a module-level constant (e.g., const MENU_ITEMS)
and have the story exports reuse it instead of duplicating the inline array;
update the Primary and RightAlign story definitions to reference MENU_ITEMS and
only set the differing props (align, triggerLabel) so the only differences
between Primary and RightAlign are the props they pass while the shared items
come from MENU_ITEMS.
content/docs/navigation/dropdown-menu.mdx (1)

17-21: Document the MenuItem shape used by items.

The props table references MenuItem[] but doesn't define what a MenuItem looks like. Since the component exports MenuItem ({ label: string; icon?: React.ReactNode; onClick?: () => void }), consumers benefit from seeing the shape and a small usage example with icon/onClick.

📝 Suggested addition
 ## Props

 | Prop | Type | Default | Description |
 |---|---|---|---|
 | items | MenuItem[] | [] | Array of menu items |
 | triggerLabel | string | "Options" | Button label |
 | align | "left" \| "right" | "left" | Dropdown alignment |
+
+### MenuItem
+
+| Field | Type | Required | Description |
+|---|---|---|---|
+| label | string | yes | Visible label of the item |
+| icon | React.ReactNode | no | Optional leading icon |
+| onClick | () => void | no | Invoked when the item is selected |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/docs/navigation/dropdown-menu.mdx` around lines 17 - 21, Add
documentation for the MenuItem shape used by the items prop: describe the
exported MenuItem type ({ label: string; icon?: React.ReactNode; onClick?: () =>
void }) and show a concise usage example demonstrating an items array with one
item that includes an icon and an onClick handler; reference the items prop and
triggerLabel in the example so consumers see how to pass MenuItem[] into the
dropdown component and how icon/onClick behave.
animata/navigation/dropdown-menu.tsx (2)

35-39: prefersReducedMotion in a ref won't react to user preference changes and is fragile on first open.

Storing the result in a useRef plus a useEffect means:

  1. The ref update doesn't trigger a re-render. It only "happens to work" because the user must click the trigger (which causes a re-render) before the menu animates — relying on this ordering is brittle.
  2. If the user toggles their OS-level reduced-motion preference while the page is mounted, the component never picks it up.

Prefer useState plus a change listener on the media query.

♻️ Suggested refactor
-  const prefersReducedMotion = useRef(false);
-
-  useEffect(() => {
-    prefersReducedMotion.current = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
-  }, []);
+  const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
+
+  useEffect(() => {
+    const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
+    setPrefersReducedMotion(mql.matches);
+    const onChange = (e: MediaQueryListEvent) => setPrefersReducedMotion(e.matches);
+    mql.addEventListener("change", onChange);
+    return () => mql.removeEventListener("change", onChange);
+  }, []);
@@
-  const animationProps = prefersReducedMotion.current
+  const animationProps = prefersReducedMotion
     ? {}
     : {
         initial: { opacity: 0, translateY: -8 },
         animate: { opacity: 1, translateY: 0 },
         exit: { opacity: 0, translateY: -8 },
       };

Also applies to: 87-93

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@animata/navigation/dropdown-menu.tsx` around lines 35 - 39, The
prefersReducedMotion currently stored in a useRef and set once inside useEffect
is brittle and won't re-render or respond to OS preference changes; replace the
useRef with useState (prefersReducedMotion state) and in useEffect create a
media query via window.matchMedia("(prefers-reduced-motion: reduce)"), set the
initial state from mq.matches, add an mq.addEventListener('change', handler) or
mq.addListener fallback to update state on changes, and clean up the listener on
unmount; update any logic referencing prefersReducedMotion to use the state
value (references: prefersReducedMotion, useEffect, useRef) and apply the same
refactor to the other occurrence around lines 87-93.

41-67: Avoid re-binding the global keydown listener on every selection change.

The effect lists selectedIndex (and items) as dependencies, so it tears down and re-attaches the window listener on every arrow press — and on every render of the parent that passes a fresh items array. Reading the latest values from refs keeps the listener stable.

♻️ Suggested refactor
+  const selectedIndexRef = useRef(0);
+  const itemsRef = useRef(items);
+  useEffect(() => { selectedIndexRef.current = selectedIndex; }, [selectedIndex]);
+  useEffect(() => { itemsRef.current = items; }, [items]);
+
   useEffect(() => {
     if (!isOpen) {
       setSelectedIndex(0);
       return;
     }

     const handleKeyDown = (e: KeyboardEvent) => {
+      const currentItems = itemsRef.current;
       if (e.key === "ArrowDown") {
         e.preventDefault();
-        setSelectedIndex((prev) => (prev + 1) % items.length);
+        setSelectedIndex((prev) => (prev + 1) % currentItems.length);
       } else if (e.key === "ArrowUp") {
         e.preventDefault();
-        setSelectedIndex((prev) => (prev - 1 + items.length) % items.length);
+        setSelectedIndex((prev) => (prev - 1 + currentItems.length) % currentItems.length);
       } else if (e.key === "Enter") {
         e.preventDefault();
-        items[selectedIndex]?.onClick?.();
+        currentItems[selectedIndexRef.current]?.onClick?.();
         setIsOpen(false);
       } else if (e.key === "Escape") {
         e.preventDefault();
         setIsOpen(false);
         triggerRef.current?.focus();
       }
     };

     window.addEventListener("keydown", handleKeyDown);
     return () => window.removeEventListener("keydown", handleKeyDown);
-  }, [isOpen, selectedIndex, items]);
+  }, [isOpen]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@animata/navigation/dropdown-menu.tsx` around lines 41 - 67, The effect is
re-attaching the global keydown listener whenever selectedIndex or items change;
instead keep a stable handler by reading mutable refs inside it: create refs
like selectedIndexRef and itemsRef, update those refs whenever setSelectedIndex
or items change, and have handleKeyDown read from selectedIndexRef.current and
itemsRef.current and call itemsRef.current[selectedIndexRef.current]?.onClick;
then make the useEffect that adds/removes window.addEventListener only depend on
isOpen (and triggerRef/setIsOpen setters if needed) so the listener is not
rebound on every selection or on fresh items arrays.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@animata/navigation/dropdown-menu.tsx`:
- Around line 97-111: The dropdown's button elements (e.g., the trigger with
ref={triggerRef} that toggles setIsOpen and the item buttons later in this file)
lack an explicit type and will default to type="submit" inside forms; update
every <button> in this component to include type="button" so clicking the
trigger or menu items won't submit a surrounding form (add type="button" to the
trigger button using triggerRef and to each menu/item button instance).
- Around line 95-111: Generate a stable menu id (e.g., with React's useId) and
set it as the id on the menu container (the motion.div rendered by the dropdown)
and also set aria-controls={menuId} on the trigger button (triggerRef block that
calls setIsOpen). Give each menu item an id like `${menuId}-item-${index}` and
update the menu container to expose the current selection via
aria-activedescendant using the selectedIndex state so screen readers announce
the highlighted item. In the dropdown keydown handler (the function that updates
selectedIndex on Arrow keys), handle Tab by closing the menu (call
setIsOpen(false)) or trapping focus (preventDefault and manage focus), and
ensure aria-expanded remains correct on the trigger; update references to
selectedIndex, triggerRef, setIsOpen, and the motion.div/menuId accordingly.

---

Nitpick comments:
In `@animata/navigation/dropdown-menu.stories.tsx`:
- Around line 32-37: Hoist the repeated menu items array into a module-level
constant (e.g., const MENU_ITEMS) and have the story exports reuse it instead of
duplicating the inline array; update the Primary and RightAlign story
definitions to reference MENU_ITEMS and only set the differing props (align,
triggerLabel) so the only differences between Primary and RightAlign are the
props they pass while the shared items come from MENU_ITEMS.

In `@animata/navigation/dropdown-menu.tsx`:
- Around line 35-39: The prefersReducedMotion currently stored in a useRef and
set once inside useEffect is brittle and won't re-render or respond to OS
preference changes; replace the useRef with useState (prefersReducedMotion
state) and in useEffect create a media query via
window.matchMedia("(prefers-reduced-motion: reduce)"), set the initial state
from mq.matches, add an mq.addEventListener('change', handler) or mq.addListener
fallback to update state on changes, and clean up the listener on unmount;
update any logic referencing prefersReducedMotion to use the state value
(references: prefersReducedMotion, useEffect, useRef) and apply the same
refactor to the other occurrence around lines 87-93.
- Around line 41-67: The effect is re-attaching the global keydown listener
whenever selectedIndex or items change; instead keep a stable handler by reading
mutable refs inside it: create refs like selectedIndexRef and itemsRef, update
those refs whenever setSelectedIndex or items change, and have handleKeyDown
read from selectedIndexRef.current and itemsRef.current and call
itemsRef.current[selectedIndexRef.current]?.onClick; then make the useEffect
that adds/removes window.addEventListener only depend on isOpen (and
triggerRef/setIsOpen setters if needed) so the listener is not rebound on every
selection or on fresh items arrays.

In `@content/docs/navigation/dropdown-menu.mdx`:
- Around line 17-21: Add documentation for the MenuItem shape used by the items
prop: describe the exported MenuItem type ({ label: string; icon?:
React.ReactNode; onClick?: () => void }) and show a concise usage example
demonstrating an items array with one item that includes an icon and an onClick
handler; reference the items prop and triggerLabel in the example so consumers
see how to pass MenuItem[] into the dropdown component and how icon/onClick
behave.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd67eb7a-aa6a-4a39-958d-c51b4408fcf7

📥 Commits

Reviewing files that changed from the base of the PR and between da9d7b2 and ae78b9c.

📒 Files selected for processing (3)
  • animata/navigation/dropdown-menu.stories.tsx
  • animata/navigation/dropdown-menu.tsx
  • content/docs/navigation/dropdown-menu.mdx

Comment on lines +95 to +111
return (
<div className="relative inline-block">
<button
ref={triggerRef}
onClick={() => setIsOpen(!isOpen)}
aria-haspopup="menu"
aria-expanded={isOpen}
className={cn(
"min-h-11 min-w-11 inline-flex items-center justify-center gap-2 rounded-lg",
"bg-background border border-border px-3 py-2 text-sm font-medium",
"text-foreground transition-colors duration-200",
"hover:bg-muted hover:text-muted-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
"dark:focus:ring-offset-background",
"active:scale-95",
)}
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

A11y: wire up aria-controls/aria-activedescendant and handle Tab.

The trigger advertises aria-haspopup="menu" / aria-expanded, but the menu has no id and there is no aria-controls linking the two. Selection is only tracked via the selectedIndex state for visual highlight — DOM focus never moves and there's no aria-activedescendant, so screen readers won't announce the highlighted item as the user arrows through it. Tab is also not handled while the menu is open: focus can escape into the page while the menu remains visible.

Suggested minimum changes:

  • Generate a stable id (e.g., useId()) for the menu, set it on motion.div, and pass it as aria-controls on the trigger.
  • Set per-item ids (${menuId}-item-${index}) and reflect the current one via aria-activedescendant on the menu.
  • Close (or trap) on Tab in the keydown handler.
♿ Sketch of the changes
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useId, useRef, useState } from "react";
@@
   const [isOpen, setIsOpen] = useState(false);
   const [selectedIndex, setSelectedIndex] = useState(0);
+  const menuId = useId();
+  const activeItemId = `${menuId}-item-${selectedIndex}`;
@@
-      } else if (e.key === "Escape") {
+      } else if (e.key === "Escape" || e.key === "Tab") {
         e.preventDefault();
         setIsOpen(false);
         triggerRef.current?.focus();
       }
@@
       <button
         ref={triggerRef}
         type="button"
         onClick={() => setIsOpen(!isOpen)}
         aria-haspopup="menu"
         aria-expanded={isOpen}
+        aria-controls={menuId}
@@
           <motion.div
             ref={menuRef}
+            id={menuId}
             role="menu"
+            tabIndex={-1}
+            aria-activedescendant={activeItemId}
@@
               <button
                 key={index}
+                id={`${menuId}-item-${index}`}
                 type="button"
                 role="menuitem"

Note: preventing default on Tab will swallow the navigation; if you'd rather let focus move naturally, just call setIsOpen(false) without preventDefault().

Also applies to: 128-140, 141-162

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@animata/navigation/dropdown-menu.tsx` around lines 95 - 111, Generate a
stable menu id (e.g., with React's useId) and set it as the id on the menu
container (the motion.div rendered by the dropdown) and also set
aria-controls={menuId} on the trigger button (triggerRef block that calls
setIsOpen). Give each menu item an id like `${menuId}-item-${index}` and update
the menu container to expose the current selection via aria-activedescendant
using the selectedIndex state so screen readers announce the highlighted item.
In the dropdown keydown handler (the function that updates selectedIndex on
Arrow keys), handle Tab by closing the menu (call setIsOpen(false)) or trapping
focus (preventDefault and manage focus), and ensure aria-expanded remains
correct on the trigger; update references to selectedIndex, triggerRef,
setIsOpen, and the motion.div/menuId accordingly.

Comment on lines +97 to +111
<button
ref={triggerRef}
onClick={() => setIsOpen(!isOpen)}
aria-haspopup="menu"
aria-expanded={isOpen}
className={cn(
"min-h-11 min-w-11 inline-flex items-center justify-center gap-2 rounded-lg",
"bg-background border border-border px-3 py-2 text-sm font-medium",
"text-foreground transition-colors duration-200",
"hover:bg-muted hover:text-muted-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
"dark:focus:ring-offset-background",
"active:scale-95",
)}
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add type="button" to all <button> elements.

Without an explicit type, buttons default to type="submit". If this dropdown is rendered inside a <form>, clicking the trigger or any item will submit the form — silently breaking forms that use this navigation primitive.

🐛 Proposed fix
       <button
         ref={triggerRef}
+        type="button"
         onClick={() => setIsOpen(!isOpen)}
         aria-haspopup="menu"
         aria-expanded={isOpen}
               <button
                 key={index}
+                type="button"
                 role="menuitem"
                 onClick={() => {
                   item.onClick?.();
                   setIsOpen(false);
                 }}

Also applies to: 142-148

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@animata/navigation/dropdown-menu.tsx` around lines 97 - 111, The dropdown's
button elements (e.g., the trigger with ref={triggerRef} that toggles setIsOpen
and the item buttons later in this file) lack an explicit type and will default
to type="submit" inside forms; update every <button> in this component to
include type="button" so clicking the trigger or menu items won't submit a
surrounding form (add type="button" to the trigger button using triggerRef and
to each menu/item button instance).

@github-actions
Copy link
Copy Markdown

🚀 Preview deployed to: https://489781b1.animata.pages.dev

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@KeenIsHere KeenIsHere requested a deployment to preview-deployment April 29, 2026 03:59 — with GitHub Actions Waiting
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (3)
content/docs/navigation/dropdown-menu.mdx (3)

60-60: Consider clarifying the default items description.

The description "4 default items" is accurate but vague. Consider being more specific about what those default items are to help users understand the component's out-of-the-box behavior.

📋 Suggested improvement
-| `items` | `MenuItem[]` | 4 default items | Array of menu items |
+| `items` | `MenuItem[]` | Profile, Settings, Help, Sign Out | Array of menu items |

Or alternatively:

-| `items` | `MenuItem[]` | 4 default items | Array of menu items |
+| `items` | `MenuItem[]` | 4 generic items | Array of menu items (defaults: Profile, Settings, Help, Sign Out) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/docs/navigation/dropdown-menu.mdx` at line 60, Update the table row
for the `items` prop (type `MenuItem[]`) to replace the vague "4 default items"
text with a specific list of those defaults (e.g., the actual labels such as
"Profile", "Settings", "Help", "Sign out") and a short note that these are the
out-of-the-box menu entries; keep the description concise so users immediately
know what the default menu contains and that it can be overridden by providing
their own `items`.

47-49: Consider adding a note about onClick handlers.

The empty onClick: () => {} handlers are appropriate placeholders for documentation, but you might consider adding a brief comment indicating these should be replaced with actual handler functions in production use.

💡 Optional enhancement

Add a brief note after the code example:

    />
  );
}
+
+// Note: Replace empty onClick handlers with your application logic
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/docs/navigation/dropdown-menu.mdx` around lines 47 - 49, Update the
example to clarify that the placeholder onClick handlers should be replaced in
real use: add a short inline comment or a brief explanatory sentence after the
code block stating that items like the objects with label, icon (User, Settings,
LogOut) and onClick: () => {} are placeholders and should be replaced with
actual handler functions (e.g., onClick handlers that perform navigation, state
updates, or sign-out logic) in production.

23-23: Minor grammar improvement: hyphenate compound adjective.

The phrase "left or right aligned" should be hyphenated when used as a compound adjective modifying "dropdown panel."

📝 Suggested fix
-- **Alignment** — left or right aligned dropdown panel
+- **Alignment** — left- or right-aligned dropdown panel
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/docs/navigation/dropdown-menu.mdx` at line 23, Update the compound
adjective in the "Alignment — left or right aligned dropdown panel" line by
hyphenating it; change the phrase to "left- or right-aligned dropdown panel" so
the modifiers correctly attach to "dropdown panel" (locate the string "Alignment
— left or right aligned dropdown panel" in the dropdown-menu.mdx content and
replace accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@content/docs/navigation/dropdown-menu.mdx`:
- Line 60: Update the table row for the `items` prop (type `MenuItem[]`) to
replace the vague "4 default items" text with a specific list of those defaults
(e.g., the actual labels such as "Profile", "Settings", "Help", "Sign out") and
a short note that these are the out-of-the-box menu entries; keep the
description concise so users immediately know what the default menu contains and
that it can be overridden by providing their own `items`.
- Around line 47-49: Update the example to clarify that the placeholder onClick
handlers should be replaced in real use: add a short inline comment or a brief
explanatory sentence after the code block stating that items like the objects
with label, icon (User, Settings, LogOut) and onClick: () => {} are placeholders
and should be replaced with actual handler functions (e.g., onClick handlers
that perform navigation, state updates, or sign-out logic) in production.
- Line 23: Update the compound adjective in the "Alignment — left or right
aligned dropdown panel" line by hyphenating it; change the phrase to "left- or
right-aligned dropdown panel" so the modifiers correctly attach to "dropdown
panel" (locate the string "Alignment — left or right aligned dropdown panel" in
the dropdown-menu.mdx content and replace accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc72309c-301d-42b2-86c1-b29333842bee

📥 Commits

Reviewing files that changed from the base of the PR and between ae78b9c and 517fd8f.

📒 Files selected for processing (1)
  • content/docs/navigation/dropdown-menu.mdx

@KeenIsHere KeenIsHere requested a deployment to preview-deployment April 29, 2026 04:03 — with GitHub Actions Waiting
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@content/docs/navigation/dropdown-menu.mdx`:
- Line 23: Update the sentence that currently reads "Alignment — left or right
aligned dropdown panel" to hyphenate the compound modifier for readability by
changing it to "Alignment — left- or right-aligned dropdown panel" (or at
minimum "right-aligned dropdown panel"); locate and edit the exact string "left
or right aligned dropdown panel" in the navigation/dropdown-menu content and
replace with the hyphenated form.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e9fa8182-48ae-47d2-bdf4-4a469d81f1ed

📥 Commits

Reviewing files that changed from the base of the PR and between 517fd8f and a419626.

📒 Files selected for processing (1)
  • content/docs/navigation/dropdown-menu.mdx

- **prefers-reduced-motion** — animation disabled when set
- **Accessible** — aria-expanded, aria-haspopup, role="menu", role="menuitem"
- **Mobile ready** — tap to open/close, 44px minimum touch targets
- **Alignment** — left or right aligned dropdown panel
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hyphenate compound modifier for readability.

Use “right-aligned dropdown panel” instead of “right aligned dropdown panel”.

🧰 Tools
🪛 LanguageTool

[grammar] ~23-~23: Use a hyphen to join words.
Context: ... targets - Alignment — left or right aligned dropdown panel ## Usage ```tsx...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/docs/navigation/dropdown-menu.mdx` at line 23, Update the sentence
that currently reads "Alignment — left or right aligned dropdown panel" to
hyphenate the compound modifier for readability by changing it to "Alignment —
left- or right-aligned dropdown panel" (or at minimum "right-aligned dropdown
panel"); locate and edit the exact string "left or right aligned dropdown panel"
in the navigation/dropdown-menu content and replace with the hyphenated form.

@KeenIsHere KeenIsHere requested a deployment to preview-deployment April 29, 2026 04:17 — with GitHub Actions Waiting
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
content/docs/navigation/dropdown-menu.mdx (1)

23-23: ⚠️ Potential issue | 🟡 Minor

Hyphenate the compound modifier in the alignment bullet.

Line 23 should use hyphenation for readability: “left- or right-aligned dropdown panel.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/docs/navigation/dropdown-menu.mdx` at line 23, Update the "Alignment"
bullet text that currently reads "left or right aligned dropdown panel" to use
proper hyphenation: change the phrase to "left- or right-aligned dropdown panel"
so the compound modifier is correctly hyphenated for readability; locate the
bullet under the dropdown menu documentation where the "Alignment" line contains
"left or right aligned dropdown panel" and replace it with the hyphenated
version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@content/docs/navigation/dropdown-menu.mdx`:
- Line 23: Update the "Alignment" bullet text that currently reads "left or
right aligned dropdown panel" to use proper hyphenation: change the phrase to
"left- or right-aligned dropdown panel" so the compound modifier is correctly
hyphenated for readability; locate the bullet under the dropdown menu
documentation where the "Alignment" line contains "left or right aligned
dropdown panel" and replace it with the hyphenated version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59f2f4b9-b797-4c7c-a091-71f911e41ca8

📥 Commits

Reviewing files that changed from the base of the PR and between a419626 and 0d0c585.

📒 Files selected for processing (1)
  • content/docs/navigation/dropdown-menu.mdx

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants