Skip to content

Tooltip

WARNING

⚠️ If you require customizing the tooltip behavior, please refer to the intergalactic/popper documentation.

The tooltip component is a wrap over intergalactic/popper with additional features:

  • Stylization and themes for the popper.
  • Adding arrow for the popper to point to its trigger.

Basic usage

As previously mentioned, the tooltip is a styled version of popper and operates similarly.

Tooltip contains Hint and DescriptionTooltip. By using correct component, you will enhance end interface accessibility.

  1. Use Hint if the trigger lacks a visible name. Content should be brief and non-interactive.
  2. Use Tooltip when the trigger has a visible name and the content consists of a single text sentence. It may also include interactive elements.
  3. Use DescriptionTooltip when the trigger has a visible name and the content provides a significant amount of additional information. It may contain numerous interactive elements.
tsx
import React from 'react';
import Tooltip, { Hint, DescriptionTooltip } from 'intergalactic/tooltip';
import { Box, Flex } from 'intergalactic/flex-box';
import { Text } from 'intergalactic/typography';
import Link from 'intergalactic/link';
import Button from 'intergalactic/button';

const Demo = () => (
  <Flex gap={4} flexWrap>
    <Hint title='Hint may contain short text only.' tag={Link}>
      Hint
    </Hint>
    <Tooltip>
      <Tooltip.Trigger tag={Link}>Tooltip</Tooltip.Trigger>
      <Tooltip.Popper>
        Default tooltip may contain formatted tex, icons,{' '}
        <Link href='https://semrush.com'>links</Link> and other elements. Be brief, add only one
        sentence for its content.
      </Tooltip.Popper>
    </Tooltip>
    <DescriptionTooltip>
      <DescriptionTooltip.Trigger tag={Button}>Additional information</DescriptionTooltip.Trigger>
      <DescriptionTooltip.Popper>
        <Text size={200} bold>
          Additional information
        </Text>
        <Box my={2}>
          Use this tooltip type for elements that already have a visible name, and you need to show
          a lot of additional information.
        </Box>
        <Box mb={2}>
          It may contain several paragraphs and interactive elements (for example,{' '}
          <Link href='https://semrush.com'>links</Link>).
        </Box>
      </DescriptionTooltip.Popper>
    </DescriptionTooltip>
    <input />
  </Flex>
);

export default Demo;
import React from 'react';
import Tooltip, { Hint, DescriptionTooltip } from 'intergalactic/tooltip';
import { Box, Flex } from 'intergalactic/flex-box';
import { Text } from 'intergalactic/typography';
import Link from 'intergalactic/link';
import Button from 'intergalactic/button';

const Demo = () => (
  <Flex gap={4} flexWrap>
    <Hint title='Hint may contain short text only.' tag={Link}>
      Hint
    </Hint>
    <Tooltip>
      <Tooltip.Trigger tag={Link}>Tooltip</Tooltip.Trigger>
      <Tooltip.Popper>
        Default tooltip may contain formatted tex, icons,{' '}
        <Link href='https://semrush.com'>links</Link> and other elements. Be brief, add only one
        sentence for its content.
      </Tooltip.Popper>
    </Tooltip>
    <DescriptionTooltip>
      <DescriptionTooltip.Trigger tag={Button}>Additional information</DescriptionTooltip.Trigger>
      <DescriptionTooltip.Popper>
        <Text size={200} bold>
          Additional information
        </Text>
        <Box my={2}>
          Use this tooltip type for elements that already have a visible name, and you need to show
          a lot of additional information.
        </Box>
        <Box mb={2}>
          It may contain several paragraphs and interactive elements (for example,{' '}
          <Link href='https://semrush.com'>links</Link>).
        </Box>
      </DescriptionTooltip.Popper>
    </DescriptionTooltip>
    <input />
  </Flex>
);

export default Demo;

Title

To simplify code, the component has a title property or passing content to the popper, reducing code volume.

The code below replicates the functionality of Hint example above.

tsx
import React from 'react';
import { Hint } from 'intergalactic/tooltip';
import { Box, Flex } from 'intergalactic/flex-box';
import Link from 'intergalactic/link';

const Demo = () => (
  <Flex>
    <Box m='auto' p={5}>
      <Hint title='Hello, stranger!' tag={Link}>
        Title trigger
      </Hint>
    </Box>
  </Flex>
);

export default Demo;
import React from 'react';
import { Hint } from 'intergalactic/tooltip';
import { Box, Flex } from 'intergalactic/flex-box';
import Link from 'intergalactic/link';

const Demo = () => (
  <Flex>
    <Box m='auto' p={5}>
      <Hint title='Hello, stranger!' tag={Link}>
        Title trigger
      </Hint>
    </Box>
  </Flex>
);

export default Demo;

Popper trigger accessibility

To ensure accessibility for assistive technologies, set the aria-describedby attribute on the trigger, referencing the popper. Therefore, it's necessary to merge nested focusable elements (like links or interactive icons) using the tag prop. If you intend to include focusable elements within the trigger, you must set the trigger's aria-describedby to undefined and assign the focusable element's aria-describedby to the value you get from the children render function.

tsx
import React from 'react';
import Tooltip from 'intergalactic/tooltip';
import { Box, Flex } from 'intergalactic/flex-box';
import Link from 'intergalactic/link';

const Demo = () => (
  <Flex>
    <Box m='auto' p={5}>
      <Tooltip>
        <Tooltip.Trigger aria-describedby={undefined} role={undefined}>
          {({ popperId }) => <Link aria-describedby={popperId}>Tooltip trigger</Link>}
        </Tooltip.Trigger>
        <Tooltip.Popper>Hello, stranger!</Tooltip.Popper>
      </Tooltip>
    </Box>
  </Flex>
);

export default Demo;
import React from 'react';
import Tooltip from 'intergalactic/tooltip';
import { Box, Flex } from 'intergalactic/flex-box';
import Link from 'intergalactic/link';

const Demo = () => (
  <Flex>
    <Box m='auto' p={5}>
      <Tooltip>
        <Tooltip.Trigger aria-describedby={undefined} role={undefined}>
          {({ popperId }) => <Link aria-describedby={popperId}>Tooltip trigger</Link>}
        </Tooltip.Trigger>
        <Tooltip.Popper>Hello, stranger!</Tooltip.Popper>
      </Tooltip>
    </Box>
  </Flex>
);

export default Demo;

Singleton

You can use a single tooltip for multiple reference elements. This allows you to "group" tooltips with a shared timer to improve the user experience. This example uses React context and memo to bypass select component rerendering and much improve performance during quick navigation.

tsx
import React from 'react';
import Tooltip from 'intergalactic/tooltip';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';

const options = Array(50)
  .fill('')
  .map((_, index) => `Option ${index}`);

const tooltipIndexContext = React.createContext(0);

const TooltipContent = () => {
  const tooltipIndex = React.useContext(tooltipIndexContext);
  return <>Option {tooltipIndex} description</>;
};

const SelectWithTooltip = React.memo(
  ({ setTooltipIndex }: { setTooltipIndex: (number) => void }) => {
    return (
      <Flex direction='column'>
        <Text tag='label' size={200} htmlFor='select-with-tooltips'>
          Select with tooltips
        </Text>
        <Select onHighlightedIndexChange={setTooltipIndex}>
          <Select.Trigger placeholder='Select option' mt={2} mr='auto' id='select-with-tooltips' />
          <Select.Menu>
            <Tooltip timeout={[0, 50]} placement='right'>
              {options.map((option, index) => (
                <Select.Option
                  onMouseEnter={() => setTooltipIndex(index)}
                  value={option}
                  key={index}
                  tag={Tooltip.Trigger}
                >
                  {option}
                </Select.Option>
              ))}
              <Tooltip.Popper w={200}>
                <TooltipContent />
              </Tooltip.Popper>
            </Tooltip>
          </Select.Menu>
        </Select>
      </Flex>
    );
  },
  () => true,
);

const Demo = () => {
  const [tooltipIndex, setTooltipIndex] = React.useState(0);

  return (
    <tooltipIndexContext.Provider value={tooltipIndex}>
      <SelectWithTooltip setTooltipIndex={setTooltipIndex} />
    </tooltipIndexContext.Provider>
  );
};

export default Demo;
import React from 'react';
import Tooltip from 'intergalactic/tooltip';
import Select from 'intergalactic/select';
import { Text } from 'intergalactic/typography';
import { Flex } from 'intergalactic/flex-box';

const options = Array(50)
  .fill('')
  .map((_, index) => `Option ${index}`);

const tooltipIndexContext = React.createContext(0);

const TooltipContent = () => {
  const tooltipIndex = React.useContext(tooltipIndexContext);
  return <>Option {tooltipIndex} description</>;
};

const SelectWithTooltip = React.memo(
  ({ setTooltipIndex }: { setTooltipIndex: (number) => void }) => {
    return (
      <Flex direction='column'>
        <Text tag='label' size={200} htmlFor='select-with-tooltips'>
          Select with tooltips
        </Text>
        <Select onHighlightedIndexChange={setTooltipIndex}>
          <Select.Trigger placeholder='Select option' mt={2} mr='auto' id='select-with-tooltips' />
          <Select.Menu>
            <Tooltip timeout={[0, 50]} placement='right'>
              {options.map((option, index) => (
                <Select.Option
                  onMouseEnter={() => setTooltipIndex(index)}
                  value={option}
                  key={index}
                  tag={Tooltip.Trigger}
                >
                  {option}
                </Select.Option>
              ))}
              <Tooltip.Popper w={200}>
                <TooltipContent />
              </Tooltip.Popper>
            </Tooltip>
          </Select.Menu>
        </Select>
      </Flex>
    );
  },
  () => true,
);

const Demo = () => {
  const [tooltipIndex, setTooltipIndex] = React.useState(0);

  return (
    <tooltipIndexContext.Provider value={tooltipIndex}>
      <SelectWithTooltip setTooltipIndex={setTooltipIndex} />
    </tooltipIndexContext.Provider>
  );
};

export default Demo;

Icon as trigger

You can use an interactive icon as a Tooltip's trigger.

tsx
import React from 'react';
import Tooltip from 'intergalactic/tooltip';
import IconInfo from 'intergalactic/icon/Info/m';

const Demo = () => {
  return (
    <Tooltip>
      <Tooltip.Trigger
        tag={IconInfo}
        color={'--intergalactic-icon-secondary-neutral'}
        interactive={true}
        aria-label='Hint'
      />
      <Tooltip.Popper>Content for tooltip</Tooltip.Popper>
    </Tooltip>
  );
};

export default Demo;
import React from 'react';
import Tooltip from 'intergalactic/tooltip';
import IconInfo from 'intergalactic/icon/Info/m';

const Demo = () => {
  return (
    <Tooltip>
      <Tooltip.Trigger
        tag={IconInfo}
        color={'--intergalactic-icon-secondary-neutral'}
        interactive={true}
        aria-label='Hint'
      />
      <Tooltip.Popper>Content for tooltip</Tooltip.Popper>
    </Tooltip>
  );
};

export default Demo;

Ignore portal stacking

By default, when a tooltip is rendered on the edge of a relatively positioned block, the popup mechanism may try to push it inside the block as much as possible. If you don't want this behavior, you can set the ignorePortalsStacking prop.

tsx
import React from 'react';
import Button from 'intergalactic/button';
import Modal from 'intergalactic/modal';
import { Text } from 'intergalactic/typography';
import { Box } from 'intergalactic/flex-box';
import Input from 'intergalactic/input';
import Tooltip from 'intergalactic/tooltip';

const Demo = () => {
  const [visible, setVisible] = React.useState(false);

  return (
    <React.Fragment>
      <Button onClick={() => setVisible(true)}>Open modal</Button>
      <Modal visible={visible} onClose={() => setVisible(false)} w={536}>
        <Box mb={2}>
          <Text size={300} tag='label' htmlFor='input-1'>
            First input with tooltip
          </Text>
        </Box>
        <Tooltip
          title='Tooltip with ignoring portals stacking.'
          visible={true}
          placement='left-start'
          ignorePortalsStacking
        >
          <Input size='l' w={440}>
            <Input.Value id='input-2' />
          </Input>
        </Tooltip>
        <Box mt={5} mb={2}>
          <Text size={300} tag='label' htmlFor='input-2'>
            Second input with tooltip
          </Text>
        </Box>
        <Tooltip
          title='Tooltip without ignoring portals stacking.'
          visible={true}
          placement='right-start'
        >
          <Input size='l' w={440}>
            <Input.Value id='input-2' />
          </Input>
        </Tooltip>
      </Modal>
    </React.Fragment>
  );
};

export default Demo;
import React from 'react';
import Button from 'intergalactic/button';
import Modal from 'intergalactic/modal';
import { Text } from 'intergalactic/typography';
import { Box } from 'intergalactic/flex-box';
import Input from 'intergalactic/input';
import Tooltip from 'intergalactic/tooltip';

const Demo = () => {
  const [visible, setVisible] = React.useState(false);

  return (
    <React.Fragment>
      <Button onClick={() => setVisible(true)}>Open modal</Button>
      <Modal visible={visible} onClose={() => setVisible(false)} w={536}>
        <Box mb={2}>
          <Text size={300} tag='label' htmlFor='input-1'>
            First input with tooltip
          </Text>
        </Box>
        <Tooltip
          title='Tooltip with ignoring portals stacking.'
          visible={true}
          placement='left-start'
          ignorePortalsStacking
        >
          <Input size='l' w={440}>
            <Input.Value id='input-2' />
          </Input>
        </Tooltip>
        <Box mt={5} mb={2}>
          <Text size={300} tag='label' htmlFor='input-2'>
            Second input with tooltip
          </Text>
        </Box>
        <Tooltip
          title='Tooltip without ignoring portals stacking.'
          visible={true}
          placement='right-start'
        >
          <Input size='l' w={440}>
            <Input.Value id='input-2' />
          </Input>
        </Tooltip>
      </Modal>
    </React.Fragment>
  );
};

export default Demo;

Released under the MIT License.

Released under the MIT License.