# Dialog A modal surface: portal, scrim, centred panel. - Group: Overlays - Import: `import { Dialog } from '@misoto22/folio'` - Page: https://ui.misoto22.com/components/dialog/ - Related: dropdown-menu, tooltip ## Anatomy - **Scrim** (required) — The full-viewport --scrim layer at --z-overlay (200). It is what a click outside lands on, and it paints over everything the page had pinned below that rank — a FloatingIconButton at 100 included. Not over an anchored panel: those sit at 220 precisely so a Select opened inside this dialog is still reachable. - **Panel** (required) — The centred box at --z-modal (210), capped at min(92vw, 32rem) wide and 85vh tall, scrolling its own body past that. It centres itself with a translate, which has consequences for anything fixed inside it. - **Title** (required) — title, rendered as the Radix Title. Always present: when title is omitted a visually hidden one is rendered reading the literal word “Dialog”, and development warns DIALOG_TITLE_MISSING — the fallback exists so an unnamed modal is not shipped, not so one can be. - **Description** — description, a quiet line under the title. It shares one wrapper with the title, so hideTitle hides both. - **Close** — The 36px X in the top-end corner, rendered while showClose is true (the default) and carrying its own aria-label of “Close”. ## Best practices ### Do - Pass title even when you set hideTitle: with no title at all the fallback accessible name is the literal string “Dialog”, so every unnamed modal in the app is announced as the same thing — and it passes an automated accessibility check while doing it, which is why development warns instead of leaving it to a review. - Wrap the cancelling control in DialogClose rather than flipping your own state — the close then runs through Radix, which returns focus to the trigger instead of dropping it at the top of the document. - Keep it to what fits. The panel stops at 32rem by 85vh and scrolls its own body past that, so a form long enough to scroll has become a Sheet, which gets the full height of the viewport, or a page. - Leave showClose on unless the panel supplies its own exit: Escape and the scrim are the only other ways out and neither is visible, so showClose={false} on a dialog full of content is a room with an unmarked door. ### Don’t - An OverlayContainer whose element is not positioned hands the dialog the wrong box: naming a container switches the panel from fixed to absolute, and an unpositioned container sends it to the nearest positioned ancestor instead — usually the page, which looks like the container was ignored. - Two dialogs open at once are ordered by the DOM, not by a rank: both sit at --z-modal, so the one mounted last paints over the first — a command palette summoned over a dialog lands on top because it opened second, and reversing that order reverses the picture. - The panel centres itself with a transform, which makes it the containing block for every position: fixed descendant — a FloatingIconButton dropped inside a dialog pins to the panel’s corner rather than the screen’s. ## Accessibility - Radix owns the focus trap, Escape, the scroll lock and aria-modal. - A dialog without a visible heading still renders a hidden title, rather than shipping an unnamed modal. ## Keyboard - Escape — Closes it, and focus returns to the trigger it came from. - Tab — Cycles inside the dialog; focus cannot leave while it is open. ## Dialog Re-export of `DialogPrimitive.Root`. Radix Dialog root + trigger + close, re-exported as typed passthroughs. ## DialogTrigger Re-export of `DialogPrimitive.Trigger`. ## DialogClose Re-export of `DialogPrimitive.Close`. ## DialogContent A modal surface: portal → scrim → centred panel. Radix owns the focus trap, the escape key, the scroll lock and the `aria-modal` wiring — all of which a hand-rolled dialog gets subtly wrong, usually by leaving focus behind in the page underneath. Radix requires a `Dialog.Title` whether or not one is shown, so a dialog without a visible heading still renders a hidden one rather than shipping an unnamed modal — and warns in development, because the fallback it renders is the literal word "Dialog" and a placeholder that passes an accessibility check is how the problem survives a review. Portals into the element an enclosing `OverlayContainer` names, and switches from viewport positioning to container positioning when there is one. A `fixed` panel covers the page whatever it is portalled into, so honouring the container without that swap would have moved the markup and left the picture unchanged. ### Props - `title` — `ReactNode`. Heading text. Pass one even when `hideTitle` is set. Radix requires a title, so omitting it renders a hidden fallback reading the literal word "Dialog" — which satisfies an automated accessibility check and announces every unnamed modal in the application as the same thing. Development says so out loud. - `description` — `ReactNode`. Sub-heading under the title. - `className` — `string`. - `showClose` — `boolean` default `true`. Show the top-right close control (default true). - `hideTitle` — `boolean` default `false`. Keeps the title for assistive tech and hides it visually. For a surface whose purpose is obvious to anyone who can see it — a command palette, a media lightbox — where a printed heading would be furniture. The title itself is never optional: Radix requires one, and a modal with no accessible name drops a screen reader into an unnamed region. Also accepts: `Omit, 'title' | 'className'>`. ## Example — a destructive confirm ```tsx import { Button, Dialog, DialogClose, DialogContent, DialogTrigger } from '@misoto22/folio'
``` ## Example — a hidden title ```tsx
changeset ready
Release 0.4.0 Five packages, one minor bump. The tag is cut when this merges, and the registry has the tarball about ninety seconds later.
``` ## Example — or a sheet ```tsx