Back to React Spectrum

Tooltip

packages/react-aria-components/docs/Tooltip.mdx

2022-12-1615.1 KB
Original Source

{/* Copyright 2020 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}

import {Layout} from '@react-spectrum/docs'; export default Layout;

import docs from 'docs:react-aria-components'; import statelyDocs from 'docs:@react-stately/tooltip'; import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import Anatomy from '/packages/react-aria/docs/tooltip/anatomy.svg'; import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; import {Divider} from '@react-spectrum/divider'; import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; import {Keyboard} from '@react-spectrum/text'; import {StarterKits} from '@react-spectrum/docs/src/StarterKits';


category: Overlays keywords: [tooltip, aria] type: component

Tooltip

<PageDescription>{docs.exports.Tooltip.description}</PageDescription>

<HeaderInfo packageData={packageData} componentNames={['Tooltip']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/TR/wai-aria-1.2/#tooltip'} ]} />

Example

tsx
import {TooltipTrigger, Tooltip, OverlayArrow, Button} from 'react-aria-components';
import {Edit} from 'lucide-react';

<TooltipTrigger>
  <Button><Edit size={18} /></Button>
  <Tooltip>
    <OverlayArrow>
      <svg width={8} height={8} viewBox="0 0 8 8"><path d="M0 0 L4 4 L8 0" /></svg>
    </OverlayArrow>
    Edit
  </Tooltip>
</TooltipTrigger>
<details> <summary style={{fontWeight: 'bold'}}><ChevronRight size="S" /> Show CSS</summary> ```css hidden @import './Button.mdx' layer(button); ```
css
@import "@react-aria/example-theme";

.react-aria-Tooltip {
  box-shadow: 0 8px 20px rgba(0 0 0 / 0.1);
  border-radius: 4px;
  background: var(--highlight-background);
  color: var(--highlight-foreground);
  forced-color-adjust: none;
  outline: none;
  padding: 2px 8px;
  max-width: 150px;
  /* fixes FF gap */
  transform: translate3d(0, 0, 0);
  transition: transform 200ms, opacity 200ms;
  transform-origin: var(--trigger-anchor-point);

  &[data-entering],
  &[data-exiting] {
    transform: scale(0.9) var(--origin);
    opacity: 0;
  }

  &[data-placement=top] {
    margin-bottom: 8px;
    --origin: translateY(4px);
  }

  &[data-placement=bottom] {
    margin-top: 8px;
    --origin: translateY(-4px);
    & .react-aria-OverlayArrow svg {
      transform: rotate(180deg);
    }
  }

  &[data-placement=right] {
    margin-left: 8px;
    --origin: translateX(-4px);
    & .react-aria-OverlayArrow svg {
      transform: rotate(90deg);
    }
  }

  &[data-placement=left] {
    margin-right: 8px;
    --origin: translateX(4px);
    & .react-aria-OverlayArrow svg {
      transform: rotate(-90deg);
    }
  }

  & .react-aria-OverlayArrow svg {
    display: block;
    fill: var(--highlight-background);
  }
}
</details>

Features

The HTML title attribute can be used to create a tooltip, but it cannot be styled. Custom styled tooltips can be hard to build in an accessible way. TooltipTrigger and Tooltip help build fully accessible tooltips that can be styled as needed.

  • Styleable – States for entry and exit animations are included for easy styling, and an optional arrow element can be rendered.
  • Accessible – The trigger element is automatically associated with the tooltip via aria-describedby. Tooltips are displayed when an element receives focus.
  • Hover behavior – Tooltips display after a global delay on hover of the first tooltip, with no delay on subsequent tooltips to avoid unintended flickering. Emulated hover events on touch devices are ignored.
  • Positioning – The tooltip is positioned relative to the trigger element, and automatically flips and adjusts to avoid overlapping with the edge of the browser window.

Anatomy

<Anatomy />

A tooltip consists of two parts: the trigger element and the tooltip itself. Users may reveal the tooltip by hovering or focusing the trigger.

tsx
import {TooltipTrigger, Tooltip, OverlayArrow, Button} from 'react-aria-components';

<TooltipTrigger>
  <Button />
  <Tooltip>
    <OverlayArrow />
  </Tooltip>
</TooltipTrigger>

Accessibility

Tooltip triggers must be focusable and hoverable in order to ensure that all users can activate them. When displayed, TooltipTrigger automatically associates the tooltip with the trigger element so that it is described by the tooltip content.

Note: tooltips are not shown on touch screen interactions. Ensure that your UI is usable without tooltips, or use an alternative component such as a Popover to show information in an adjacent element.

Starter kits

To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured Storybook that you can experiment with, or use as a starting point for your own component library.

<StarterKits component="tooltip" />

Reusable wrappers

If you will use a Tooltip in multiple places in your app, you can wrap all of the pieces into a reusable component. This way, the DOM structure, styling code, and other logic are defined in a single place and reused everywhere to ensure consistency.

This example wraps Tooltip and all of its children together into a single component.

tsx
import type {TooltipProps} from 'react-aria-components';
import {Save} from 'lucide-react';

interface MyTooltipProps extends Omit<TooltipProps, 'children'> {
  children: React.ReactNode
}

function MyTooltip({children, ...props}: MyTooltipProps) {
  return (
    <Tooltip {...props}>
      <OverlayArrow>
        <svg width={8} height={8} viewBox="0 0 8 8"><path d="M0 0 L4 4 L8 0" /></svg>
      </OverlayArrow>
      {children}
    </Tooltip>
  );
}

<TooltipTrigger>
  <Button><Save size={18} /></Button>
  <MyTooltip>Save</MyTooltip>
</TooltipTrigger>

Interactions

Delay

Tooltips appear after a short delay when hovering the trigger, or instantly when using keyboard focus. This delay is called the "warmup period", and it is a global timer, shared by all tooltips. Once a tooltip is displayed, other tooltips display immediately. If the user waits for the "cooldown period" before hovering another element, the warmup timer restarts.

tsx
<div style={{display: 'flex', gap: 8}}>
  <TooltipTrigger>
    <Button>Hover me</Button>
    <MyTooltip>I come up after a delay.</MyTooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <Button>Then hover me</Button>
    <MyTooltip>If you did it quickly, I appear immediately.</MyTooltip>
  </TooltipTrigger>
</div>

The delay can be adjusted for hover using the delay prop.

tsx
<TooltipTrigger delay={0}>
  <Button><Save size={18} /></Button>
  <MyTooltip>Save</MyTooltip>
</TooltipTrigger>

Trigger

By default, tooltips appear both on hover and on focus. The trigger prop can be set to "focus" to display the tooltip only on focus, and not on hover.

tsx
import {Disc} from 'lucide-react';
<TooltipTrigger trigger="focus">
  <Button><Disc size={18} /></Button>
  <MyTooltip>Burn CD</MyTooltip>
</TooltipTrigger>

Controlled open state

The open state of the tooltip can be controlled via the defaultOpen and isOpen props.

tsx
import {Bell} from 'lucide-react';
function Example() {
  let [isOpen, setOpen] = React.useState(false);

  return (
    <>
      <TooltipTrigger isOpen={isOpen} onOpenChange={setOpen}>
        <Button><Bell size={18} /></Button>
        <MyTooltip>Notifications</MyTooltip>
      </TooltipTrigger>
      <p>Tooltip is {isOpen ? 'showing' : 'not showing'}</p>
    </>
  );
}

Positioning

Placement

The Tooltip's placement with respect to its trigger element can be adjusted using the placement prop. See the props table for a full list of available placement combinations.

tsx
import {ArrowUp, ArrowDown, ArrowRight, ArrowLeft} from 'lucide-react';
<div style={{display: 'flex', gap: 8}}>
  <TooltipTrigger>
    <Button><ArrowLeft size={18} /></Button>
    <MyTooltip placement="start">In left-to-right, this is on the left. In right-to-left, this is on the right.</MyTooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <Button><ArrowUp size={18} /></Button>
    <MyTooltip placement="top">This tooltip is above the button.</MyTooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <Button><ArrowDown size={18} /></Button>
    <MyTooltip placement="bottom">This tooltip is below the button.</MyTooltip>
  </TooltipTrigger>
  <TooltipTrigger>
    <Button><ArrowRight size={18} /></Button>
    <MyTooltip placement="end">In left-to-right, this is on the right. In right-to-left, this is on the left.</MyTooltip>
  </TooltipTrigger>
</div>

Offset and cross offset

The Tooltip's offset with respect to its trigger can be adjusted using the offset and crossOffset props. The offset prop controls the spacing applied along the main axis between the element and its trigger whereas the crossOffset prop handles the spacing applied along the cross axis.

Below is a tooltip offset by an additional 50px above the trigger.

tsx
<TooltipTrigger>
  <Button><ArrowUp size={18} /></Button>
  <MyTooltip offset={50}>This will shift up.</MyTooltip>
</TooltipTrigger>

Below is a tooltip cross offset by an additional 100px to the right of the trigger.

tsx
<TooltipTrigger>
  <Button><ArrowRight size={18} /></Button>
  <MyTooltip crossOffset={60} placement="bottom">This will shift over to the right.</MyTooltip>
</TooltipTrigger>

Disabled

The isDisabled prop can be provided to a TooltipTrigger to disable the tooltip, without disabling the trigger it displays on.

tsx
import {Printer} from 'lucide-react';
<TooltipTrigger isDisabled>
  <Button><Printer size={18} /></Button>
  <MyTooltip>Print</MyTooltip>
</TooltipTrigger>

Custom trigger

TooltipTrigger works out of the box with any focusable React Aria component (e.g. Button, Link, etc.). Custom trigger elements such as third party components and other DOM elements are also supported by wrapping them with the <Focusable> component.

tsx
import {Focusable} from 'react-aria-components';

<TooltipTrigger>
  <Focusable>
    <span role="button">Custom trigger</span>
  </Focusable>
  <MyTooltip>Tooltip</MyTooltip>
</TooltipTrigger>

Note that any <Focusable> child must have an ARIA role or use an appropriate semantic HTML element so that screen readers can announce the content of the tooltip. Trigger components must forward their ref and spread all props to a DOM element.

tsx
const CustomTrigger = React.forwardRef((props, ref) => (
  <button {...props} ref={ref} />
));

DialogTrigger or MenuTrigger with custom triggers via <Pressable> also automatically work with TooltipTrigger. All <Pressable> elements are already focusable, so there's no need to wrap them in <Focusable> in this case.

Props

TooltipTrigger

<PropTable component={docs.exports.TooltipTrigger} links={docs.links} />

Tooltip

<PropTable component={docs.exports.Tooltip} links={docs.links} />

OverlayArrow

OverlayArrow accepts all HTML attributes.

Styling

React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName naming convention.

css
.react-aria-Tooltip {
  /* ... */
}

A custom className can also be specified on any component. This overrides the default className provided by React Aria with your own.

jsx
<Tooltip className="my-tooltip">
</Tooltip>

In addition, some components support multiple UI states (e.g. focused, placeholder, readonly, etc.). React Aria components expose states using data attributes, which you can target in CSS selectors. For example:

css
.react-aria-Tooltip[data-placement=left] {
  /* ... */
}

The className and style props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply, which is useful when using utility CSS libraries like Tailwind.

jsx
<OverlayArrow className={({placement}) => placement === 'left' || placement === 'right' ? 'rotate-90' : 'rotate-0'}>
</OverlayArrow>

Tooltips also support entry and exit animations via states exposed as data attributes and render props. Tooltip will automatically wait for any exit animations to complete before it is removed from the DOM. The --trigger-anchor-point variable is set to the position of the trigger relative to the popover, which is useful for origin-aware animations. See the animation guide for more details.

css
.react-aria-Tooltip {
  transition: opacity 300ms, scale 300ms;
  transform-origin: var(--trigger-anchor-point);

  &[data-entering],
  &[data-exiting] {
    opacity: 0;
    scale: 0.85;
  }
}

The states, selectors, and render props for each component used in a TooltipTrigger are documented below.

TooltipTrigger

The TooltipTrigger component does not render any DOM elements (it only passes through its children) so it does not support styling. If you need a wrapper element, add one yourself inside the <TooltipTrigger>.

jsx
<TooltipTrigger>
  <div className="my-tooltip-trigger">
  </div>
</TooltipTrigger>

Tooltip

A Tooltip can be targeted with the .react-aria-Tooltip CSS selector, or by overriding with a custom className. It supports the following states and render props:

<StateTable properties={docs.exports.TooltipRenderProps.properties} />

OverlayArrow

A OverlayArrow can be targeted with the .react-aria-OverlayArrow CSS selector, or by overriding with a custom className. It supports the following states and render props:

<StateTable properties={docs.exports.OverlayArrowRenderProps.properties} />

Advanced customization

State

TooltipTrigger provides an <TypeLink links={statelyDocs.links} type={statelyDocs.exports.TooltipTriggerState} /> object to its children via TooltipTriggerStateContext. This can be used to access and manipulate the tooltip trigger's state.

Hooks

If you need to customize things further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See useTooltipTrigger for more details.