Component reference
DialogHeader
The title block of a dialog: an optional eyebrow, the title, a description and actions.
Preview
DialogHeader has no story of its own; this one draws it as part of Dialog.
Keyboard and focus return
Uses a real dialog.showModal. Focus moves into the dialog when it opens, Escape or cancel closes it, and focus returns to the open button.
render: () => (
<div className="workshop-stack workshop-constrained">
<Section
title="One confirmation before finishing"
description="Use Escape or cancel to return; the dangerous action keeps explicit text."
/>
<Button data-open-dialog>Open confirmation dialog</Button>
<Note data-feedback role="status">
The current work is still in progress.
</Note>
<Dialog labelledBy="workshop-dialog-title" describedBy="workshop-dialog-description">
<DialogPanel
header={
<DialogHeader
title="End the current work?"
titleId="workshop-dialog-title"
description="This is a local demo and does not affect real tasks. You can cancel and keep reviewing components."
descriptionId="workshop-dialog-description"
/>
}
>
<DialogActions>
<Button variant="quiet" data-close-dialog="cancel" autofocus>
Cancel
</Button>
<Button variant="danger" data-close-dialog="confirm">
End work
</Button>
</DialogActions>
</DialogPanel>
</Dialog>
</div>
)Installation
Usage
Taken from the examples on this page.
import { DialogHeader } from "meowtail";
<DialogHeader
title="End the current work?"
titleId="workshop-dialog-title"
description="This is a local demo and does not affect real tasks. You can cancel and keep reviewing components."
descriptionId="workshop-dialog-description"
/>Examples
Other stories that draw DialogHeader.
Narrow-screen navigation
The title and close button share a fixed region; the body scrolls independently. After choosing a destination, pressing Escape, or closing via the scrim, focus returns to the trigger button.
render() {
const open = signal(false);
return (
<Stack align="start">
<Button
variant="outline"
onClick={() => {
open.value = true;
}}
>
<Icon name="menu" />
Open navigation drawer
</Button>
<Drawer
label="Main navigation"
open={open}
onOpenChange={(next) => {
open.value = next;
}}
header={
<DialogHeader
title="Meowland"
actions={
<IconButton
label="Close navigation"
onClick={() => {
open.value = false;
}}
>
<Icon name="x" />
</IconButton>
}
/>
}
>
<Stack gap="compact">
<NavItem
label="Chat"
icon={<Icon name="message-square" />}
active
onClick={() => {
open.value = false;
}}
/>
<NavItem
label="Memory and knowledge"
icon={<Icon name="file-text" />}
onClick={() => {
open.value = false;
}}
/>
<NavItem
label="Services"
icon={<Icon name="network" />}
onClick={() => {
open.value = false;
}}
/>
<NavItem
label="Messaging"
icon={<Icon name="message-square" />}
onClick={() => {
open.value = false;
}}
/>
<NavItem
label="Reminders"
icon={<Icon name="list-checks" />}
badge={
<Tag kind="count" tone="warning">
1
</Tag>
}
onClick={() => {
open.value = false;
}}
/>
{Array.from({ length: 8 }, (_, i) => (
<NavItem
label={`Authorized sample extension ${i + 1}`}
icon={<Icon name="file-text" />}
onClick={() => {
open.value = false;
}}
/>
))}
<span>
<StatusDot state="success" /> Runtime connected
</span>
<NavItem
label="Settings"
icon={<Icon name="settings" />}
onClick={() => {
open.value = false;
}}
/>
</Stack>
</Drawer>
</Stack>
);
}Fixed header and body scrolling
Short forms keep their natural height; long bodies and host-constrained windows share the same page shell. Forms still use native submit and cancel.
render() {
return (
<Specimen
label="Open a dialog"
note="Each dialog keeps its title and close action fixed while the body scrolls."
>
<Inline gap="compact">
{(["short", "long", "limited", "workspace"] as const).map((kind) => {
let dialog: HTMLDialogElement | undefined;
const name = {
short: "Short form",
long: "Long body",
limited: "Limited height",
workspace: "Full workspace",
}[kind];
return (
<div>
<Button
variant="outline"
size={kind === "limited" ? "small" : "default"}
onClick={() => {
dialog?.showModal();
}}
>
Open {name}
</Button>
<Dialog
labelledBy={`${kind}-title`}
style={
kind === "limited"
? { maxHeight: "40svh" }
: kind === "workspace"
? { height: "100dvh", maxHeight: "none" }
: undefined
}
ref={(element: HTMLDialogElement) => {
dialog = element;
}}
>
<DialogPanel
as="form"
method="dialog"
header={
<DialogHeader
title={`${name} · Current item and close action`}
titleId={`${kind}-title`}
actions={
<IconButton
label={`Close ${name}`}
onClick={() => {
dialog?.close("cancel");
}}
>
<Icon name="x" />
</IconButton>
}
/>
}
>
<Stack gap="compact">
<Field label={`${name} draft`} htmlFor={`${kind}-draft`}>
<Input
id={`${kind}-draft`}
name="draft"
value="Keep the current input"
required
/>
</Field>
{kind !== "short" ? (
Array.from({ length: 24 }, (_, i) => (
<p>
Body {i + 1}: Reading content, editing the draft, and closing the window
stay independent; the title and close entry always belong to the same
top region.
</p>
))
) : (
<p>A short form uses only the height its content needs.</p>
)}
</Stack>
<Button
type="submit"
value="save"
size={kind === "limited" ? "small" : "default"}
>
Save {name}
</Button>
</DialogPanel>
</Dialog>
</div>
);
})}
</Inline>
</Specimen>
);
}API reference
Props
| Prop | Type | Default |
|---|---|---|
titleRequired | Child | – |
eyebrow | Child | – |
description | Child | – |
actions | Child | – |
titleId | string | – |
descriptionId | string | – |
Also takes className, children and the attributes and events of its root element.
DOM parts
Registry
The component is registered as dialog-header in the meowtail component registry.