Components
Modal
Modals are used to overlay content above an interface. They are intended to capture the user's attention in order to inform or shift focus to a pertinent task.
Modal V2 provides comprehensive accessibility features, keyboard navigation, and focus management out of the box. It supports both controlled and uncontrolled state modes, and includes responsive bottom sheet layout on mobile devices.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Assign Chatbot"><ModalHeadertitle="Assign Chatbot"subtitle="The chatbot will respond to customers from this profile."/><ModalBody><Text fontSize={300} color="text.body">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Loremipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmodtempor incididunt ut labore et dolore magna aliqua. Lorem ipsumdolor sit amet, consectetur adipiscing elit, sed do eiusmod temporincididunt ut labore et dolore magna aliqua.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Go back</Button>}primaryButton={<Button appearance="primary" px={500}>Submit</Button>}/></Modal></Box>)}
Uncontrolled Modal (recommended)
Prefer the uncontrolled pattern whenever the trigger can live near the modal in the component tree. Pass your trigger element to the modalTrigger prop and Modal handles everything else — ARIA attributes, focus management, and open/close state — automatically. No extra hooks or refs required.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Example Modal"><ModalHeadertitle="Modal Title"subtitle="Trigger and accessibility are handled automatically."/><ModalBody><Text fontSize={300} color="text.body">The trigger button's ARIA attributes (aria-haspopup, aria-expanded,aria-controls) and focus restoration are managed by the Modalcomponent with no additional setup needed.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Confirm</Button>}/></Modal></Box>)}
Controlled Modal
When the trigger cannot live near the modal — for example, a button in a table row, list item, or page header — use the controlled pattern with open and onOpenChange. You are responsible for applying ARIA attributes to the trigger and restoring focus when the modal closes. Use one of the utilities below to handle this correctly.
useModalExternalTrigger (recommended)
The useModalExternalTrigger hook is the recommended approach for controlled modals. It provides:
triggerRef— attach to the trigger element so focus returns to it when the modal closestriggerProps(isOpen)— returnsaria-haspopup,aria-expanded, andaria-controlsto spread onto the triggeronCloseAutoFocus— pass to Modal to automatically restore focus to the trigger on close
import { Modal, ModalHeader, ModalBody, ModalFooter, useModalExternalTrigger } from '@sproutsocial/racine/modal/v2'() => {const [isOpen, setIsOpen] = useState(false);const { triggerRef, triggerProps, onCloseAutoFocus } = useModalExternalTrigger();return (<Box><Buttonref={triggerRef}{...triggerProps(isOpen)}appearance="primary"onClick={() => setIsOpen(true)}>Open modal</Button><Modalopen={isOpen}onOpenChange={setIsOpen}onCloseAutoFocus={onCloseAutoFocus}aria-label="Controlled Modal"><ModalHeadertitle="Controlled Modal"subtitle="Trigger and modal are in separate parts of the component tree."/><ModalBody><Text fontSize={300} color="text.body">The trigger button above is outside the Modal component tree but hasthe correct ARIA attributes applied. When this modal closes, focusautomatically returns to the trigger.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Confirm</Button>}/></Modal></Box>)}
ModalExternalTrigger component
When the trigger is specifically a Seeds Button, ModalExternalTrigger provides the same ARIA attributes without needing to spread hook props. Focus restoration still requires passing onCloseAutoFocus manually.
import { Modal, ModalHeader, ModalBody, ModalFooter, ModalExternalTrigger } from '@sproutsocial/racine/modal/v2'() => {const [isOpen, setIsOpen] = useState(false);const triggerRef = useRef(null);return (<Box><ModalExternalTriggerref={triggerRef}isOpen={isOpen}onTrigger={() => setIsOpen(true)}appearance="primary">Open modal</ModalExternalTrigger><Modalopen={isOpen}onOpenChange={setIsOpen}onCloseAutoFocus={(e) => {e.preventDefault();triggerRef.current?.focus();}}aria-label="Controlled Modal"><ModalHeadertitle="Controlled Modal"subtitle="Using ModalExternalTrigger component."/><ModalBody><Text fontSize={300} color="text.body">ModalExternalTrigger is a Seeds Button pre-configured witharia-haspopup, aria-expanded, and aria-controls attributes.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Confirm</Button>}/></Modal></Box>)}
useModalTriggerProps (low-level)
If the trigger is not a Seeds Button (e.g., a custom component or anchor element), use useModalTriggerProps to get just the ARIA attributes. You are responsible for attaching a ref and implementing onCloseAutoFocus manually.
import { Modal, ModalHeader, ModalBody, ModalFooter, useModalTriggerProps } from '@sproutsocial/racine/modal/v2'() => {const [isOpen, setIsOpen] = useState(false);const triggerRef = useRef(null);const triggerProps = useModalTriggerProps(isOpen);return (<Box><Buttonref={triggerRef}{...triggerProps}appearance="primary"onClick={() => setIsOpen(true)}>Open modal</Button><Modalopen={isOpen}onOpenChange={setIsOpen}onCloseAutoFocus={(e) => {e.preventDefault();triggerRef.current?.focus();}}aria-label="Controlled Modal"><ModalHeader title="Controlled Modal" /><ModalBody><Text fontSize={300} color="text.body">useModalTriggerProps returns only the ARIA attributes. Use thiswhen you need to apply them to a non-Button trigger element.</Text></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close</Button>}/></Modal></Box>)}
Modal Components
Modal V2 consists of several components that work together:
Modal (Root Component)
The main Modal component that wraps all modal content. It handles accessibility, focus management, and state.
Note: This component extends Radix UI Dialog primitives to provide accessible dialog functionality.
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
children | ReactNode | Modal content. Typically includes ModalHeader, ModalBody, and ModalFooter components. | ||
modalTrigger | ReactElement | React element (typically a Button) that triggers the modal when clicked. Used for uncontrolled modals. | ||
open | boolean | Controls whether the modal is open (controlled mode). Use with onOpenChange. | ||
defaultOpen | boolean | Default open state for uncontrolled mode. Use with modalTrigger. | ||
onOpenChange | (open: boolean) => void | Callback fired when the open state changes. Receives the new open state. | ||
aria-label | string | Accessible label for the modal dialog. Required for screen readers. | ||
title | string | Modal title. Automatically creates a ModalHeader if provided. | ||
subtitle | string | Modal subtitle. Automatically creates a ModalHeader if provided. | ||
description | string | Modal description. Automatically wrapped in ModalDescription for accessibility. | ||
draggable | boolean | Enable draggable functionality. When true, the modal header becomes a drag handle. | ||
showOverlay | boolean | Whether to show the background overlay. Defaults to true. | ||
actions | Array<ModalActionConfig> | Array of action configurations for the floating action rail. | ||
closeButtonAriaLabel | string | Accessible label for the close button. Defaults to 'Close'. | ||
data | Record<string, any> | Additional data attributes to pass to the modal content. | ||
onEscapeKeyDown | (e: KeyboardEvent) => void | Callback fired when the Escape key is pressed while the modal is open. Call `e.preventDefault()` to prevent the modal from closing. | ||
onInteractOutside | (e: PointerEvent) => void | Callback fired when the user interacts outside the modal (overlay click). Call `e.preventDefault()` to prevent the modal from closing. Not available when `draggable` is true. | ||
onPointerDownOutside | (e: PointerEvent) => void | Callback fired on pointer down events outside the modal. | ||
onFocusOutside | (e: FocusEvent) => void | Callback fired when focus moves outside the modal. | ||
onOpenAutoFocus | (e: Event) => void | Callback fired when the modal opens and focus is about to move into it. Call `e.preventDefault()` to prevent auto-focus. | ||
onCloseAutoFocus | (e: Event) => void | Callback fired when the modal closes and focus is about to restore. Call `e.preventDefault()` to handle focus restoration manually. | ||
disableEscapeKeyClose | boolean | Prevents the modal from closing when pressing the Escape key. The `onEscapeKeyDown` handler will still fire if provided. | ||
disableOutsideClickClose | boolean | Prevents the modal from closing when clicking outside. The `onInteractOutside` handler will still fire if provided. Not available when `draggable` is true. | ||
closeButtonProps | object | Props to customize the close button including `onClick`, `aria-label`, `id`, `disabled`, and other button HTML attributes. | ||
zIndex | number | Controls the z-index CSS property. Defaults to 6 to match Modal V1. |
ModalHeader
Renders the modal's header with title and optional subtitle. Automatically created when title or subtitle props are provided to the root Modal component.
Note: This component extends Radix UI Dialog.Title and Dialog.Description primitives for accessible header content. For custom header layouts with complex content or actions, use ModalCustomHeader instead.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with header</Button>}aria-label="Example Modal"><ModalHeadertitle="Modal Title"subtitle="This is a subtitle that provides additional context"/><ModalBody><Text fontSize={300} color="text.body">Modal content goes here. The header above was created using the ModalHeader component.</Text></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close</Button>}/></Modal></Box>)}
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
title | string | Modal title text displayed as a headline | ||
subtitle | string | Modal subtitle text.
Automatically wrapped in Dialog.Description for accessibility. | ||
titleProps | Omit<
React.ComponentPropsWithoutRef<typeof Dialog.Title>,
"asChild" | "children"
> | Additional props for Dialog.Title when title is provided | ||
subtitleProps | Omit<
React.ComponentPropsWithoutRef<typeof Dialog.Description>,
"asChild" | "children"
> | Additional props for Dialog.Description when subtitle is provided |
ModalBody
Renders the scrollable main content area of the modal between the header and footer.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with body content</Button>}aria-label="Example Modal"><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body" mb={400}>This is the modal body content. It can contain any content you need.</Text><Box bg="neutral.200" p={400} borderRadius={400}><Text fontSize={200} color="text.body">ModalBody accepts all Box props for styling, so you can customizethe layout, spacing, and appearance as needed.</Text></Box></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close</Button>}/></Modal></Box>)}
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
children | React.ReactNode | The main content of the modal body |
ModalFooter
Provides automatic button wrapping and layout management. Buttons are automatically wrapped in ModalCloseWrapper to close the modal when clicked.
Note: For custom footer layouts with informational text or complex layouts, use ModalCustomFooter instead.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with footer</Button>}aria-label="Example Modal"><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body">Modal content goes here.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Save</Button>}/></Modal></Box>)}
Footer with left action:
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with left action</Button>}aria-label="Delete Item"><ModalHeader title="Delete Item" /><ModalBody><Text fontSize={300} color="text.body">Are you sure you want to delete this item?</Text></ModalBody><ModalFooterleftAction={<Button appearance="destructive">Delete</Button>}cancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Save</Button>}/></Modal></Box>)}
Async primary action:
By default, both cancelButton and primaryButton automatically close the modal when clicked. For async operations like saving data, set closeOnPrimaryAction={false} to keep the modal open until the operation completes. The consumer is then responsible for closing the modal programmatically via controlled state.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {const [isOpen, setIsOpen] = useState(false);const [isSaving, setIsSaving] = useState(false);const handleSave = async () => {setIsSaving(true);// Simulate async operationawait new Promise((resolve) => setTimeout(resolve, 1500));setIsSaving(false);setIsOpen(false);};return (<Box><Button appearance="primary" onClick={() => setIsOpen(true)}>Open modal</Button><Modalopen={isOpen}onOpenChange={setIsOpen}aria-label="Async save example"><ModalHeadertitle="Save changes"subtitle="The modal stays open until the save completes."/><ModalBody><Text fontSize={300} color="text.body">Click Save to trigger an async operation. The modal will remainopen until the operation completes.</Text></ModalBody><ModalFootercloseOnPrimaryAction={false}cancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Buttonappearance="primary"onClick={handleSave}disabled={isSaving}>{isSaving ? "Saving..." : "Save"}</Button>}/></Modal></Box>)}
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
closeOnPrimaryAction | boolean | Whether clicking the primary button automatically closes the modal.
Set to `false` when the primary action is async (e.g., saving data)
and the modal should stay open until the operation completes.
@default true |
ModalCloseWrapper
Wraps elements (typically buttons) to automatically close the modal when clicked. This component is primarily used for custom implementations, such as when creating a custom ModalFooter or custom header/footer layouts. ModalFooter automatically wraps its buttons in ModalCloseWrapper, so you typically only need to use this component directly when building custom footer or header implementations.
Note: This component extends Radix UI Dialog.Close primitive to provide accessible close functionality.
import { Modal, ModalHeader, ModalBody, ModalCloseWrapper, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with custom close wrapper</Button>}aria-label="Example Modal"><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body" mb={400}>This modal uses ModalCloseWrapper to wrap custom buttons that close the modal.</Text><Box display="flex" gap={300}><ModalCloseWrapper><Button appearance="unstyled">Custom Close Button</Button></ModalCloseWrapper><ModalCloseWrapper><Button appearance="secondary">Another Close Button</Button></ModalCloseWrapper></Box></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Save</Button>}/></Modal></Box>)}
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
children | React.ReactNode | The element to wrap with close functionality | ||
onClick | (e: React.MouseEvent) => void | Optional click handler called before closing the modal | ||
asChild | boolean | Whether to merge props into the child element (default: true) |
ModalCloseWrapper does not accept system props. It only accepts children, onClick, and asChild props.
Actions Prop
The actions prop on the Modal component allows you to add quick action buttons to a floating rail alongside the modal. These actions appear in a vertical rail next to the modal (horizontal on mobile) and provide quick access to common actions like close, expand, etc.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with custom actions</Button>}aria-label="Example Modal"actions={[{actionType: "button",iconName: "arrows-pointing-out","aria-label": "Expand modal",onClick: () => console.log("Expand clicked"),},]}><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body">This modal includes custom actions in the floating rail. The close buttonis automatically included, and we've added an expand action using the `actions` prop.</Text></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close</Button>}/></Modal></Box>)}
Action Configuration:
Each action in the actions array accepts:
actionType:"close"(automatically closes modal) or"button"(default, requiresonClick)iconName: Icon name from the Seeds icon setaria-label: Required accessible label for the actiononClick: Optional click handler (ignored foractionType: "close")
Customizing the Close Button
The default floating close button can be customized in two ways:
Simple customization - Use closeButtonAriaLabel to customize just the accessible label:
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}closeButtonAriaLabel="Close this dialog"aria-label="Example Modal"><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body">The close button has a custom aria-label.</Text></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close</Button>}/></Modal></Box>)}
Advanced customization - Use closeButtonProps to customize the close button with additional props like onClick, id, disabled, className, etc:
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {const [clickCount, setClickCount] = useState(0);return (<Box><Text fontSize={200} color="text.body" mb={400}>Close button clicked {clickCount} times</Text><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Example Modal"closeButtonProps={{"aria-label": "Close this dialog",onClick: () => {console.log("Close button clicked!");setClickCount(prev => prev + 1);},id: "custom-close-btn",}}><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body">The close button has custom onClick, id, and aria-label props.Click the close button to see the counter increment.</Text></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close</Button>}/></Modal></Box>)}
Note: You must provide an accessible label through either closeButtonAriaLabel or closeButtonProps["aria-label"]. TypeScript will enforce this requirement.
ModalCustomHeader
For custom headers with complex layouts or actions, use ModalCustomHeader instead of the simple title/subtitle props.
import { Modal, ModalCustomHeader, ModalBody, ModalCloseWrapper } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with custom header</Button>}aria-label="Example Modal"><ModalCustomHeader><Box display="flex" alignItems="center" justifyContent="space-between" width={1}><Box><Text fontSize={400} fontWeight="bold" color="text.headline">Custom Header</Text><Text fontSize={200} color="text.subtext">With custom layout and actions</Text></Box><Box display="flex" gap={300}><ModalCloseWrapper><Button appearance="unstyled">Cancel</Button></ModalCloseWrapper><ModalCloseWrapper><Button appearance="primary">Save</Button></ModalCloseWrapper></Box></Box></ModalCustomHeader><ModalBody><Text fontSize={300} color="text.body">Modal content goes here.</Text></ModalBody></Modal></Box>)}
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
children | ReactNode | Content to render inside the custom header | ||
draggable | boolean | Internal prop for draggable functionality (automatically set when Modal is draggable) | ||
isDragging | boolean | Internal prop indicating if modal is currently being dragged |
ModalCustomFooter
For custom footers with informational text or complex layouts, use ModalCustomFooter instead of the standard ModalFooter component.
import { Modal, ModalHeader, ModalBody, ModalCustomFooter, ModalCloseWrapper } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal with custom footer</Button>}aria-label="Example Modal"><ModalHeader title="Example Modal" /><ModalBody><Text fontSize={300} color="text.body">Modal content goes here.</Text></ModalBody><ModalCustomFooterdisplay="flex"alignItems="center"justifyContent="space-between"><Text fontSize={200} color="text.subtext">This is some information you may need to know!</Text><Box display="flex" gap={300}><ModalCloseWrapper><Button appearance="unstyled">Cancel</Button></ModalCloseWrapper><ModalCloseWrapper><Button appearance="primary">Save</Button></ModalCloseWrapper></Box></ModalCustomFooter></Modal></Box>)}
| Name | Type | Default | Description | Required? |
|---|---|---|---|---|
children | ReactNode | Content to render inside the custom footer |
Handling close events
Modal V2 provides granular event callbacks that let you distinguish why a modal was closed. This is useful for analytics tracking, conditional close prevention, or triggering different behaviors based on the user's action.
| Close reason | Callback | Can prevent close? |
|---|---|---|
| Escape key | onEscapeKeyDown | Yes (e.preventDefault()) |
| Overlay / outside click | onInteractOutside | Yes (e.preventDefault()) |
| Close button (X) | closeButtonProps.onClick | No |
| Any state change | onOpenChange | No |
The specific event callbacks (onEscapeKeyDown, onInteractOutside, closeButtonProps.onClick) fire before onOpenChange, so you can identify the close reason before the state updates.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {const [isOpen, setIsOpen] = useState(false);const [lastCloseReason, setLastCloseReason] = useState("none");return (<Box><Box display="flex" flexDirection="column" alignItems="flex-start" gap={300}><Text fontSize={200} color="text.body">Last close reason: <strong>{lastCloseReason}</strong></Text><Button appearance="primary" onClick={() => setIsOpen(true)}>Open modal</Button></Box><Modalopen={isOpen}onOpenChange={setIsOpen}aria-label="Close event tracking example"onEscapeKeyDown={() => {setLastCloseReason("escape");}}onInteractOutside={() => {setLastCloseReason("overlay_click");}}closeButtonProps={{"aria-label": "Close modal",onClick: () => {setLastCloseReason("close_button");},}}><ModalHeadertitle="Close event tracking"subtitle="Try closing this modal different ways and check the label above."/><ModalBody><Text fontSize={300} color="text.body">Close this modal by pressing Escape, clicking the overlay, orclicking the close button to see different close reasons tracked.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Done</Button>}/></Modal></Box>)}
Preventing close
You can prevent the modal from closing on specific interactions by calling e.preventDefault() in the event handler, or by using the convenience boolean props.
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Prevent close example"disableEscapeKeyClosedisableOutsideClickClose><ModalHeadertitle="Protected modal"subtitle="This modal can only be closed via the close button or footer buttons."/><ModalBody><Text fontSize={300} color="text.body">Pressing Escape or clicking the overlay will not close this modal.Use the close button or footer buttons instead.</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled">Cancel</Button>}primaryButton={<Button appearance="primary">Confirm</Button>}/></Modal></Box>)}
The same behavior can be achieved with event handlers for more control:
<Modal onEscapeKeyDown={(e) => { e.preventDefault(); // prevent close // custom logic here }} onInteractOutside={(e) => { e.preventDefault(); // prevent close // custom logic here }}>
Recipes
Modal with no header
Modal headers can be omitted when the first item in the modal body is an image or illustration. If the modal body begins with text, a header with a title or subtitle should be used.
The following example shows a modal without a header, which displays only a close button in the floating action rail.
import { Modal, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Example Modal"><ModalBody><Boxmb={500}display="flex"alignItems="center"flexDirection="column"><Box width="400px" height="400px" bg="neutral.200" /><TextfontSize={400}fontWeight="bold"color="text.headline"pt={600}>Some headline</Text><TextfontSize={300}textAlign="center"color="text.body"pt={450}px={500}>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed doeiusmod tempor incididunt ut labore et dolore magna aliqua. Loremipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmodtempor incididunt ut labore et dolore magna aliqua.</Text></Box></ModalBody><ModalFooterprimaryButton={<Button appearance="primary">Close modal</Button>}/></Modal></Box>)}
Expressive modal
import { Modal, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Example Modal"bg="blue.400"><ModalBody><Imagealt="expressive illustration"mb={600}src={withPrefix('/illus.svg')}/><Boxwidth="100%"bg="neutral.0"p={600}display="flex"flexDirection="column"><TextfontSize={400}color="text.headline"fontWeight={700}textAlign="center"pb={400}>Engagement makes the world go 'round.</Text><Text fontSize={200} textAlign="center" color="text.body">We care about how you engage with your customers and we're here tomake that experience as smooth and seamless as possible.</Text></Box></ModalBody><ModalFooterprimaryButton={<Button appearance="primary" minWidth={120}>Thanks</Button>}/></Modal></Box>)}
Destructive confirmation
import { Modal, ModalHeader, ModalBody, ModalFooter } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Delete profiles confirmation"><ModalHeadertitle="Delete profiles?"subtitle="This action can't be undone."/><ModalBody><Text as='div' py={300} fontSize={300} color="text.body">Deleting this profile will remove it from all groups as well asremove any messages associated with this profile. Are you sure youwant to proceed?</Text></ModalBody><ModalFootercancelButton={<Button appearance="unstyled" mr={400}>Cancel</Button>}primaryButton={<Button appearance="destructive" minWidth="120px">Confirm</Button>}/></Modal></Box>)}
Free form modal
This example showcases the freedom and flexibility we have within ModalBody. For custom headers and footers, use ModalCustomHeader and ModalCustomFooter components.
import { Modal, ModalCustomHeader, ModalBody, ModalCloseWrapper } from '@sproutsocial/racine/modal/v2'() => {return (<Box><ModalmodalTrigger={<Button appearance="primary">Open modal</Button>}aria-label="Free form experience"><ModalCustomHeader><Boxdisplay="flex"alignItems="center"justifyContent="space-between"width={1}><Box display="flex" flexDirection="column"><Text fontSize={400} fontWeight="bold" color="text.headline">Free form experience</Text><Text fontSize={200} color="text.subtext">Browse and select things from the list as you please, nothing isrequired.</Text></Box><Box display="flex"><ModalCloseWrapper><Button mr={350} appearance="unstyled">Cancel</Button></ModalCloseWrapper><ModalCloseWrapper><Button minWidth="120px" appearance="primary">Complete</Button></ModalCloseWrapper></Box></Box></ModalCustomHeader><ModalBody display="flex"><Box width={1 / 2} display="flex" flexDirection="column" pr={450}><TextfontWeight={700}fontSize={300}color="text.headline"mb={400}>An informational headline</Text><Text fontSize={200} color="text.body" mb={350}>Authoritatively integrate installed base deliverables withoutworldwide intellectual capital. Progressively promote functionalmarkets before mission-critical potentialities.</Text><Text fontSize={200} color="text.body" mb={350}>Assertively incentivize 2.0 communities with quality technologies.Globally benchmark accurate sources for go forward systems.Energistically develop out-of-the-box ideas and quality services.</Text></Box><Box width={1 / 2}><ModalListExample /></Box></ModalBody></Modal></Box>)}