Skip to content
TopClass UI

Dialog

A modal dialog whose body scrolls between a sticky header and footer, while the page behind it stays still.

client - size not measured yet

Dialog

client - size not measured yet

A modal dialog whose body scrolls between a sticky header and footer, while the page behind it stays still.

Install

Try it

size
closeButton

The code

dialog.tsx
import { Button } from 'topclassui/button'
import {

<DialogRoot>
  <DialogTrigger render={<Button>Publish</Button>} />
  <DialogPopup>
    <DialogHeader>
      <DialogTitle>Publish changes</DialogTitle>
      <DialogDescription>Your changes go live right away.</DialogDescription>
    </DialogHeader>
    <DialogBody>11 files changed since the last publish.</DialogBody>
    <DialogFooter>
      <DialogClose render={<Button variant="outline">Cancel</Button>} />
      <DialogClose render={<Button>Publish now</Button>} />
    </DialogFooter>
  </DialogPopup>
</DialogRoot>

Wrap the parts in a form with the class contents, and a Server Action submits from the footer with no client handler.

Every state

5 states

Import from the package

topclassui/dialog
import { DialogRoot, DialogBody, DialogClose, DialogDescription, DialogFooter, DialogHeader, DialogPopup, DialogTitle, DialogTrigger } from 'topclassui/dialog'

Install once:npm i topclassui@beta zod

Examples

  • For a task or a question the page waits for: a form, a confirm, a text to accept.
  • Nine flat parts and no bare Dialog: DialogRoot, DialogTrigger, DialogPopup, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter and DialogClose.
  • DialogBody scrolls and the header and the footer stay put. On a very short screen the whole popup scrolls instead.
  • size on DialogPopup is the widest it grows: 320, 360, 400, 512 or 640 px. custom sets no width.
  • For a confirm, disablePointerDismissal on DialogRoot: a press outside no longer closes it.
  • From a server page pass text, booleans, elements and a Server Action. onOpenChange and any other function need a 'use client' file.
basic
confirm
with-form
close-on-success
long-body
long-content
sizes
no-close-button
non-modal
trap-focus
with-tooltip
In Arabic, right to left

Props

DialogRoot

The props of DialogRoot
PropTypeDefaultLimitsDescription
childrenReactNode--The trigger and the popup. With no trigger, hold open in a client file.

DialogBody

No props of its own: it takes the attributes of the element it renders.

DialogClose

No props of its own: it takes the attributes of the element it renders.

DialogDescription

No props of its own: it takes the attributes of the element it renders.

DialogFooter

No props of its own: it takes the attributes of the element it renders.

DialogHeader

The props of DialogHeader
PropTypeDefaultLimitsDescription
closeButtonbooleantrue-DialogHeader: whether it ends with the built-in close button.

DialogPopup

The props of DialogPopup
PropTypeDefaultLimitsDescription
sizexs | sm | md | lg | xl | custom"md"-DialogPopup: the widest it grows, 320 to 640 px; custom sets no width.

DialogTitle

No props of its own: it takes the attributes of the element it renders.

DialogTrigger

No props of its own: it takes the attributes of the element it renders.

Keyboard and accessibility

The keyboard of Dialog
KeysWhat it does
EnterSpaceOn the trigger, opens the dialog. Focus lands on the x.
TabShift+TabMove through the dialog. Focus stays inside it.
EscapeCloses a tooltip open inside first, then the dialog. Focus returns to the trigger.
  • DialogTitle names the dialog. With no title, pass aria-label to DialogPopup.
  • Remove the x with closeButton={false} only when a DialogClose sits in the footer: a touch screen reader has no Escape key.
  • While DialogBody scrolls it is a tab stop, so the keyboard reaches a long text with no control in it.

Right to left

The x moves to the left, the text aligns right and the actions end at the left. The popup takes its direction from the nearest TopClassProvider.

Server and client

A client component: it renders on the server and hydrates in the browser.

Files: index.tsx, client.tsx.

On the chip: client - size not measured yet.