Modal
A modal is an overlay dialog that focuses the user’s attention on a specific task or content.
A Modal is a focused container that appears above the page to display essential, interruptive, or task-related content without navigating away from the current view. It temporarily disables interaction with the underlying interface and requires the user to complete an action before returning to the previous context.
When to use it (and when not to)
Section titled “When to use it (and when not to)”Use when:
- The user must take an action before continuing (e.g. confirm, acknowledge, complete a task).
- You need to display content that requires full focus without distractions.
- The interaction must not be missed (e.g. legal notices, blocking states).
Avoid when:
- The information is optional, lightweight, or can be shown inline or via a Popover/Tooltip.
- The interruption breaks the user flow unnecessarily.
- A non-blocking alternative (sheet, inline block, banner) would provide a smoother experience.
Quick summary of accessibility & consistency
Section titled “Quick summary of accessibility & consistency”- The Modal must trap focus until it is closed (keyboard and assistive technologies).
- Focus must move to the Modal container on open, and return to the triggering element on close.
- Provide a clear and visible close action.
- Ensure all content inside the Modal is reachable and announced in a logical order.
- Maintain consistent spacing, elevation, and layout across breakpoints.
- On mobile, Modals are replaced by a Bottom Sheet to ensure touch accessibility and ergonomic interactions.
Properties
Section titled “Properties”Anatomy : Full Screen
Section titled “Anatomy : Full Screen”color-background-neutral-default#ffffffcolor-background-neutral-default#ffffffAnatomy : With Image
Section titled “Anatomy : With Image”color-background-neutral-default#ffffffcolor-background-neutral-default#ffffffAnatomy : Without Image
Section titled “Anatomy : Without Image”color-background-neutral-default#ffffffcolor-background-neutral-default#ffffffBehaviour
Section titled “Behaviour”The Modal is built using a small set of structural elements. All buttons inside the Modal — including the Close Button and all action buttons — inherit their behaviour and specifications from the Button Main component.
- Close Button (mandatory) — Always required to ensure a reliable, accessible way to dismiss the Modal. Uses the Button Main component (LG size with the “Close” icon). Optional behaviours such as dismissing by tapping outside the Modal are not defined at component level and must not replace the Close Button.
- Image (optional, 16:9 or 9:16) — An optional image can be placed at the top of the Modal. Recommended ratio: 16:9 or 9:16, sourced from the Image component to maintain consistency and responsive behaviour. Optional short text may be placed over the image when required by the content. For more details, refer to the Image component.
- Content Slot (mandatory) — The content slot is flexible and can hold different types of content such as text, images or simple forms. It does not impose its own copy rules. Any element placed inside the slot must follow the guidelines of its own component (for example Button Main, Link, Image or Form elements). The modal only provides the container and overall behaviour.
- Action Area — All action buttons inside the Modal (Primary, Secondary, Tertiary, Link) use the Button or Link Button components. Only the layout changes depending on viewport:
- Horizontal Actions (Desktop & Tablet) — Buttons appear side-by-side following Button spacing and hierarchy rules. Supports any combination of Primary, Secondary, Tertiary and Link buttons. Refer to the Button and Link Button documentation for behaviour, states, and accessibility.
- Vertical Actions (Desktop, Tablet & Mobile) — Buttons are stacked vertically to ensure optimal tap targets on smaller screens. Automatically used in Mobile viewports. May also be used on larger viewports when clarity or long labels require it.
- Link (optional) — A Link Button may be used as a secondary or tertiary action. Follows all behaviour, spacing, and accessibility rules defined in the Link Button component.
- Backdrop (mandatory) — The Modal appears above a semi-transparent Backdrop that blocks interaction with underlying content and ensures focus isolation. For tokens (colour, opacity, elevation) refer to the Backdrop component documentation.
Transitions
Section titled “Transitions”The Modal uses a light fade-in/out animation to provide a smooth appearance and reduce abrupt visual changes.
Default transition: transition: opacity .5s ease-out;
The Modal supports Toyota and Lexus brand modes. The structural layout, behaviour, and spacing remain identical — only brand-specific tokens (colours, typography) are applied through the design token system.
Composition
Section titled “Composition”Layout & Spacing : Desktop, Full Screen
Section titled “Layout & Spacing : Desktop, Full Screen”Content
Styling
Section titled “Styling”Colors
Section titled “Colors”| Name | Applied as | Applied to |
|---|---|---|
color-background-neutral-default | Background color | Modal |
color-background-neutral-default | Background color | Modal |
color-surface-dim-neutral-default | Background color | Backdrop |
color-surface-dim-neutral-default | Background color | Backdrop |
color-foreground-contrast-emphasis | Text color | Title |
color-foreground-contrast-emphasis | Text color | Title |
Text Styles
Section titled “Text Styles”| Name | Applied as | Applied to |
|---|---|---|
Lorem Ipsum typography-title-7 | Text style | Title |
Lorem Ipsum typography-title-7 | Text style | Title |
- Use a Modal when the user must focus on a single task or piece of information.
- Always keep the Close Button visible and accessible.
- Avoid using a Modal for long or complex content. If the content becomes too long, consider using a dedicated page instead.
- Do not stack multiple Modals.
Responsive Behaviour
Section titled “Responsive Behaviour”The Modal adapts to each viewport to ensure usability and accessibility:
- Desktop & Tablet — The component appears as a centred dialog with a backdrop.
- Mobile — The same Modal automatically adopts a bottom-sheet layout (sliding up from the bottom). This layout improves touch interaction, keeps key actions reachable, and follows mobile accessibility conventions.
Both views share the same structure, slot, close button behaviour, and action sets. Only the layout changes.
Styles
Section titled “Styles”A modal is an overlay dialog that focuses the user’s attention on a specific task or content.
<div class="tng-modal"> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> <span>Close</span> <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-modal-content"> <div class="tng-slot is-primary"></div> </div> </div></div>Fullscreen
Section titled “Fullscreen”You can use the positioning utilities to create a fullscreen modal.
<div class="tng-modal | p-absolute at-maximum"> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> <span>Close</span> <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-modal-content"> <div class="tng-slot is-primary"></div> </div> </div></div>With Scroll
Section titled “With Scroll”When a modal contains potentially more content than fits in the viewport, you can use the overflow utility to make the content area scrollable.
<div class="tng-modal" style="max-block-size: 300px"> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> Close <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-overflow-scroll is-block"> <div class="tng-modal-content"> <p class="tng-text-body">…</p> <p class="tng-text-body">…</p> </div> </div> </div></div>With Image
Section titled “With Image”<div class="tng-modal"> <div class="tng-frame"> <div class="tng-slot"></div> </div> <div class="tng-modal-panel"> <button class="tng-button is-tertiary is-ghost"> Close <i class="tng-icon icon-close" aria-hidden="true"></i> </button> <div class="tng-modal-content"> <div class="tng-slot is-primary"></div> </div> </div></div>Recipes
Section titled “Recipes”Modal Dialog
Section titled “Modal Dialog”Import the Modal component from @tmedxp/react-components.
Properties
Section titled “Properties”ModalProperties extends <PropsWithChildren<Omit<ComponentProps<'dialog'>, 'open'>>>.
| Prop | Type | Description | Optional |
|---|---|---|---|
instance | ModalInstance | The modal instance used to open / close | ❌ |
position | ModalPosition | Position: 'ABSOLUTE' | 'FIXED' | 'RELATIVE' | ✅ |
placement | ModalPlacement | Placement: centered, top, bottom, etc. | ✅ |
labels | ModalLabels | Collection of labels used by the modal | ❌ |
frameContent | React.ReactElement | Content of tng-frame container if framed | ✅ |
Example without frame
Section titled “Example without frame”import { Modal, ModalPlacement, ModalPosition, useModalInstance, Button,} from '@tmedxp/react-components';
const modalInstance = useModalInstance();const ModalExample = () => { return ( <> <Button buttonType="button" buttonSize="md" buttonStyle="primary" onClick={modalInstance.open} text="Open modal overlay" /> <Modal instance={modalInstance} position={ModalPosition.ABSOLUTE} placement={ModalPlacement.CENTER} labels={{ Close: 'Close overlay' }} > <p class="tng-text-body"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p> </Modal> </> );};
export { ModalExample };Example with frame
Section titled “Example with frame”import { Modal, ModalPlacement, ModalPosition, useModalInstance, Button,} from '@tmedxp/react-components';
const modalInstance = useModalInstance();const frameContent = <div className="tng-slot"></div>;const ModalExample = () => { return ( <> <Button buttonType="button" buttonSize="md" buttonStyle="primary" onClick={modalInstance.open} text="Open framed modal overlay" /> <Modal instance={modalInstance} position={ModalPosition.ABSOLUTE} placement={ModalPlacement.CENTER} labels={{ Close: 'Close overlay' }} frameContent={frameContent} > <p class="tng-text-body"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p> </Modal> </> );};
export { ModalExample };useDialog hook
Section titled “useDialog hook”Pairs with Modal (and with Backdrop’s Overlay) to manage open/close state.
Properties
Section titled “Properties”id— a string referencing the dialog.isModal—trueto show as a modal.onSubmit— invoked when the dialog is closing.
Returned API
Section titled “Returned API”reference— the dialog’s DOM reference.properties— the dialog’s properties.softClose()— close the dialog.openOverlay()— open the dialog.
Example
Section titled “Example”const dialog = useDialog({ id: 'overlay', isModal: true, onSubmit: () => alert('The dialog has been closed'),});
dialog.openOverlay();
dialog.softClose();For designers
Section titled “For designers”- The Modal must trap focus while it is open, keeping keyboard and assistive technology users inside the dialog.
- Focus must return to the trigger once the Modal is closed.
- The first focusable element should be the Close Button.
- The Modal must announce itself to screen readers as a dialog using the correct accessibility attributes (for example
role="dialog"andaria-modal="true"). - Content inside the slot must follow the accessibility rules of each component placed inside it.
For developers
Section titled “For developers”The <dialog> element
Section titled “The <dialog> element”Always render a modal on a <dialog> element and open it with showModal(). This gives you built-in browser behaviour that is difficult to replicate manually:
- Promotes the dialog to the top layer — no
z-indexconflicts. - Renders a
::backdroppseudo-element automatically. - Traps focus inside the dialog while it is open.
- Returns focus to the previously focused element on close.
- Closes on Escape by default.
Roles & attributes
Section titled “Roles & attributes”| Attribute | Purpose |
|---|---|
aria-labelledby | Point to the modal’s visible title so assistive technologies announce it. |
aria-describedby | Optionally point to a description paragraph for additional context. |
aria-controls | Set on the trigger button, referencing the dialog’s id. |
aria-modal="true" | Implied automatically when using showModal(). |
Inert background
Section titled “Inert background”When a modal is open the rest of the page must be unreachable. showModal() handles this for the accessibility tree, but pointer events on the underlying page can still leak through. Set document.body.inert = true while the dialog is open and reset it on close, as shown in the Modal Dialog recipe.
Keyboard interaction
Section titled “Keyboard interaction”| Key | Action |
|---|---|
| Tab / Shift+Tab | Cycles focus through focusable elements inside the dialog (focus trap is built in with showModal()). |
| Escape | Closes the dialog (built-in). Listen for the close event to run cleanup. |
Closing with a form
Section titled “Closing with a form”Wrap the dialog content in a <form method="dialog">. Any <button> inside the form that is not type="button" will close the dialog and its value will be available via dialog.returnValue. This keeps close / confirm actions accessible without custom JavaScript.
Focus Order
Section titled “Focus Order”The Modal follows a consistent focus-trap pattern across all devices:
Desktop & Portable Devices (keyboard navigation)
- Initial focus moves to the first interactive element: the Close button (always present). This ensures a predictable and accessible starting point regardless of the content inserted in the slot.
- Tab / Shift+Tab cycle through all interactive elements inside the Modal in this order:
- Close button
- Interactive elements inside the content slot (if any)
- Action buttons (horizontal or vertical set — optional)
- Link (optional)
- Focus cannot escape the Modal until it is closed.
- When closing, focus returns to the element that triggered the Modal.
Mobile & Tablet (touch + assistive tech)
- Mobile users do not navigate with a physical Tab key, but assistive technologies (VoiceOver, TalkBack, Switch Control) follow the same logical focus sequence defined for desktop.
- The focus-trap still applies:
- Screen readers move through the elements in the same order: Close → Slot content → Action buttons (optional) → Link (optional).
- Users cannot reach content outside the Modal until it is closed.
Universal rule — Closing the Modal always returns accessibility focus to the element that opened it.