Modal
The Modal component presents a dialog window over the page that focuses the user's attention on a single task or piece of information.
Use ModalTrigger to wire a trigger element (typically a Button) to a Modal. The Modal exposes high-level slots for title, description, actions, and media, while still accepting arbitrary children for custom content.
Installation
npm install --save @godaddy/antaresProps
The Modal component accepts the following props:
title?ReactNode—Title of the modal.
description?ReactNode—Description of the modal.
overlayProps?RACModalOverlayProps | undefined—Additional props to pass to the overlay container.
containerProps?Omit<FlexProps, "as"> | undefined—Additional props to pass to the modal container.
className?string | undefined'react-aria-Dialog'Additional class name for the modal.
children?ReactNode—Content of the modal.
actions?ReactNode—Actions to display in the modal.
actionProps?FlexProps | undefined—Additional props to pass to the actions container.
media?ReactNode—Media element (e.g. `<img />`) to display in the modal.
mediaProps?FlexProps | undefined—Additional props to pass to the media container.
mediaVariant?"inset" | "full" | undefined—Variant of the media container.
mediaPosition?"start" | "end" | undefined—Position of the media container.
mediaDirection?"column" | "row" | undefined—Direction of the media container.
titleProps?TextProps | undefined—Additional props to pass to the title.
descriptionProps?TextProps | undefined—Additional props to pass to the description.
closeProps?ButtonProps | undefined—Additional props to pass to the close button.
isDismissable?boolean | undefinedtrueWhether the modal can be dismissed via overlay click or Escape key.
centered?boolean | undefined—Whether the modal content is centered.
role?"dialog" | "alertdialog" | undefined'dialog'The accessibility role for the dialog.
id?string | undefined—The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
style?React.CSSProperties | undefined—The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element.
slot?string | null | undefined—A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent.
render?DOMRenderFunction<"section", undefined> | undefined—Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.
Examples
Basic usage
The default modal exposes a title, description, and children content area. Open and close state is managed automatically by ModalTrigger.
import { ModalTrigger, Modal, Button, Flex } from '@godaddy/antares';export function DefaultExample() { return ( <ModalTrigger> <Button variant="primary">Open modal</Button> <Modal title="Modal title" description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther." > <Flex padding="md" elevation="card" justifyContent="center"> This is the children content </Flex> </Modal> </ModalTrigger> );}Controlled
Pass isOpen and onOpenChange to ModalTrigger to control the open state externally.
import { ModalTrigger, Modal, Button, Flex, Text } from '@godaddy/antares';import { useState } from 'react';export function ControlledExample() { const [isOpen, setIsOpen] = useState(false); return ( <Flex direction="column" gap="sm" alignItems="start"> <ModalTrigger isOpen={isOpen} onOpenChange={setIsOpen}> <Button variant="primary">Open modal</Button> <Modal title="Modal title" description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther." actions={[ <Button key="close" slot="close" variant="primary"> Close </Button> ]} /> </ModalTrigger> <Text>The modal is currently {isOpen ? 'open' : 'closed'}.</Text> <Button variant="secondary" onPress={() => setIsOpen(true)}> Open from outside </Button> </Flex> );}With actions
Render footer buttons via the actions prop. Use slot="close" on a button to close the modal automatically when pressed.
import { ModalTrigger, Modal, Button } from '@godaddy/antares';export function WithActionsExample() { return ( <ModalTrigger> <Button variant="primary">Open modal</Button> <Modal title="Modal title" description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther." actions={[ <Button key="cancel" slot="close" variant="secondary"> Cancel </Button>, <Button key="confirm" slot="close" variant="primary"> Confirm </Button> ]} /> </ModalTrigger> );}Alignment
Use centered to center the title and description, and actionProps to control the alignment and direction of the action buttons.
import { ModalTrigger, Modal, Button, RadioGroup, Radio, Flex } from '@godaddy/antares';import { useState } from 'react';export function AlignmentExample() { const [alignment, setAlignment] = useState<string>(); const [actionsAlignment, setActionsAlignment] = useState<string>(); const [actionsDirection, setActionsDirection] = useState<string>(); return ( <Flex direction="column" gap="sm" alignItems="start"> <ModalTrigger> <Button variant="primary">Open modal</Button> <Modal title="Modal title" description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther." centered={alignment === 'center'} actionProps={{ ...(actionsAlignment ? { justifyContent: actionsAlignment } : {}), ...(actionsDirection ? { direction: actionsDirection as 'row' | 'column' } : {}) }} actions={[ <Button key="cancel" slot="close" variant="secondary"> Cancel </Button>, <Button key="confirm" slot="close" variant="primary"> Confirm </Button> ]} /> </ModalTrigger> <RadioGroup orientation="horizontal" label="Modal alignment" value={alignment} onChange={setAlignment}> <Radio value="">Start (Default)</Radio> <Radio value="center">Centered</Radio> </RadioGroup> <RadioGroup orientation="horizontal" label="Actions alignment" value={actionsAlignment} onChange={setActionsAlignment} > <Radio value="start">Start</Radio> <Radio value="center">Centered</Radio> <Radio value="">End (Default)</Radio> </RadioGroup> <RadioGroup orientation="horizontal" label="Actions direction" value={actionsDirection} onChange={setActionsDirection} > <Radio value="row">Row (Default)</Radio> <Radio value="column">Column</Radio> </RadioGroup> </Flex> );}With media
Pass a media element to display imagery alongside the content. Configure its layout with mediaVariant (full or inset), mediaDirection (column or row), and mediaPosition (start or end).
import { ModalTrigger, Modal, Button, RadioGroup, Radio, Flex } from '@godaddy/antares';import { useState } from 'react';export function WithMediaExample() { const [mediaVariant, setMediaVariant] = useState<string>(); const [mediaDirection, setMediaDirection] = useState<string>(); const [mediaPosition, setMediaPosition] = useState<string>(); return ( <Flex direction="column" gap="sm" alignItems="start"> <ModalTrigger> <Button variant="primary">Open modal</Button> <Modal title="Modal title" description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther." media={ <Flex style={{ width: '100%', height: '100%', minWidth: 350, minHeight: 200, backgroundColor: 'teal' }} /> } mediaVariant={mediaVariant as 'inset' | 'full' | undefined} mediaDirection={mediaDirection as 'column' | 'row' | undefined} mediaPosition={mediaPosition as 'start' | 'end' | undefined} actions={[ <Button key="cancel" slot="close" variant="secondary"> Cancel </Button>, <Button key="confirm" slot="close" variant="primary"> Confirm </Button> ]} > <Flex padding="md" elevation="card" justifyContent="center"> This is the children content </Flex> </Modal> </ModalTrigger> <RadioGroup orientation="horizontal" label="Variant" value={mediaVariant} onChange={setMediaVariant}> <Radio value="inset">Inset</Radio> <Radio value="full">Full (Default)</Radio> </RadioGroup> <RadioGroup orientation="horizontal" label="Direction" value={mediaDirection} onChange={setMediaDirection}> <Radio value="row">Row</Radio> <Radio value="column">Column (Default)</Radio> </RadioGroup> <RadioGroup orientation="horizontal" label="Position" value={mediaPosition} onChange={setMediaPosition}> <Radio value="start">Start (Default)</Radio> <Radio value="end">End</Radio> </RadioGroup> </Flex> );}Accessibility
Keyboard interaction
| Key | Action |
|---|---|
Tab | Moves focus to the next focusable element inside the modal |
Shift + Tab | Moves focus to the previous focusable element inside the modal |
Escape | Closes the modal (when dismissable) |
ARIA
role="dialog"is applied automatically by the underlying React AriaDialog- The
titleis rendered as an<h2>and linked to the dialog viaaria-labelledby - The close button is rendered with
aria-label="Close" - Focus is trapped within the modal while open and returned to the trigger on close
Best Practices
- Always provide a
titleso the dialog has an accessible name - Pair every footer action with
slot="close"when it should dismiss the modal - Set
isDismissable={false}for required confirmations the user must explicitly answer - Provide meaningful
alttext for image media, oralt=""if it's purely decorative - Prefer the
actionsprop over manually adding footer buttons inchildrenso they get consistent spacing and alignment
MetricsLockup
The MetricsLockup component displays a single metric with an optional title, info tooltip, trend indicator, and description.
NumberField
NumberField is a numeric input with optional label, description, error message, min/max/step, and increment/decrement stepper buttons. Use it in forms for quantities, amounts, or percentages.