or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

button-typography-components.mdcore-components.mddata-display-components.mdfeedback-components.mdform-components.mdindex.mdlayout-components.mdnavigation-components.mdoverlay-components.mdtheme-system.md

overlay-components.mddocs/

0

# Overlay Components

1

2

@mantine/core provides comprehensive overlay components for creating modal dialogs, popovers, tooltips, and other floating elements. These components handle complex positioning, focus management, and accessibility requirements.

3

4

## Modal

5

6

Full-featured modal dialog component with backdrop, transitions, and accessibility.

7

8

```typescript { .api }

9

interface ModalProps {

10

/** If true, modal is opened */

11

opened: boolean;

12

/** Called when modal should be closed */

13

onClose: () => void;

14

/** Modal title */

15

title?: React.ReactNode;

16

/** Modal content */

17

children?: React.ReactNode;

18

/** Modal size */

19

size?: MantineSize | string | number;

20

/** If true, modal will be centered */

21

centered?: boolean;

22

/** If true, modal cannot be closed by clicking outside or pressing Escape */

23

closeOnClickOutside?: boolean;

24

/** If true, modal cannot be closed by pressing Escape */

25

closeOnEscape?: boolean;

26

/** If true, close button will not be rendered */

27

withCloseButton?: boolean;

28

/** Modal z-index */

29

zIndex?: number;

30

/** Overlay props */

31

overlayProps?: OverlayProps;

32

/** Transition props */

33

transitionProps?: TransitionProps;

34

/** Modal radius */

35

radius?: MantineRadius;

36

/** If true, modal will have full screen on mobile */

37

fullScreen?: boolean;

38

/** Modal keep mounted */

39

keepMounted?: boolean;

40

}

41

42

interface ModalRootProps {

43

/** Root children */

44

children: React.ReactNode;

45

/** If true, modal is opened */

46

opened: boolean;

47

/** Called when modal should be closed */

48

onClose: () => void;

49

/** Modal z-index */

50

zIndex?: number;

51

/** If true, modal cannot be closed by clicking outside */

52

closeOnClickOutside?: boolean;

53

/** If true, modal cannot be closed by pressing Escape */

54

closeOnEscape?: boolean;

55

/** If true, modal will be centered */

56

centered?: boolean;

57

/** Transition props */

58

transitionProps?: TransitionProps;

59

/** If true, modal will have full screen on mobile */

60

fullScreen?: boolean;

61

}

62

63

interface ModalOverlayProps {

64

/** Overlay background color */

65

color?: string;

66

/** Overlay background opacity */

67

backgroundOpacity?: number;

68

/** Overlay blur */

69

blur?: number;

70

}

71

72

interface ModalContentProps {

73

/** Content children */

74

children: React.ReactNode;

75

/** Modal size */

76

size?: MantineSize | string | number;

77

/** Modal radius */

78

radius?: MantineRadius;

79

}

80

81

interface ModalHeaderProps {

82

/** Header children */

83

children: React.ReactNode;

84

}

85

86

interface ModalTitleProps {

87

/** Title content */

88

children: React.ReactNode;

89

}

90

91

interface ModalCloseButtonProps {

92

/** Close button aria-label */

93

'aria-label'?: string;

94

}

95

96

interface ModalBodyProps {

97

/** Body children */

98

children: React.ReactNode;

99

}

100

```

101

102

**Usage Example:**

103

104

```tsx

105

import { Modal, Button, TextInput, Group } from '@mantine/core';

106

import { useDisclosure } from '@mantine/hooks';

107

108

function ModalDemo() {

109

const [opened, { open, close }] = useDisclosure(false);

110

111

return (

112

<>

113

<Modal opened={opened} onClose={close} title="Authentication">

114

<TextInput label="Email" placeholder="your@email.com" />

115

<TextInput label="Password" type="password" mt="md" />

116

<Group mt="md" justify="flex-end">

117

<Button onClick={close}>Login</Button>

118

</Group>

119

</Modal>

120

121

<Button onClick={open}>Open modal</Button>

122

</>

123

);

124

}

125

126

// Compound components usage

127

function CompoundModal() {

128

const [opened, { open, close }] = useDisclosure(false);

129

130

return (

131

<>

132

<Modal.Root opened={opened} onClose={close}>

133

<Modal.Overlay />

134

<Modal.Content>

135

<Modal.Header>

136

<Modal.Title>Modal title</Modal.Title>

137

<Modal.CloseButton />

138

</Modal.Header>

139

<Modal.Body>

140

Modal content

141

</Modal.Body>

142

</Modal.Content>

143

</Modal.Root>

144

145

<Button onClick={open}>Open modal</Button>

146

</>

147

);

148

}

149

```

150

151

## ModalBase

152

153

Unstyled base modal implementation for custom modal components.

154

155

```typescript { .api }

156

interface ModalBaseProps {

157

/** If true, modal is opened */

158

opened: boolean;

159

/** Called when modal should be closed */

160

onClose: () => void;

161

/** Modal content */

162

children?: React.ReactNode;

163

/** Modal size */

164

size?: MantineSize | string | number;

165

/** If true, modal will be centered */

166

centered?: boolean;

167

/** If true, modal cannot be closed by clicking outside */

168

closeOnClickOutside?: boolean;

169

/** If true, modal cannot be closed by pressing Escape */

170

closeOnEscape?: boolean;

171

/** Modal z-index */

172

zIndex?: number;

173

/** Overlay props */

174

overlayProps?: OverlayProps;

175

/** Transition props */

176

transitionProps?: TransitionProps;

177

/** If true, modal will have full screen on mobile */

178

fullScreen?: boolean;

179

}

180

```

181

182

## Drawer

183

184

Slide-out drawer component for navigation or content panels.

185

186

```typescript { .api }

187

interface DrawerProps {

188

/** If true, drawer is opened */

189

opened: boolean;

190

/** Called when drawer should be closed */

191

onClose: () => void;

192

/** Drawer title */

193

title?: React.ReactNode;

194

/** Drawer content */

195

children?: React.ReactNode;

196

/** Drawer position */

197

position?: 'top' | 'bottom' | 'left' | 'right';

198

/** Drawer size */

199

size?: MantineSize | string | number;

200

/** If true, drawer cannot be closed by clicking outside */

201

closeOnClickOutside?: boolean;

202

/** If true, drawer cannot be closed by pressing Escape */

203

closeOnEscape?: boolean;

204

/** If true, close button will not be rendered */

205

withCloseButton?: boolean;

206

/** Drawer z-index */

207

zIndex?: number;

208

/** Overlay props */

209

overlayProps?: OverlayProps;

210

/** Transition props */

211

transitionProps?: TransitionProps;

212

/** If true, drawer will have scroll area */

213

scrollAreaComponent?: any;

214

}

215

216

interface DrawerRootProps extends Pick<DrawerProps, 'opened' | 'onClose' | 'position' | 'size' | 'closeOnClickOutside' | 'closeOnEscape' | 'zIndex' | 'transitionProps'> {

217

/** Root children */

218

children: React.ReactNode;

219

}

220

221

interface DrawerOverlayProps extends ModalOverlayProps {}

222

223

interface DrawerContentProps {

224

/** Content children */

225

children: React.ReactNode;

226

}

227

228

interface DrawerHeaderProps {

229

/** Header children */

230

children: React.ReactNode;

231

}

232

233

interface DrawerTitleProps {

234

/** Title content */

235

children: React.ReactNode;

236

}

237

238

interface DrawerCloseButtonProps {

239

/** Close button aria-label */

240

'aria-label'?: string;

241

}

242

243

interface DrawerBodyProps {

244

/** Body children */

245

children: React.ReactNode;

246

}

247

```

248

249

**Usage Example:**

250

251

```tsx

252

import { Drawer, Button, Group } from '@mantine/core';

253

import { useDisclosure } from '@mantine/hooks';

254

255

function DrawerDemo() {

256

const [opened, { open, close }] = useDisclosure(false);

257

258

return (

259

<>

260

<Drawer opened={opened} onClose={close} title="Authentication">

261

<p>Drawer content...</p>

262

<Group mt="xl">

263

<Button variant="outline" onClick={close}>

264

Cancel

265

</Button>

266

<Button onClick={close}>

267

Save

268

</Button>

269

</Group>

270

</Drawer>

271

272

<Button onClick={open}>Open Drawer</Button>

273

</>

274

);

275

}

276

277

// Different positions

278

function DrawerPositions() {

279

return (

280

<>

281

<Drawer position="left" opened={opened} onClose={close}>

282

Left drawer

283

</Drawer>

284

<Drawer position="right" opened={opened} onClose={close}>

285

Right drawer

286

</Drawer>

287

<Drawer position="top" opened={opened} onClose={close}>

288

Top drawer

289

</Drawer>

290

<Drawer position="bottom" opened={opened} onClose={close}>

291

Bottom drawer

292

</Drawer>

293

</>

294

);

295

}

296

```

297

298

## Popover

299

300

Floating popover component with positioning and trigger options.

301

302

```typescript { .api }

303

interface PopoverProps {

304

/** Popover content */

305

children: React.ReactNode;

306

/** If true, popover is opened */

307

opened?: boolean;

308

/** Default opened state */

309

defaultOpened?: boolean;

310

/** Called when popover state changes */

311

onChange?: (opened: boolean) => void;

312

/** Popover position relative to target */

313

position?: FloatingPosition;

314

/** Popover placement offset */

315

offset?: number;

316

/** If true, popover will be closed on outside click */

317

closeOnClickOutside?: boolean;

318

/** If true, popover will be closed on escape key press */

319

closeOnEscape?: boolean;

320

/** If true, popover will be closed when target element is clicked */

321

clickOutsideEvents?: string[];

322

/** Popover width */

323

width?: number | 'target';

324

/** If true, popover will have arrow */

325

withArrow?: boolean;

326

/** Arrow size */

327

arrowSize?: number;

328

/** Arrow position */

329

arrowPosition?: 'center' | 'side';

330

/** Arrow offset */

331

arrowOffset?: number;

332

/** If true, popover will be disabled */

333

disabled?: boolean;

334

/** Popover z-index */

335

zIndex?: number;

336

/** Popover radius */

337

radius?: MantineRadius;

338

/** Popover shadow */

339

shadow?: MantineShadow;

340

/** If true, popover will have focus trap */

341

trapFocus?: boolean;

342

/** If true, popover will return focus to trigger when closed */

343

returnFocus?: boolean;

344

}

345

346

interface PopoverTargetProps {

347

/** Target element */

348

children: React.ReactElement;

349

/** Popover reference type */

350

refProp?: string;

351

}

352

353

interface PopoverDropdownProps {

354

/** Dropdown content */

355

children: React.ReactNode;

356

}

357

```

358

359

**Usage Example:**

360

361

```tsx

362

import { Popover, Button, Text } from '@mantine/core';

363

364

function PopoverDemo() {

365

return (

366

<Popover width={200} position="bottom" withArrow shadow="md">

367

<Popover.Target>

368

<Button>Toggle popover</Button>

369

</Popover.Target>

370

<Popover.Dropdown>

371

<Text size="sm">This is uncontrolled popover</Text>

372

</Popover.Dropdown>

373

</Popover>

374

);

375

}

376

377

// Controlled popover

378

function ControlledPopover() {

379

const [opened, setOpened] = useState(false);

380

381

return (

382

<Popover opened={opened} onChange={setOpened}>

383

<Popover.Target>

384

<Button onClick={() => setOpened((o) => !o)}>

385

Toggle popover

386

</Button>

387

</Popover.Target>

388

<Popover.Dropdown>

389

<Text size="sm">Controlled popover content</Text>

390

</Popover.Dropdown>

391

</Popover>

392

);

393

}

394

```

395

396

## HoverCard

397

398

Popover that opens on hover with delay support.

399

400

```typescript { .api }

401

interface HoverCardProps extends Omit<PopoverProps, 'opened' | 'onChange'> {

402

/** Open delay in ms */

403

openDelay?: number;

404

/** Close delay in ms */

405

closeDelay?: number;

406

/** Called when hover card opens */

407

onOpen?: () => void;

408

/** Called when hover card closes */

409

onClose?: () => void;

410

}

411

412

interface HoverCardTargetProps {

413

/** Target element */

414

children: React.ReactElement;

415

}

416

417

interface HoverCardDropdownProps {

418

/** Dropdown content */

419

children: React.ReactNode;

420

}

421

```

422

423

**Usage Example:**

424

425

```tsx

426

import { HoverCard, Button, Text, Avatar, Group } from '@mantine/core';

427

428

function HoverCardDemo() {

429

return (

430

<Group justify="center">

431

<HoverCard width={280} shadow="md">

432

<HoverCard.Target>

433

<Avatar

434

src="https://images.unsplash.com/photo-1623582854588-d60de57fa33f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80"

435

radius="xl"

436

/>

437

</HoverCard.Target>

438

<HoverCard.Dropdown>

439

<div>

440

<Avatar

441

src="https://images.unsplash.com/photo-1623582854588-d60de57fa33f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80"

442

size={94}

443

radius="md"

444

/>

445

<Text fz="sm" tt="uppercase" fw={700} c="dimmed">

446

Software engineer

447

</Text>

448

449

<Text fz="lg" fw={500} mt={-5}>

450

Jane Doe

451

</Text>

452

453

<Text fz="sm" c="dimmed">

454

Jane.Doe@email.com β€’ +47 333 22 11

455

</Text>

456

</div>

457

</HoverCard.Dropdown>

458

</HoverCard>

459

</Group>

460

);

461

}

462

```

463

464

## Tooltip

465

466

Simple tooltip component for providing additional information on hover.

467

468

```typescript { .api }

469

interface TooltipProps {

470

/** Tooltip label */

471

label: React.ReactNode;

472

/** Tooltip children (trigger element) */

473

children: React.ReactNode;

474

/** Tooltip position */

475

position?: FloatingPosition;

476

/** If true, tooltip will be disabled */

477

disabled?: boolean;

478

/** Tooltip offset from target */

479

offset?: number;

480

/** Tooltip z-index */

481

zIndex?: number;

482

/** Tooltip radius */

483

radius?: MantineRadius;

484

/** Tooltip color */

485

color?: MantineColor;

486

/** If true, tooltip will have arrow */

487

withArrow?: boolean;

488

/** Arrow size */

489

arrowSize?: number;

490

/** Arrow position */

491

arrowPosition?: 'center' | 'side';

492

/** Arrow offset */

493

arrowOffset?: number;

494

/** Open delay in ms */

495

openDelay?: number;

496

/** Close delay in ms */

497

closeDelay?: number;

498

/** If true, tooltip will stay open when hovered */

499

keepMounted?: boolean;

500

/** Events that trigger tooltip */

501

events?: { hover: boolean; focus: boolean; touch: boolean };

502

}

503

504

interface TooltipFloatingProps extends TooltipProps {}

505

506

interface TooltipGroupProps {

507

/** Group children */

508

children: React.ReactNode;

509

/** Open delay for all tooltips in group */

510

openDelay?: number;

511

/** Close delay for all tooltips in group */

512

closeDelay?: number;

513

}

514

```

515

516

**Usage Example:**

517

518

```tsx

519

import { Tooltip, Button, Group } from '@mantine/core';

520

521

function TooltipDemo() {

522

return (

523

<Group>

524

<Tooltip label="This is tooltip">

525

<Button variant="outline">Button with tooltip</Button>

526

</Tooltip>

527

528

<Tooltip label="Colored tooltip" color="orange">

529

<Button variant="outline">Orange tooltip</Button>

530

</Tooltip>

531

532

<Tooltip

533

label="Tooltip with arrow"

534

withArrow

535

position="bottom"

536

>

537

<Button variant="outline">With arrow</Button>

538

</Tooltip>

539

</Group>

540

);

541

}

542

543

// Tooltip group

544

function TooltipGroupDemo() {

545

return (

546

<Tooltip.Group openDelay={500} closeDelay={100}>

547

<Group>

548

<Tooltip label="Tooltip 1">

549

<Button variant="outline">Button 1</Button>

550

</Tooltip>

551

<Tooltip label="Tooltip 2">

552

<Button variant="outline">Button 2</Button>

553

</Tooltip>

554

<Tooltip label="Tooltip 3">

555

<Button variant="outline">Button 3</Button>

556

</Tooltip>

557

</Group>

558

</Tooltip.Group>

559

);

560

}

561

```

562

563

## Menu

564

565

Context menu component with items, labels, and dividers.

566

567

```typescript { .api }

568

interface MenuProps {

569

/** Menu content */

570

children: React.ReactNode;

571

/** If true, menu is opened */

572

opened?: boolean;

573

/** Default opened state */

574

defaultOpened?: boolean;

575

/** Called when menu state changes */

576

onChange?: (opened: boolean) => void;

577

/** Menu position */

578

position?: FloatingPosition;

579

/** Menu offset */

580

offset?: number;

581

/** Menu trigger */

582

trigger?: 'click' | 'hover';

583

/** Open delay for hover trigger */

584

openDelay?: number;

585

/** Close delay for hover trigger */

586

closeDelay?: number;

587

/** If true, menu will be closed on item click */

588

closeOnItemClick?: boolean;

589

/** If true, menu will be closed on outside click */

590

closeOnClickOutside?: boolean;

591

/** If true, menu will be closed on escape */

592

closeOnEscape?: boolean;

593

/** Menu width */

594

width?: number | 'target';

595

/** If true, menu will have arrow */

596

withArrow?: boolean;

597

/** Arrow size */

598

arrowSize?: number;

599

/** Arrow position */

600

arrowPosition?: 'center' | 'side';

601

/** Arrow offset */

602

arrowOffset?: number;

603

/** Menu z-index */

604

zIndex?: number;

605

/** Menu radius */

606

radius?: MantineRadius;

607

/** Menu shadow */

608

shadow?: MantineShadow;

609

/** If true, menu will have focus trap */

610

trapFocus?: boolean;

611

/** If true, menu will return focus to trigger */

612

returnFocus?: boolean;

613

/** Keyboard events */

614

loop?: boolean;

615

}

616

617

interface MenuTargetProps {

618

/** Target element */

619

children: React.ReactElement;

620

}

621

622

interface MenuDropdownProps {

623

/** Dropdown content */

624

children: React.ReactNode;

625

}

626

627

interface MenuItemProps {

628

/** Item content */

629

children: React.ReactNode;

630

/** Item left section */

631

leftSection?: React.ReactNode;

632

/** Item right section */

633

rightSection?: React.ReactNode;

634

/** Item color when hovered/focused */

635

color?: MantineColor;

636

/** If true, item is disabled */

637

disabled?: boolean;

638

/** Called when item is clicked */

639

onClick?: () => void;

640

}

641

642

interface MenuLabelProps {

643

/** Label content */

644

children: React.ReactNode;

645

}

646

647

interface MenuDividerProps {

648

/** Divider label */

649

label?: React.ReactNode;

650

}

651

```

652

653

**Usage Example:**

654

655

```tsx

656

import { Menu, Button, Text, rem } from '@mantine/core';

657

658

function MenuDemo() {

659

return (

660

<Menu>

661

<Menu.Target>

662

<Button>Toggle menu</Button>

663

</Menu.Target>

664

665

<Menu.Dropdown>

666

<Menu.Label>Application</Menu.Label>

667

<Menu.Item leftSection="βš™οΈ">

668

Settings

669

</Menu.Item>

670

<Menu.Item leftSection="πŸ’¬">

671

Messages

672

</Menu.Item>

673

<Menu.Item leftSection="πŸ“·">

674

Gallery

675

</Menu.Item>

676

677

<Menu.Divider />

678

679

<Menu.Label>Danger zone</Menu.Label>

680

<Menu.Item leftSection="πŸ—ƒοΈ">

681

Transfer my data

682

</Menu.Item>

683

<Menu.Item

684

color="red"

685

leftSection="πŸ—‘οΈ"

686

>

687

Delete my account

688

</Menu.Item>

689

</Menu.Dropdown>

690

</Menu>

691

);

692

}

693

```

694

695

## Dialog

696

697

Simple dialog component for notifications and confirmations.

698

699

```typescript { .api }

700

interface DialogProps {

701

/** If true, dialog is opened */

702

opened: boolean;

703

/** Called when dialog should be closed */

704

onClose?: () => void;

705

/** Dialog content */

706

children: React.ReactNode;

707

/** Dialog position */

708

position?: { top?: number; bottom?: number; left?: number; right?: number };

709

/** Dialog size */

710

size?: MantineSize | number;

711

/** Dialog radius */

712

radius?: MantineRadius;

713

/** Dialog shadow */

714

shadow?: MantineShadow;

715

/** Dialog z-index */

716

zIndex?: number;

717

/** If true, dialog will have close button */

718

withCloseButton?: boolean;

719

/** If true, dialog cannot be closed by clicking outside */

720

closeOnClickOutside?: boolean;

721

/** If true, dialog cannot be closed by pressing Escape */

722

closeOnEscape?: boolean;

723

/** Transition props */

724

transitionProps?: TransitionProps;

725

}

726

```

727

728

**Usage Example:**

729

730

```tsx

731

import { Dialog, Text, Button, Group } from '@mantine/core';

732

import { useDisclosure } from '@mantine/hooks';

733

734

function DialogDemo() {

735

const [opened, { toggle, close }] = useDisclosure(false);

736

737

return (

738

<>

739

<Group justify="center">

740

<Button onClick={toggle}>Toggle dialog</Button>

741

</Group>

742

743

<Dialog

744

opened={opened}

745

withCloseButton

746

onClose={close}

747

size="lg"

748

radius="md"

749

>

750

<Text size="sm" mb="xs" fw={500}>

751

Subscribe to email newsletter

752

</Text>

753

754

<Text size="xs">

755

Subscribe to our newsletter to stay up to date with the latest news

756

</Text>

757

</Dialog>

758

</>

759

);

760

}

761

```

762

763

## Overlay

764

765

Generic overlay component for creating backgrounds.

766

767

```typescript { .api }

768

interface OverlayProps {

769

/** Overlay background color */

770

color?: string;

771

/** Overlay opacity */

772

backgroundOpacity?: number;

773

/** Overlay blur amount */

774

blur?: number;

775

/** Overlay z-index */

776

zIndex?: number;

777

/** Overlay radius */

778

radius?: MantineRadius;

779

/** If true, overlay will cover entire viewport */

780

fixed?: boolean;

781

/** Overlay children */

782

children?: React.ReactNode;

783

/** Called when overlay is clicked */

784

onClick?: () => void;

785

}

786

```

787

788

**Usage Example:**

789

790

```tsx

791

import { Overlay, Button, Box } from '@mantine/core';

792

import { useState } from 'react';

793

794

function OverlayDemo() {

795

const [visible, setVisible] = useState(false);

796

797

return (

798

<Box pos="relative" h={200}>

799

{visible && <Overlay color="#000" backgroundOpacity={0.35} />}

800

<Button onClick={() => setVisible(!visible)}>

801

Toggle overlay

802

</Button>

803

</Box>

804

);

805

}

806

```

807

808

## Portal

809

810

Renders children in a different part of the DOM.

811

812

```typescript { .api }

813

interface PortalProps {

814

/** Portal children */

815

children: React.ReactNode;

816

/** Element where portal should be rendered */

817

target?: HTMLElement | string;

818

}

819

```

820

821

**Usage Example:**

822

823

```tsx

824

import { Portal, Box, Button } from '@mantine/core';

825

import { useState } from 'react';

826

827

function PortalDemo() {

828

const [opened, setOpened] = useState(false);

829

830

return (

831

<Box>

832

<Button onClick={() => setOpened(!opened)}>

833

Toggle portal

834

</Button>

835

836

{opened && (

837

<Portal>

838

<Box

839

style={{

840

position: 'absolute',

841

top: 0,

842

right: 0,

843

width: 200,

844

height: 100,

845

backgroundColor: 'red',

846

zIndex: 1000,

847

}}

848

>

849

Portal content rendered at document.body

850

</Box>

851

</Portal>

852

)}

853

</Box>

854

);

855

}

856

```

857

858

## Affix

859

860

Fixed positioned element that sticks to viewport edge.

861

862

```typescript { .api }

863

interface AffixProps {

864

/** Affix position */

865

position?: { top?: number; bottom?: number; left?: number; right?: number };

866

/** Affix z-index */

867

zIndex?: number;

868

/** If true, affix will be within scrollable container */

869

withinPortal?: boolean;

870

/** Affix children */

871

children: React.ReactNode;

872

/** Target element for within portal */

873

portalProps?: PortalProps;

874

}

875

```

876

877

**Usage Example:**

878

879

```tsx

880

import { Affix, Button, Text, Transition } from '@mantine/core';

881

import { useWindowScroll } from '@mantine/hooks';

882

883

function AffixDemo() {

884

const [scroll, scrollTo] = useWindowScroll();

885

886

return (

887

<>

888

<Text ta="center">Scroll down to see button</Text>

889

890

<Affix position={{ bottom: 20, right: 20 }}>

891

<Transition transition="slide-up" mounted={scroll.y > 0}>

892

{(transitionStyles) => (

893

<Button

894

leftSection="↑"

895

style={transitionStyles}

896

onClick={() => scrollTo({ y: 0 })}

897

>

898

Scroll to top

899

</Button>

900

)}

901

</Transition>

902

</Affix>

903

</>

904

);

905

}

906

```

907

908

## FloatingIndicator

909

910

Animated indicator that moves between elements.

911

912

```typescript { .api }

913

interface FloatingIndicatorProps {

914

/** Target element selector or ref */

915

target: string | HTMLElement;

916

/** Parent element selector or ref */

917

parent?: string | HTMLElement;

918

/** Indicator className */

919

className?: string;

920

/** If true, indicator will be disabled */

921

disabled?: boolean;

922

}

923

```

924

925

**Usage Example:**

926

927

```tsx

928

import { FloatingIndicator, UnstyledButton } from '@mantine/core';

929

import { useState } from 'react';

930

931

function FloatingIndicatorDemo() {

932

const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);

933

const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});

934

const [active, setActive] = useState(0);

935

936

const setControlRef = (index: number) => (node: HTMLButtonElement) => {

937

controlsRefs[index] = node;

938

setControlsRefs(controlsRefs);

939

};

940

941

const controls = Array(3).fill(0).map((_, index) => (

942

<UnstyledButton

943

key={index}

944

ref={setControlRef(index)}

945

onClick={() => setActive(index)}

946

mod={{ active: active === index }}

947

>

948

Tab {index + 1}

949

</UnstyledButton>

950

));

951

952

return (

953

<div ref={setRootRef}>

954

<FloatingIndicator

955

target={controlsRefs[active]}

956

parent={rootRef}

957

className="indicator"

958

/>

959

{controls}

960

</div>

961

);

962

}

963

```

964

965

## Theme Integration

966

967

Overlay components integrate with the Mantine theme system:

968

969

```tsx

970

import { MantineProvider } from '@mantine/core';

971

972

const theme = {

973

components: {

974

Modal: {

975

defaultProps: {

976

centered: true,

977

radius: 'md',

978

overlayProps: { backgroundOpacity: 0.55, blur: 3 },

979

},

980

},

981

Popover: {

982

defaultProps: {

983

position: 'bottom',

984

withArrow: true,

985

shadow: 'md',

986

},

987

},

988

Tooltip: {

989

defaultProps: {

990

withArrow: true,

991

color: 'dark',

992

},

993

},

994

},

995

};

996

```

997

998

## Accessibility Features

999

1000

Overlay components include comprehensive accessibility support:

1001

1002

- **Focus Management**: Proper focus trapping and restoration

1003

- **Keyboard Navigation**: Full keyboard support with arrow keys and Enter/Escape

1004

- **ARIA Attributes**: Proper labeling and roles for screen readers

1005

- **Screen Reader Support**: Announcements for state changes

1006

- **Color Contrast**: Sufficient contrast ratios for overlays and content