component
Tooltip
A small label that explains something when you point at it or focus it.
label
import { Tooltip } from 'rost/components/tooltip/Tooltip'
<span className="has-tip" aria-describedby="tip-here">
Work
<Tooltip id="tip-here">You're already here</Tooltip>
</span>
/* Where and when is the page's: */
.has-tip { position: relative; }
.has-tip .rost-tooltip { position: absolute; top: calc(100% + 8px); left: 50%; translate: -50% 0; opacity: 0; pointer-events: none; }
.has-tip:is(:hover, :focus-within) .rost-tooltip { opacity: 1; }src/components/tooltip/Tooltip.tsx
import type { HTMLAttributes, ReactNode } from 'react'
import './tooltip.css'
export interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
/** A few words. Link it from what it explains with `aria-describedby`. */
readonly children?: ReactNode
}
const b = 'rost-tooltip'
/**
* The bubble: a few words on the secondary surface. Where it sits and when
* it shows is the page's (8px under the thing it explains, on hover and
* focus). Built from contracts/tooltip.contract.yaml.
*/
export function Tooltip({ children, className, ...rest }: TooltipProps) {
return (
<span role="tooltip" className={[b, className].filter(Boolean).join(' ')} {...rest}>
<span className={`${b}__label`}>{children}</span>
</span>
)
}When to use
- Used to explain something that can't explain itself: an icon-only button, or a nav item for the page you're already on.
When not to use
- Anything people need in order to use the page. A tooltip is easy to miss, and on a phone there's no hover.
- More than a few words. That's help text, or an InlineBanner.
Behaviour
Shows on hover and on keyboard focus of the thing it explains, 8px under it, and goes when the pointer or focus leaves.
Accessibility
- Element:
role="tooltip", linked from the thing it explains witharia-describedby. - Name: the tooltip isn't a name. The thing it explains still needs its own
aria-label. - Keyboard: it shows when focus arrives, and Escape hides it without moving focus.
- Focus: it never takes focus itself.
- Hover: it never holds anything you'd need to click, and the pointer can move onto it without it going.
Content
A few words, sentence case, no full stop.
Props
| Prop | Type | Values | Default |
|---|---|---|---|
| label | slot |
Values
Straight from the contract. Each value sits next to its token, because six tokens here all come out as #3a3a3a and the colour alone can't tell you which one is right.
Never changes
| Part | Property | Value | Token |
|---|---|---|---|
| root | display | inline-flex | layout |
| root | align-items | center | layout |
| root | justify-content | center | layout |
| root | white-space | nowrap | layout |
| root | height | 20px | on purpose: Figma's own 20px; no size token is 20. |
| root | padding-left | 8px | spacing/gap/small |
| root | padding-right | 8px | spacing/gap/small |
| root | padding-top | 0px | spacing/gap/none |
| root | padding-bottom | 0px | spacing/gap/none |
| root | border-radius | 4px | spacing/radius/xsmall |
| root | background | #3a3a3a | actions/secondary/regular/default/background |
| label | font-family | 'General Sans Variable' | font-family/base |
| label | font-weight | 500 | typography/label/font-weight/medium |
| label | font-size | 12px | typography/label/font-size |
| label | line-height | 14px | typography/label/line-height |
| label | letter-spacing | 0px | typography/label/letter-spacing |
| label | color | #d3d9d5 | actions/secondary/regular/default/label |