Uh oh!
There was an error while loading. Please reload this page.
[Feature] Dialog: use native <dialog> element (#343) - #435
Conversation
Replace the template-clone pattern with a native <dialog> element. DialogContent now renders as <dialog> opened via showModal() and closed via close(), gaining free top-layer rendering, native backdrop, built-in focus trapping, and Esc-to-close. Body scroll-lock is preserved. All existing public component API and Stimulus controller actions are backward-compatible.
There was a problem hiding this comment.
1 issue found across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Uh oh!
There was an error while loading. Please reload this page.
…P registry (#435) Clear overflow-hidden from body in disconnect() so Turbo navigation while a dialog is open does not leave a permanent scroll lock. Also rebuilt the MCP registry to include the native-dialog changes from this PR.
djalmaaraujo
commented
Jun 22, 2026
Addressed the code review finding from cubic (confidence 8): added |
cirdes
left a comment
There was a problem hiding this comment.
Really clean PR. Native <dialog> is the right move!
Uh oh!
There was an error while loading. Please reload this page.
- Bump RubyUI::VERSION to 1.4.0 and regenerate both lockfiles - Update home hero badge to "Native Dialog, Form docs, and more" (header version badge reads RubyUI::VERSION, updates automatically) - Rebuild MCP registry for 1.4.0 Highlights since v1.3.0: native <dialog> element (#435), accordion closed-content visibility fix (#433), Form Rails-integration docs (#434), multi-component generator and as:-render options.
Closes#343
How to reproduce the original problem
Before this change, opening the browser devtools and inspecting an open Dialog revealed the modal rendered as nested
<div>elements cloned from a<template>tag — no native<dialog>semantics, no top-layer rendering, no built-in focus trap, and Esc required a manualkeydownlistener wired via a Stimulus action.Steps:
<div data-controller="ruby-ui--dialog">— no<dialog>element in the DOMWhat changed
gem/lib/ruby_ui/dialog/dialog_content.rb<template>wrapper and custom backdrop<div>;DialogContentnow renders directly as a native<dialog>element withdata-ruby-ui--dialog-target="dialog"and aclick->ruby-ui--dialog#backdropClickaction. The CSSbackdrop:pseudo-element replaces the manual overlay div.gem/lib/ruby_ui/dialog/dialog_controller.jsinsertAdjacentHTML/element.remove()pattern withdialogTarget.showModal()/dialogTarget.close(). Backdrop click is handled by comparingevent.target === dialogTarget. Acloseevent listener removesoverflow-hiddenfrom the body so Esc (which firesclosenatively) also unlocks scroll.gem/test/ruby_ui/dialog_test.rb<dialog>is rendered (not<div>/<template>), Stimulus target/action attributes, size variants,openvalue, trigger action, and close button action.Public API is fully preserved — all component names, size variants,
open:prop, and Stimulus action names (open,dismiss) remain unchanged.Testing instructions
Automated tests
Manual browser verification (docs site)
cd docs && bin/dev)/docs/dialog<dialog>element (not a<div>)overflow-hiddenis applied to<body>)Summary by cubic
Move Dialog to the native element using showModal()/close() for top-layer rendering, native backdrop, focus trap, and Esc‑to‑close while keeping the public API and Stimulus actions unchanged. Also clear body scroll lock on controller disconnect to avoid stuck scrolling during Turbo navigation.
Refactors
<dialog>:DialogContentrenders<dialog>withdata-ruby-ui--dialog-target="dialog"andclick->ruby-ui--dialog#backdropClick; controller usesshowModal()/close()and listens forcloseto restore body scroll.backdrop:/open:utilities; tests cover native<dialog>, attributes, size variants, and open/dismiss actions.Bug Fixes
overflow-hiddenindisconnect()so navigating with Turbo while a dialog is open doesn’t leave the page locked; rebuilt MCP registry to include native-dialog changes.Written for commit 9290140. Summary will update on new commits.