Skip to content

[Bug Fix] DropdownMenu: fix z-index stacking and default placement (#439)#440

Merged
cirdes merged 2 commits into
mainfrom
fix/dropdown-menu-zindex-placement-439
Jun 24, 2026
Merged

[Bug Fix] DropdownMenu: fix z-index stacking and default placement (#439)#440
cirdes merged 2 commits into
mainfrom
fix/dropdown-menu-zindex-placement-439

Conversation

@djalmaaraujo

@djalmaaraujo djalmaaraujo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #439

Problems

After the Floating UI port (#158), the DropdownMenu had two issues (reproducible on https://www.rubyui.com/docs/dropdown_menu):

  1. Open menu renders behind sibling elements. On the Placement grid, opening a menu showed it under neighbouring triggers. The container carried a static z-50, and so did every sibling dropdown — equal z-index, so paint order fell back to DOM order and later siblings covered an open menu.
  2. First Usage example: menu detached from the "Open" button. Default placement was top (unusual for a dropdown — convention is bottom), and the demo wrapped the trigger in class: 'w-full', so Floating UI used the full-width wrapper as the reference and centred the menu over it, far from the small button.

Changes

  • dropdown_menu_controller.js (gem + docs copy):
    • default placement topbottom.
    • set element.style.zIndex only while the menu is open ("50" on open, cleared on close).
  • dropdown_menu.rb: removed the static z-50 from the container. Closed dropdowns now stack in normal flow (no stacking context), so the one open menu — elevated by the controller — always paints above its neighbours.
  • dropdown_menu_docs.rb + docs/app/views/docs/dropdown_menu.rb: dropped w-full from the Usage trigger so the floating reference matches the visible button.

Testing

  • cd gem && bundle exec rake → 237 runs, 0 failures, StandardRB clean.
  • node --check on both controllers.
  • Manual: on the docs dropdown page, the Usage menu now opens directly below the "Open" button, and Placement menus paint above neighbouring triggers.

Screenshots

Before/after to be added.


Summary by cubic

Fixes DropdownMenu stacking so open menus render above siblings, and sets the default placement to open below the trigger. Updates docs and MCP registry so the menu positions relative to the button, not a full-width wrapper.

  • Bug Fixes
    • Removed container z-50; controller now sets zIndex = "50" only while open and clears on close.
    • Changed default placement from top to bottom in both controllers.
    • Docs: dropped w-full from DropdownMenuTrigger; rebuilt MCP registry.json to sync examples.

Written for commit 9c4be6c. Summary will update on new commits.

Review in cubic

)

- Default placement changed from `top` to `bottom` so menus open below
  their trigger (matches dropdown convention).
- Removed the static `z-50` from the DropdownMenu container; the controller
  now sets z-index only while open, so closed sibling dropdowns no longer
  cover an open menu (they share the same z-50 and DOM order decided paint
  order).
- Dropped `w-full` from the Usage demo trigger so the floating reference
  matches the visible button instead of a full-width wrapper.
@djalmaaraujo djalmaaraujo requested a review from cirdes as a code owner June 23, 2026 19:22

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="gem/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js">

<violation number="1" location="gem/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js:85">
P2: `close()` does not sync `data-state="closed"` — close animations never trigger</violation>
</file>

Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.

Re-trigger cubic

close() {
this.openValue = false;
this.#removeEventListeners();
this.element.style.zIndex = "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: close() does not sync data-state="closed" — close animations never trigger

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At gem/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js, line 85:

<comment>`close()` does not sync `data-state="closed"` — close animations never trigger</comment>

<file context>
@@ -73,12 +73,16 @@ export default class extends Controller {
   close() {
     this.openValue = false;
     this.#removeEventListeners();
+    this.element.style.zIndex = "";
     this.contentTarget.classList.add("hidden");
   }
</file context>
Suggested change
this.element.style.zIndex = "";
this.element.style.zIndex = "";
this.element.setAttribute("data-state", "closed");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks — flagging this as out of scope for this PR (which only covers the z-index stacking and default placement). A few points on the finding:

  1. Pre-existing, not introduced here. The controller has never synced data-state; my diff only touched close() to reset the inline zIndex.
  2. Suggested target is wrong. The data-[state=open|closed]:* animation utilities live on the inner content <div> in DropdownMenuContent (the attrs div), not on this.element (the DropdownMenu container). Setting data-state on the container won't trigger any animation.
  3. hidden blocks the close animation regardless. close() adds hidden (display:none) to the content wrapper, so the exit animation can't play unless the hide is deferred until animationend.

Proper close-animation handling is being tracked separately (see the data-state/hidden sync work on the Floating UI port branch). Leaving it out to keep this PR focused.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You're right: this PR only changes z-index/default placement, so the data-state/close-animation issue is out of scope here. Also, the animation utilities apply to the inner content wrapper, and hidden prevents the exit animation unless hide is deferred.

This comment was influenced by this learning. Open the link to edit it, or reply here to edit or delete it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Open a new issue with the of of scope for me.

@ruby-ui ruby-ui deleted a comment from cubic-dev-ai Bot Jun 23, 2026
@cirdes cirdes merged commit 0edfc3a into main Jun 24, 2026
8 checks passed
@cirdes cirdes deleted the fix/dropdown-menu-zindex-placement-439 branch June 24, 2026 19:04
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.

[Bug] DropdownMenu: open menu renders behind siblings (z-index) + default 'top' placement detaches menu from trigger

2 participants