Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,14 @@
}

.Description {
margin: 0 0 1.5rem;
margin: 1.5rem 0;
font-size: 1rem;
line-height: 1.5rem;
color: var(--color-gray-600);

& a {
text-decoration: underline;
}
}

.Actions {
Expand All @@ -109,3 +113,48 @@
display: flex;
gap: 8px;
}

.Fieldset {
border: 0;
margin: 0;
margin-top: 1rem;
padding: 0;
display: flex;
flex-direction: column;
gap: 1rem;
width: 100%;
max-width: 16rem;
}

.Field {
display: flex;
flex-direction: column;
align-items: start;
gap: 0.25rem;
}

.Label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}

.Input {
box-sizing: border-box;
padding-left: 0.875rem;
margin: 0;
border: 1px solid var(--color-gray-200);
width: 100%;
height: 2.5rem;
border-radius: 0.375rem;
font-family: inherit;
font-size: 1rem;
background-color: transparent;
color: var(--color-gray-900);

&:focus {
outline: 2px solid var(--color-blue);
outline-offset: -1px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client';
import * as React from 'react';
import { Dialog } from '@base-ui-components/react/dialog';
import { Fieldset } from '@base-ui-components/react/fieldset';
import { Field } from '@base-ui-components/react/field';
import styles from '../../_index.module.css';

export default function ExampleDialog() {
const initialFocusRef = React.useRef<HTMLInputElement | null>(null);
const finalFocusRef = React.useRef<HTMLButtonElement | null>(null);
return (
<div className={styles.Container}>
<Dialog.Root>
<Dialog.Trigger className={styles.Button}>Open dialog</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Backdrop className={styles.Backdrop} />
<Dialog.Popup
className={styles.Popup}
initialFocus={initialFocusRef}
finalFocus={finalFocusRef}
>
<Dialog.Title className={styles.Title}>Feedback form</Dialog.Title>
<Dialog.Description render={<div />} className={styles.Description}>
<p>
You feedback means a lot to us! See how we process this data <a href="#">here</a>.
</p>
<Fieldset.Root className={styles.Fieldset}>
<Field.Root className={styles.Field}>
<Field.Label className={styles.Label}>Full name (optional)</Field.Label>
<Field.Control
ref={initialFocusRef}
placeholder="Enter your name"
className={styles.Input}
/>
</Field.Root>
<Field.Root className={styles.Field}>
<Field.Label className={styles.Label}>Your feedback *</Field.Label>
<Field.Control
required
placeholder="Enter your feedback"
className={styles.Input}
/>
</Field.Root>
</Fieldset.Root>
</Dialog.Description>
<div className={styles.Actions}>
<Dialog.Close className={styles.Button}>Close</Dialog.Close>
</div>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
<button ref={finalFocusRef} type="button" className={styles.Button}>
Final focus
</button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createDemoWithVariants } from 'docs/src/utils/createDemo';
import CssModules from './css-modules';
import Tailwind from './tailwind';

export const DemoDialogFocusManagement = createDemoWithVariants(import.meta.url, {
CssModules,
Tailwind,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';
import * as React from 'react';
import { Dialog } from '@base-ui-components/react/dialog';
import { Fieldset } from '@base-ui-components/react/fieldset';
import { Field } from '@base-ui-components/react/field';

export default function ExampleDialog() {
const initialFocusRef = React.useRef<HTMLInputElement | null>(null);
const finalFocusRef = React.useRef<HTMLButtonElement | null>(null);
return (
<div className="flex gap-2">
<Dialog.Root>
<Dialog.Trigger className="flex h-10 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3.5 text-base font-medium text-gray-900 select-none hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-blue-800 active:bg-gray-100">
Open dialog
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Backdrop className="fixed inset-0 min-h-dvh bg-black opacity-20 transition-all duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 dark:opacity-70 supports-[-webkit-touch-callout:none]:absolute" />
<Dialog.Popup
initialFocus={initialFocusRef}
finalFocus={finalFocusRef}
className="fixed top-1/2 left-1/2 -mt-8 w-96 max-w-[calc(100vw-3rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg bg-gray-50 p-6 text-gray-900 outline outline-1 outline-gray-200 transition-all duration-150 data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[starting-style]:scale-90 data-[starting-style]:opacity-0 dark:outline-gray-300"
>
<Dialog.Title className="-mt-1.5 mb-1 text-lg font-medium">Feedback form</Dialog.Title>
<Dialog.Description render={<div />} className="mt-6 mb-6 text-base text-gray-600">
<p>
You feedback means a lot to us! See how we process this data <a href="#" className='underline'>here</a>.
</p>
<Fieldset.Root className="flex w-full max-w-64 flex-col gap-4 mt-[1rem]">
<Field.Root className="flex flex-col items-start gap-1">
<Field.Label className="text-sm font-medium text-gray-900">
Full name (optional)
</Field.Label>
<Field.Control
ref={initialFocusRef}
placeholder="Enter your name"
className="h-10 w-full rounded-md border border-gray-200 pl-3.5 text-base text-gray-900 focus:outline focus:outline-2 focus:-outline-offset-1 focus:outline-blue-800"
/>
</Field.Root>
<Field.Root className="flex flex-col items-start gap-1">
<Field.Label className="text-sm font-medium text-gray-900">
Your feedback *
</Field.Label>
<Field.Control
required
placeholder="Enter your feedback"
className="h-10 w-full rounded-md border border-gray-200 pl-3.5 text-base text-gray-900 focus:outline focus:outline-2 focus:-outline-offset-1 focus:outline-blue-800"
/>
</Field.Root>
</Fieldset.Root>
</Dialog.Description>
<div className="flex justify-end gap-4">
<Dialog.Close className="flex h-10 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3.5 text-base font-medium text-gray-900 select-none hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-blue-800 active:bg-gray-100">
Close
</Dialog.Close>
</div>
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
<button
ref={finalFocusRef}
type="button"
className="flex h-10 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3.5 text-base font-medium text-gray-900 select-none hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-blue-800 active:bg-gray-100"
>
Final focus
</button>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ import { DemoDialogCloseConfirmation } from './demos/close-confirmation';

<DemoDialogCloseConfirmation compact />

### Custom focus management

You can control where the focus goes when the dialog opens and close using the `initialFocus` and `finalFocus` props on the `<Dialog.Popup>` component.

import { DemoDialogFocusManagement } from './demos/focus-management';

<DemoDialogFocusManagement compact />

You can also set these props to `false` if you don't want to move the focus anywhere after opening/closing the dialog, or a function for specifying the target focusable element ref dynamically.

### Outside scroll dialog

The dialog can be made scrollable by using `<Dialog.Viewport>` as an outer scrollable container for `<Dialog.Popup>` while the popup can extend past the bottom edge. The scrollable area uses the [Scroll Area component](/react/components/scroll-area) to provide custom scrollbars.
Expand Down
Loading