Modal is a panel-shaped content surface for a single, self-contained message: a title, optional supporting copy or a short list of points, one piece of leading media, and a row of actions.import { Button, ButtonGroup, Modal, Page } from '@wearables-ui-toolkit/mrbd';
export function PermissionScreen({ onAllow, onDismiss }: PermissionScreenProps) {
return (
<Page>
<Modal
title="Allow notifications?"
subtitle="Notifications keep playback controls available while you are in another app."
icon={notificationsIcon}
buttons={
<ButtonGroup>
<Button title="Not now" onActivate={onDismiss} />
<Button title="Allow" onActivate={onAllow} />
</ButtonGroup>
}
/>
</Page>
);
}
import {
Button,
ButtonGroup,
Modal,
ModalBannerSize,
ModalBannerTag,
} from '@wearables-ui-toolkit/mrbd';
<Modal
title="Live translation"
subtitle="Translate what you hear, in the moment."
bannerSrc="/media/translation-banner.webp"
bannerAlt="Two people talking with translated captions between them"
bannerSize={ModalBannerSize.TALLER}
bannerTag={ModalBannerTag.BETA}
buttons={
<ButtonGroup>
<Button title="Try it" onActivate={startTranslation} />
</ButtonGroup>
}
/>;
listItems replaces subtitle, so pass one or the other:import { IconTintColor, Modal, type ModalListItem } from '@wearables-ui-toolkit/mrbd';
const capabilities: ModalListItem[] = [
{ icon: cameraIcon, description: 'Capture photos and video' },
{ icon: micIcon, description: 'Record audio' },
{ icon: lockIcon, description: 'Stored on device', iconColor: IconTintColor.POSITIVE },
];
<Modal title="What this app can do" listItems={capabilities} />;
import { Modal, ModalContentMode } from '@wearables-ui-toolkit/mrbd';
<Modal title="Hold to talk" contentMode={ModalContentMode.TITLE_ONLY} />;
import { Carousel, Modal } from '@wearables-ui-toolkit/mrbd';
<Carousel onItemCentered={setStep}>
<Modal isCarouselItem title="Look up" subtitle="Wake the display with a glance." />
<Modal isCarouselItem title="Swipe" subtitle="Move between cards with the trackpad." />
</Carousel>;
ModalProps. It extends the non-interactive Panel surface, minus children, clickable, focusable, onClick, and pressable — the modal manages its own focus and click behavior and takes its content through the slots below.| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Primary line. Hidden when omitted or empty. |
subtitle | string | — | Supporting copy. Hidden in title-only mode and whenever listItems are supplied. |
contentMode | ModalContentMode | ModalContentMode.STANDARD | Selects the standard presentation or the compact title-only presentation. |
icon | IconSource | — | Leading icon. Lowest priority of the four leading treatments. |
bannerSrc | string | — | Banner image URL. Highest priority of the four leading treatments. |
bannerAlt | string | 'Banner image' | Alternative text passed to the banner image fallback. The complete leading area is decorative and hidden from assistive technology. |
bannerSize | ModalBannerSize | ModalBannerSize.STANDARD | Banner aspect ratio. |
bannerTag | ModalBannerTag | ModalBannerTag.NONE | Tag overlaid on the bottom of the banner. Ignored without a banner. |
logoSrc | string | — | Logo image URL, rendered in a circular Avatar at the large size. |
logoAlt | string | 'Logo' | Alternative text passed to the logo avatar. The complete leading area is decorative and hidden from assistive technology. |
avatarSrc | string | — | Primary avatar image URL, rendered in an Avatar at the xxlarge size. |
avatarPrimaryContent | ReactNode | — | Content for the primary avatar slot. Overrides avatarSrc; the avatar owns the slot size. |
avatarAlt | string | 'Avatar' | Alternative text passed to the avatar. The complete leading area is decorative and hidden from assistive technology. |
avatarSecondarySrc | string | — | Second image, which switches the avatar into its duo layout. |
avatarSecondaryContent | ReactNode | — | Content for the secondary avatar slot. Also switches the avatar into its duo layout, and overrides avatarSecondarySrc. |
avatarBadgeSrc | string | — | Image for the avatar’s image-badge slot. |
avatarBadgeContent | ReactNode | — | Content for the avatar’s image-badge slot. Overrides avatarBadgeSrc. |
statusIndicator | StatusIndicatorType | — | Status dot drawn on the avatar. Takes over the badge slot, so it replaces avatarBadgeSrc and avatarBadgeContent. |
statusIndicatorIcon | IconSource | — | Glyph drawn over the status dot. Only renders when statusIndicator is also set. |
primaryImageShape | AvatarShape | — | Shape of the primary avatar image. |
placeholderStyle | PlaceholderStyle | — | Placeholder treatment used when an avatar image is missing. |
listItems | ModalListItem[] | — | Icon-and-description rows shown instead of the subtitle. Ignored in title-only mode. |
buttons | ReactNode | — | Actions slot below the content, normally a ButtonGroup. |
isCarouselItem | boolean | false | Makes the modal focusable so focus can drive carousel selection. |
className | string | '' | Extra class on the modal panel. |
style | CSSProperties | {} | Extra inline styles on the modal panel. |
| Prop | Type | Default | Description |
|---|---|---|---|
shapeProvider | ShapeProvider | rounded rectangle at the medium corner radius | Geometry used for the panel’s clip path and every material path. |
width | number \| string | — | Explicit width. Leave it unset; the modal already fills the width it is given. |
height | number \| string | — | Explicit height. Leave it unset so the modal sizes to its content. |
aria-label | string | the title, subtitle, and banner tag joined with commas | Accessible name for the panel. |
onStateChange | (prev: InteractionState, next: InteractionState) => void | — | Fires when the interaction state changes. Only meaningful while the modal is focusable. |
div attributes are forwarded to the panel element, and the component forwards its ref there too. See Panel and Container for the rest of the inherited surface.ModalContentMode| Member | Description |
|---|---|
STANDARD | Full presentation. Title in the heading appearance, with the leading area, subtitle or list, and actions all available. |
TITLE_ONLY | Compact presentation. Title in the emphasized body appearance; the leading area, subtitle, and list are suppressed. Actions still render. |
| Member | Description |
|---|---|
STANDARD | Banner at the 440:204 aspect ratio. |
TALLER | Banner at the 440:300 aspect ratio. |
| Member | Description |
|---|---|
NONE | No tag. |
BETA | Draws the design system’s “Beta” Tag over the bottom of the banner. |
ModalListItem| Field | Type | Description |
|---|---|---|
icon | IconSource | Required leading glyph for the row. |
description | string | Required row text. |
iconColor | IconTintColor | Optional semantic tint for the glyph. Free-form CSS colors are not accepted. |
bannerSrc wins, then logoSrc, then avatarSrc or avatarPrimaryContent, then icon. Supplying several is not an error — the lower-priority ones simply do not render.TITLE_ONLY suppresses the whole leading area, the subtitle, and the list, and switches the title to the emphasized body appearance. The standard mode reserves a single-line minimum height for the title so stacked modals stay visually aligned.listItems always wins.bannerTag is not NONE, over a gradient scrim pinned to the bottom of the banner.isCarouselItem is set or when the subtitle overflows. Press feedback is disabled, so a focusable modal never enters the pressed state. The controls inside buttons are the real focus stops, and focus navigation moves between them like any other controls on the route.role="dialog", and no aria-modal. Escape is left unhandled so the surrounding route can act on it — the one exception is while the read-more surface is open, where Escape closes that surface and goes no further.aria-label to replace it when the visible copy does not read well out of context.*Alt values do not contribute to the modal’s accessible name, so repeat any meaning carried by the media in the title, subtitle, or list.role="dialog", aria-modal="true", is labelled with the complete subtitle, receives focus when it opens, and closes on Escape.buttons supplies its own accessible name. Keep those labels explicit (“Delete photo”, not “OK”).useBackNavigation (see Navigation and Focus) rather than adding an in-app back button to the modal.title, and choose either subtitle or listItems.buttons as a ButtonGroup so they share the group’s alignment and focus handling.isCarouselItem on every modal used as a Carousel item.IconTintColor case when the glyph carries status meaning.Modal as an overlay presenter. It paints no scrim, does not trap focus, and ignores Escape and system Back, so a dedicated route or separate presenter must own those behaviors.subtitle or listItems; when both are supplied, both do not appear.bannerSrc, logoSrc, avatarSrc, and icon for the leading treatment.listItems informational and place interactive controls in buttons.Toast for brief confirmation of an action that already succeeded.