JGuang

HoverCard

HoverCard · Mantine Core

Preview
npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1

HoverCard

Install: npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1

Version and compatibility

Pinned Mantine Core 9.7.1 requires React and React DOM ^19.2.0 and matching @mantine/hooks@9.7.1, from the installed npm package's peerDependencies. Do not force it into React 18 or another incompatible version. Explain upgrade impact and agree on a plan with the project owner; do not hide conflicts with --force/--legacy-peer-deps. The native Provider enables theme.respectReducedMotion and uses forceColorScheme for light/dark.

Use case

Show contextual content around a hover target.

Native props used by this example

These props are extracted from the local typechecked JSX and grouped by their owning native component. This is not an exhaustive prop list; see pinned source signatures and inheritance below.

This minimal example passes children only; no additional props are invented. Optional props are in the source reference.

Interaction and state checks

Current scenarios: Default. Check actual behavior after selecting supported disabled/error/read-only scenarios; components without those native states do not receive invented universal props. Verify keyboard focus and exits, then connect changed values through the native callback signature.

Business integration boundary

Demo content/state is local, without server persistence, authorization or fetching. A static display component is not a completed business workflow.

Host setup

Install pinned @mantine/core and @mantine/hooks, import @mantine/core/styles.css at the entry and use MantineProvider. The example's theme/variant are JGuang-authored host props, not universal native component APIs; forceColorScheme synchronizes the app theme. Native implementation source is a reading reference: use npm exports rather than copying internal engines. This batch covers Core, not dates/charts/forms extension packages. Detailed upstream prose remains English.

Runnable usage

import { useState } from 'react';
import * as M from '@mantine/core';
import '@mantine/core/styles.css';


function ComponentPreview({ variant = 'Default' }: { variant?: string }) {
const content = <M.Text>Build something beautiful / 构建美好的界面</M.Text>;
return <M.HoverCard><M.HoverCard.Target><M.Button>Hover me</M.Button></M.HoverCard.Target><M.HoverCard.Dropdown>{content}</M.HoverCard.Dropdown></M.HoverCard>;
}
export default function Example({ variant = 'Default', theme = 'light' }: { variant?: string; theme?: 'light' | 'dark' }) {
return <M.MantineProvider forceColorScheme={theme} theme={{ respectReducedMotion: true }}><ComponentPreview variant={variant} /></M.MantineProvider>;
}

Preview states

Default

The example uses the native Mantine API. For supported input demos, change the local variant to Disabled, Invalid or Read only; these are example labels, not new component props. Keep the documented data, controlled values, stores and compound children when adapting the example.

Usage

Delays

Set open and close delays in ms with the openDelay and closeDelay props:

HoverCard delay group

Use the HoverCard.Group component to sync open and close delays of multiple HoverCard components:

Activation events

The events prop determines which interactions open the dropdown. Fields that are not specified keep their default values, for example events={{ focus: false }} disables focus activation and leaves hover activation enabled:

<DataTable head={['Field', 'Default', 'Description']} data={[ [<code>hover</code>, <code>true</code>, 'Dropdown is opened when the target is hovered with a mouse'], [<code>focus</code>, <code>true</code>, 'Dropdown is opened when the target is focused with the keyboard'], [ <code>touch</code>, <code>false</code>, <> Dropdown is opened when the target is tapped on a touch device, requires <code>hover</code>{' '} to be enabled </>, ], ]} />

Focus activation uses :focus-visible semantics: the dropdown is opened when the target is focused with the keyboard, but not when it is focused by a mouse click.

touch is disabled by default because there is no hover on touch devices – the first tap would open the dropdown instead of activating the target. Enable it only if the target does not have its own tap action. touch extends hover activation, it has no effect if hover is disabled.

Keyboard interactions

<DataTable head={['Key', 'Description']} data={[ ['Tab', <>Moves focus to the target, opens the dropdown if <code>events.focus</code> is enabled</>], ['Escape', <>Closes the dropdown, can be disabled with <code>{'closeOnEscape={false}'}</code></>], ]} />

The dropdown is also closed when the user presses outside of the target and the dropdown (pointerdown event), set closeOnClickOutside={false} to disable this behavior. Note that the clickOutsideEvents prop is not supported by HoverCard: outside press is always detected with the pointerdown event.

Interactive dropdown

By default, the dropdown is closed as soon as the pointer leaves the target: if there is a gap between the target and the dropdown, or if the pointer travels diagonally over unrelated elements, the dropdown closes before the pointer reaches it.

Set the interactive prop to keep the dropdown open while the pointer travels toward it. It is required by WCAG 1.4.13 if the dropdown contains interactive content. Note that an interactive dropdown intercepts pointer events of the content it overlaps:

With interactive elements

HoverCard is displayed only when the mouse is over the target element or dropdown. You can use anchors and buttons within dropdowns, using inputs is not recommended:

The role prop controls the accessible relation between the target and the dropdown:

<DataTable head={['Value', 'Target attributes', 'Dropdown attributes']} data={[ [ <> <code>dialog</code> (default) </>, <> <code>aria-haspopup="dialog"</code>, <code>aria-expanded</code>, <code>aria-controls</code> </>, <> <code>role="dialog"</code>, <code>aria-labelledby</code> </>, ], [<code>tooltip</code>, <code>aria-describedby</code>, <code>role="tooltip"</code>], ]} />

Use role="tooltip" if the dropdown contains only descriptive content – its content is then announced by screen readers as the description of the target.

Keep the default role="dialog" if the dropdown contains headings, links, buttons or any other content that is not a plain description of the target.

Accessibility

HoverCard can be opened with a mouse and with the keyboard, and is announced by screen readers:

  • The target is focusable if it is an interactive element (a button or a link). If the target is not interactive, add tabIndex={0} to it to make it reachable with the keyboard.
  • Focusing the target with the keyboard opens the dropdown, Escape and a press outside of the target and the dropdown close it.
  • onDismiss is called when the dropdown is closed with Escape key or with a press outside of the target and the dropdown.
  • aria-haspopup, aria-expanded, aria-controls and aria-describedby attributes are managed by the component, see the Dropdown role section.
  • The returnFocus prop is ignored while focus activation is enabled (events.focus, the default): focus is already on the target when the dropdown is opened with the keyboard, and returning it after the target was blurred would trap keyboard users on the target. Escape pressed inside the dropdown always returns focus to the target.

Limitations

The dropdown is rendered in a portal at the end of document.body, it is not a part of the tab order of the page. Keyboard users can open, read and dismiss the dropdown, but cannot move focus into it – links and buttons inside the dropdown are not reachable with the keyboard.

If the dropdown contains interactive content, that content must also be available elsewhere on the page. Do not use the trapFocus prop to work around this limitation: focus cannot be moved into the dropdown in the first place, and trapping focus in a hover-triggered element strands keyboard users.

With withinPortal={false} the dropdown is rendered next to the target and focus can be moved into it with Tab, but moving focus out of the dropdown with Tab does not close it – the dropdown is closed with Escape, with a press outside or when the pointer leaves the target.

Hover and focus activations do not coordinate: if the pointer leaves the target while the target is focused with the keyboard, the dropdown is closed after closeDelay and is not reopened until the target is blurred and focused again.

Source-backed parameter reference

Implementation signatures and types are generated from the pinned local source. Optional props, native inherited types, union branches and never exclusions are preserved. A forwardRef implementation's ref is a React reference, not an additional business prop.

HoverCard(props: HoverCardProps)
export interface HoverCardEvents {
  /** Determines whether the dropdown should be opened when the target is hovered @default true */
  hover?: boolean;

  /** Determines whether the dropdown should be opened when the target is focused with the keyboard @default true */
  focus?: boolean;

  /** Determines whether the dropdown should be opened when the target is tapped on touch devices. Requires `hover` to be enabled @default false */
  touch?: boolean;
}

export interface HoverCardProps extends Omit<PopoverProps, 'opened' | 'onChange'> {
  /** Initial opened state */
  initiallyOpened?: boolean;

  /** Called when the dropdown is opened */
  onOpen?: () => void;

  /** Called when the dropdown is closed */
  onClose?: () => void;

  /**
   * Delay in ms before the dropdown opens after mouse enters the target.
   * Overridden by HoverCard.Group delay if used within a group.
   * @default 0
   */
  openDelay?: number;

  /**
   * Delay in ms before the dropdown closes after mouse leaves the target or dropdown.
   * Overridden by HoverCard.Group delay if used within a group.
   * @default 150
   */
  closeDelay?: number;

  /**
   * Determines which events open the dropdown. Fields that are not specified keep their default values.
   * @default { hover: true, focus: true, touch: false }
   */
  events?: HoverCardEvents;

  /**
   * If set, the dropdown stays open while the pointer travels from the target to the dropdown
   * and remains over it. Set it if the dropdown contains interactive content.
   * @default false
   */
  interactive?: boolean;

  /**
   * Accessible relation between the target and the dropdown. `dialog` sets `aria-haspopup`,
   * `aria-expanded` and `aria-controls` on the target, `tooltip` sets `aria-describedby` instead
   * and should be used if the dropdown contains only descriptive content.
   * @default 'dialog'
   */
  role?: 'dialog' | 'tooltip';

  /** Determines whether the dropdown should be closed when the user presses outside of the target and the dropdown @default true */
  closeOnClickOutside?: boolean;

  /** Not supported by `HoverCard`: outside press is always detected with the `pointerdown` event. Use `closeOnClickOutside` to disable it. */
  clickOutsideEvents?: string[];

  /**
   * Determines whether focus should be returned to the target when the dropdown closes.
   * Ignored when focus activation is enabled (`events.focus`, the default), otherwise moving
   * focus away from the target would return it and reopen the dropdown.
   * `Escape` pressed inside the dropdown always returns focus to the target.
   * @default false
   */
  returnFocus?: boolean;

  /** Called when the dropdown is closed with `Escape` key or with a press outside of the target and the dropdown */
  onDismiss?: () => void;
}

Native and external types

External types remain defined by React or native dependencies, not manually expanded into guessed props. The installed version's TypeScript definitions are authoritative.

Omit<PopoverProps, 'opened' | 'onChange'>
PopoverProps