Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsSurfaces

Modal

An accessible dialog overlay built on Base UI. Compound component pattern with Header, Body, Footer, Title, Description, and Close. Five sizes. Five corner-rounding options. LTR and RTL support.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import {
  Modal,
  ModalTrigger,
  ModalClose,
  ModalContent,
  ModalHeader,
  ModalTitle,
  ModalDescription,
  ModalBody,
  ModalFooter,
} from "@tessinaui/ui";
<Modal>
  <ModalTrigger className="...">Open</ModalTrigger>

  <ModalContent>
    <ModalHeader>
      <ModalTitle>Confirm changes</ModalTitle>
      <ModalDescription>This action cannot be undone.</ModalDescription>
    </ModalHeader>

    <ModalBody>
      <p>Additional content goes here.</p>
    </ModalBody>

    <ModalFooter>
      <ModalClose className="...">Cancel</ModalClose>
      <ModalClose className="...">Save</ModalClose>
    </ModalFooter>
  </ModalContent>
</Modal>

Showcase

API Reference

Modal

Root component. Wraps Dialog.Root from Base UI and provides size and dir context to all children.

PropTypeDefaultDescription
size"sm" | "md" | "lg" | "xl" | "full""md"Max-width of the popup panel
width"narrow" | "default" | "wide""default"Additional width constraint — narrow (xs), default (from size), wide (3xl)
dir"ltr" | "rtl""ltr"Text direction — applied to the popup and all its children
openboolean—Controlled open state
defaultOpenbooleanfalseUncontrolled initial open state
onOpenChange(open: boolean) => void—Callback when open state changes
dismissiblebooleantrueWhether clicking the backdrop or pressing Escape closes the modal
modalbooleantrueWhether the dialog uses modal behavior (focus trap, scroll lock)

ModalTrigger

Re-export of Dialog.Trigger. Renders a <button> that opens the modal. Apply styles via className or swap the element via the render prop:

<ModalTrigger render={<Button />}>Open modal</ModalTrigger>

ModalContent

Renders the popup panel inside a Portal. Includes the backdrop automatically.

PropTypeDefaultDescription
size"sm" | "md" | "lg" | "xl" | "full"from <Modal>Overrides the size from context
width"narrow" | "default" | "wide"from <Modal>Overrides the width from context
rounded"full" | "lg" | "md" | "sm" | "none""full"Corner rounding of the panel
classNamestring—Extra classes on the popup

All other native <div> attributes (forwarded to Dialog.Popup) are accepted.

ModalHeader

PropTypeDefaultDescription
iconReact.ReactNode—Optional leading icon beside the title
showClosebooleantrueWhether to render the built-in X close button. Set to false to use a custom <ModalClose>

ModalTitle

Renders Dialog.Title (linked to aria-labelledby). Accepts all <div> attributes plus className.

ModalDescription

Renders Dialog.Description (linked to aria-describedby). Accepts all <p> attributes plus className.

ModalBody

Scrollable content wrapper. Uses flex-1 overflow-y-auto — grows to fill space between header and footer.

ModalFooter

Flex row aligned to the end (justify-end). Accepts className to override layout if needed.

ModalClose

Re-export of Dialog.Close. Renders a <button> that closes the modal. Apply styles via className or swap via render prop:

<ModalClose render={<Button variant="secondary" />}>Cancel</ModalClose>

ModalBackdrop

Pre-styled backdrop. Included inside <ModalContent> by default — only use directly for custom layouts.

Sizes

SizeMax-widthUse case
sm384 pxConfirmations, alerts, short forms
md460 pxStandard dialogs (default)
lg512 pxMedium-complexity forms
xl576 pxRich forms, previews
full100% − 2remFull-screen panels

Animations

The modal uses CSS transitions driven by Base UI's data attributes:

AttributeWhen appliedEffect
data-starting-styleFirst frame of openingopacity 0, scale 0.96
(none)Open steady stateopacity 1, scale 1
data-ending-styleDuring close animationopacity 0, scale 0.96

The backdrop fades in/out independently with transition-opacity.

Controlled vs Uncontrolled

Uncontrolled — use <ModalTrigger> and <ModalClose> for open/close:

<Modal>
  <ModalTrigger className="...">Open</ModalTrigger>
  <ModalContent>
    <ModalFooter>
      <ModalClose className="...">Close</ModalClose>
    </ModalFooter>
  </ModalContent>
</Modal>

Controlled — manage open state yourself:

const [open, setOpen] = useState(false);

<Modal open={open} onOpenChange={setOpen}>
  <ModalTrigger onClick={() => setOpen(true)} className="...">Open</ModalTrigger>
  <ModalContent>
    <ModalFooter>
      <button onClick={() => setOpen(false)}>Cancel</button>
      <button onClick={() => { doSomething(); setOpen(false); }}>Confirm</button>
    </ModalFooter>
  </ModalContent>
</Modal>

Accessibility

  • Renders as role="dialog" with aria-modal="true"
  • <ModalTitle> is linked via aria-labelledby
  • <ModalDescription> is linked via aria-describedby
  • Focus is trapped inside the dialog when open
  • Scroll is locked on <body> when open
  • Escape key closes the modal (unless dismissible={false})
  • Focus returns to the trigger when the modal closes
  • The backdrop provides a click-outside dismiss target

CodeBlock

Syntax-highlighted code display with single-file and multi-tab modes, copy button, line highlighting, and collapsible long blocks — always rendered in a dark palette.

AlertDialog

Blocking confirmation dialog for destructive or critical actions. Built on Base UI's alert-dialog primitive with role="alertdialog", focus trap, and explicit-action-only dismissal — the user must choose Confirm or Cancel.

On this page

PlaygroundInstallationUsageShowcaseAPI ReferenceModalModalTriggerModalContentModalHeaderModalTitleModalDescriptionModalBodyModalFooterModalCloseModalBackdropSizesAnimationsControlled vs UncontrolledAccessibility