Show · Tooltip

Tooltip

Tooltip is used to identify an element or attach a small amount of auxiliary information. The most typical scenario is to explain the meaning of the icon to the user, display the truncated text, display the description of the picture, and so on.

Demos

How to import

import { Tooltip } from '@fibrous/semi-ui-solid';

Cautions

Tooltip applies the trigger attributes and event listeners to the trigger element, and positions the popup against the real DOM element of that trigger. children supports the following:

  1. A function (trigger) => JSX: spread {...trigger()} on the element that takes focus. It can be an actual DOM node, or a component that passes the props and ref to its root DOM node. Use this form for focusable triggers such as Button and Input
  2. An actual DOM element, text or any other content, which Tooltip wraps in a span that becomes the trigger
Functional ComponentDOMText
Code
import { type ComponentProps } from 'solid-js';
import { Space, Tooltip } from '@fibrous/semi-ui-solid';

const style = { border: '2px solid var(--semi-color-border)', 'padding-left': '4px', 'padding-right': '4px', 'border-radius': '4px' };

// A component only works as a trigger when the props from getTriggerProps (including ref) reach its DOM node
function Child(props: ComponentProps<'span'>) {
    return (
        <span {...props} style={style}>
            Functional Component
        </span>
    );
}

export default function TooltipDemo() {
    return (
        <Space>
            <Tooltip content="semi design">{(trigger) => <Child {...trigger()} />}</Tooltip>
            <Tooltip content="semi design">
                <span style={style}>DOM</span>
            </Tooltip>
            <Tooltip content="semi design">Text</Tooltip>
        </Space>
    );
}

Position

The direction and alignment position of the popup layer can be configured through position. For detailed optional values of position, please refer to the API document below

When configured as topLeft, it pops up, and the popup layer is left-aligned with the children (when arrowPointAtCenter=false) . When configured as topRight, it pops up, and the popup layer is right-aligned with the children (when arrowPointAtCenter=false) . Same for other directions

TL
Top
TR
LT
Left
LB
RT
Right
RB
BL
Bottom
BR
Code
import { For } from 'solid-js';
import { Tag, Tooltip, type TooltipPosition } from '@fibrous/semi-ui-solid';

type Item = [TooltipPosition, string];

const tops: Item[] = [
    ['topLeft', 'TL'],
    ['top', 'Top'],
    ['topRight', 'TR'],
];
const lefts: Item[] = [
    ['leftTop', 'LT'],
    ['left', 'Left'],
    ['leftBottom', 'LB'],
];
const rights: Item[] = [
    ['rightTop', 'RT'],
    ['right', 'Right'],
    ['rightBottom', 'RB'],
];
const bottoms: Item[] = [
    ['bottomLeft', 'BL'],
    ['bottom', 'Bottom'],
    ['bottomRight', 'BR'],
];

function Tips(props: { items: Item[]; width?: string }) {
    return (
        <For each={props.items}>
            {([position, label]) => (
                <Tooltip
                    content={
                        <article>
                            <p>hi bytedance</p>
                            <p>hi bytedance</p>
                        </article>
                    }
                    arrowPointAtCenter={false}
                    position={position}
                >
                    {(trigger) => (
                        <Tag style={{ margin: '8px', padding: '20px', width: props.width }} {...trigger()}>
                            {label}
                        </Tag>
                    )}
                </Tooltip>
            )}
        </For>
    );
}

export default function TooltipDemo() {
    return (
        <div>
            <div style={{ 'margin-left': '80px', 'white-space': 'nowrap' }}>
                <Tips items={tops} />
            </div>
            <div style={{ width: '80px', float: 'left' }}>
                <Tips items={lefts} width="60px" />
            </div>
            <div style={{ width: '40px', 'margin-left': '300px' }}>
                <Tips items={rights} width="60px" />
            </div>
            <div style={{ 'margin-left': '80px', clear: 'both', 'white-space': 'nowrap' }}>
                <Tips items={bottoms} width="60px" />
            </div>
        </div>
    );
}

Arrow Point at Center

By default arrowPointAtCenter=true, the small triangle always points to the center of the children element.
You can set it to false and the little triangle will no longer keep pointing to the center of the element. The popover is aligned to the edges of the children

Code
import { Button, Tooltip } from '@fibrous/semi-ui-solid';

export default function TooltipDemo() {
    return (
        <>
            <div>
                <Tooltip position="topLeft" content="semi design tooltip">
                    {(trigger) => (
                        <Button type="secondary" style={{ 'margin-right': '8px' }} {...trigger()}>
                            指向元素中心
                        </Button>
                    )}
                </Tooltip>
            </div>

            <div style={{ 'margin-top': '20px' }}>
                <Tooltip content="semi design tooltip" arrowPointAtCenter={false} position="topLeft">
                    {(trigger) => (
                        <Button type="secondary" style={{ 'margin-right': '8px', width: '120px' }} {...trigger()}>
                            边缘对齐
                        </Button>
                    )}
                </Tooltip>
            </div>
        </>
    );
}

Trigger Timing

  • Configure the timing of the trigger display, the default is hover, optional hover / focus / click / custom / contextMenu.
  • When set to custom, it needs to be used in conjunction with the visibleattribute, at which point the display is completely controlled
  • The contextMenu (right-click) trigger is available




Code
import { createSignal } from 'solid-js';
import { Button, Input, Radio, RadioGroup, Tooltip } from '@fibrous/semi-ui-solid';

export default function TooltipDemo() {
    const [visible, setVisible] = createSignal(false);

    return (
        <div>
            <Tooltip content="hi bytedance">
                {(trigger) => (
                    <Button theme="solid" type="tertiary" style={{ 'margin-bottom': '20px' }} {...trigger()}>
                        悬停显示
                    </Button>
                )}
            </Tooltip>
            <br />
            <Tooltip content="hi bytedance" trigger="click">
                {(trigger) => (
                    <Button style={{ 'margin-bottom': '20px' }} {...trigger()}>
                        点击显示
                    </Button>
                )}
            </Tooltip>
            <br />
            <Tooltip content="hi bytedance" trigger="focus">
                {(trigger) => <Input aria-label="聚焦显示" style={{ width: '100px', 'margin-bottom': '20px' }} placeholder="聚焦显示" {...trigger()} />}
            </Tooltip>
            <br />
            <Tooltip content="hi bytedance" trigger="contextMenu">
                {(trigger) => (
                    <Button theme="solid" type="secondary" style={{ 'margin-bottom': '20px' }} {...trigger()}>
                        右键点击展示
                    </Button>
                )}
            </Tooltip>
            <br />
            <Tooltip content="hi bytedance" trigger="custom" visible={visible()}>
                {(trigger) => (
                    <span style={{ display: 'inline-block' }} {...trigger({ tabIndex: -1 })}>
                        <RadioGroup type="button" onChange={(e) => setVisible(Boolean(e.target.value))} value={visible()}>
                            <Radio value>受控显示</Radio>
                            <Radio value={false}>受控隐藏</Radio>
                        </RadioGroup>
                    </span>
                )}
            </Tooltip>
        </div>
    );
}

Conditional trigger (condition)

When condition={false}, Tooltip will not respond to hover/click/focus triggers (with trigger="custom", visible still shows it); when condition changes from true to false, an open popup closes at once; with visible controlled it only calls onVisibleChange(false), and the popup closes once the owner sets visible to false.

condition
Code
import { createSignal } from 'solid-js';
import { Button, Space, Switch, Tooltip, Typography } from '@fibrous/semi-ui-solid';

export default function TooltipDemo() {
    const [enabled, setEnabled] = createSignal(true);
    const content = () => `condition is ${enabled() ? 'true' : 'false'}`;

    return (
        <Space align="center">
            <Typography.Text id="tooltip-condition-label">condition</Typography.Text>
            <Switch aria-labelledby="tooltip-condition-label" checked={enabled()} onChange={setEnabled} />
            <Tooltip content={content()} trigger="hover" condition={enabled()}>
                {(trigger) => <Button {...trigger()}>Hover me</Button>}
            </Tooltip>
            <Tooltip content={content()} trigger="click" condition={enabled()}>
                {(trigger) => <Button {...trigger()}>Click me</Button>}
            </Tooltip>
        </Space>
    );
}

Override Style

Configure specific styles for the pop-up layer through class and style, such as overriding the default maxWidth (240px)

Custom Style And ClassName
Code
import { Tag, Tooltip } from '@fibrous/semi-ui-solid';

export default function TooltipDemo() {
    return (
        <div>
            <Tooltip style={{ 'max-width': '320px' }} class="another-classname" content="hi semi semi semi semi semi semi semi">
                {(trigger) => (
                    <Tag style={{ 'margin-right': '8px' }} {...trigger()}>
                        Custom Style And ClassName
                    </Tag>
                )}
            </Tooltip>
        </div>
    );
}

Use with Popver or Popconfirm

Tooltip, Popconfirm, and Popover all bind related events on the trigger (mouseenter / mouseleave / click ....) and add trigger attributes such as aria-describedby and data-popupid.
When the outer component receives plain content as children, it is wrapped in the outer component's own span, which cannot describe the focusable element inside it, and a warning is logged in development.
To keep each component's own trigger attributes, add a layer of elements (div or span) in the middle as the trigger of the outer one: the outer component spreads {...trigger({ tabIndex: -1 })} on it through function children, and the inner one spreads {...trigger()} on its real trigger element.

Code
import { Button, Popconfirm, Tooltip } from '@fibrous/semi-ui-solid';

export default function TooltipDemo() {
    return (
        <Popconfirm content="是否确认删除" title="确认" style={{ width: '320px' }}>
            {(popconfirmTrigger) => (
                <span style={{ display: 'inline-block' }} {...popconfirmTrigger({ tabIndex: -1 })}>
                    <Tooltip content="删除评价">
                        {(tooltipTrigger) => (
                            <Button type="danger" {...tooltipTrigger()}>
                                删除
                            </Button>
                        )}
                    </Tooltip>
                </span>
            )}
        </Popconfirm>
    );
}

Show Tooltip only when the content overflows

Semi provides the Typography component for this scenario, so you do not need to check for overflow yourself before showing a Tooltip. For details, see the Typography documentation.

是一个很长很长很长很长5号标题

是一个很长很长很长很长的链接

多行截断,展示 Popover:Semi Design 是由抖音前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。

Code
import { Typography } from '@fibrous/semi-ui-solid';

const { Paragraph, Title, Text } = Typography;

export default function TooltipDemo() {
    return (
        <div>
            <Title heading={5} ellipsis={{ showTooltip: true }} style={{ width: '250px' }}>
                是一个很长很长很长很长5号标题
            </Title>
            <br />
            <Text link ellipsis={{ showTooltip: true, pos: 'middle' }} style={{ width: '150px' }}>
                是一个很长很长很长很长的链接
            </Text>
            <br />
            <br />
            <Paragraph ellipsis={{ rows: 3, showTooltip: { type: 'popover', opts: { style: { width: '300px' } } } }} style={{ width: '300px' }}>
                多行截断,展示 Popover:Semi Design 是由抖音前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。
            </Paragraph>
        </div>
    );
}

API Reference


Props

PropertyDescriptionTypeDefault
afterCloseCalled after the popup has closed and its leave animation has finished.Type() => void-
afterOpenCalled after the popup has opened and its enter animation has finished, or right after opening when there is no enter animation; not called for an open that is closed before the enter animation ends.Type() => void-
aria-labelaria-label of the popup when its role is dialog; ignored for other roles.string-
aria-labelledbyaria-labelledby of the popup when its role is dialog; ignored for other roles.string-
arrowPointAtCenterWhether the arrow points at the center of the trigger. Requires showArrow.booleantrue
autoAdjustOverflowWhether the popup flips automatically when it would be clipped.booleantrue
childrenThe trigger. A function receives getTriggerProps; spread getTriggerProps(ownProps) on the element that takes focus. Anything else is wrapped in a span owned by Tooltip.TypeJSX.Element | ((getTriggerProps: GetTriggerProps) => JSX.Element)-
classClass name of the popup.string-
clickToHideWhether a click on the popup or anything inside it closes the popup.boolean-
clickTriggerToHideWhether a click on the trigger closes an open popup.boolean-
closeOnEscWhether Esc closes the open popup. With trigger custom it only calls onEscKeyDown and the caller decides whether to close.booleantrue
conditionWhether the popup may be triggered. Only an explicit false disables the hover, click and focus triggers; with trigger custom, visible still shows the popup. Changing it from true to false closes an open popup at once; with a controlled visible it only calls onVisibleChange(false), and the popup closes once the caller writes back.booleantrue
contentPopup content: an element, or a function returning one. The function receives initialFocusRef to name the element that takes focus once the popup opens.JSX.Element | RenderContent-
disableArrowKeyDownWhether the arrow keys on the trigger are prevented from moving focus into the popup.booleanfalse
disableFocusListenerWhether keyboard focus is ignored when trigger is hover.booleanfalse
guardFocusWhether Tab cycles inside the popup while focus is in it.booleanfalse
keepDOMWhether the popup content stays mounted while closed.booleanfalse
motionWhether the popup is animated.booleantrue
mouseEnterDelayMilliseconds before the popup shows after the pointer enters. Applies to the hover and focus triggers.number50
mouseLeaveDelayMilliseconds before the popup hides after the pointer leaves. Applies to the hover and focus triggers; not less than mouseEnterDelay.number50
onClickOutSideCalled when the mouse is pressed outside of the trigger and the open popup, with the native MouseEvent. Only for trigger click, contextMenu or custom, or with clickTriggerToHide.Type(e: MouseEvent) => void-
onEscKeyDownCalled with the native KeyboardEvent when Esc is pressed while the popup is open and closeOnEsc is true.Type(e: KeyboardEvent) => void-
onOpenCalled once per opening when the popup is shown, as its enter animation starts, before afterOpen, controlled or not; not called for a hover open withdrawn because the pointer already left.Type() => void-
onVisibleChangeCalled when the visibility of the popup changes; with a controlled visible it reports only requests from user interaction, not changes the caller makes to visible.Type(visible: boolean) => void-
positionPosition of the popup relative to the trigger element.Position"top"
prefixClsClass prefix of the popup. A custom value drops the Tooltip styles.string"semi-tooltip"
preventScrollWhether focusing an element avoids scrolling it into view.boolean-
refReceives the instance methods listed under Methods below.TypeRef<TooltipHandle>-
returnFocusOnCloseWhether focus returns to the trigger after the popup closes.booleanfalse
roleARIA role of the popup.string"tooltip"
showArrowWhether the arrow is shown. An element renders as a custom arrow.boolean | JSX.Elementtrue
spacingDistance in pixels between the popup and the trigger. An object sets the horizontal and vertical distances.number | { x: number; y: number }8
stopPropagationWhether click, mousedown and focus in/out events on the popup stop propagating.boolean-
styleInline style of the popup. The arrow color reads background-color of an object style only.JSX.CSSProperties | string-
triggerWhen the popup shows. With custom, visible alone decides.'hover' | 'focus' | 'click' | 'custom' | 'contextMenu'"hover"
visibleWhether the popup is shown (controlled). When given, the trigger, outside clicks and Esc only request a change through onVisibleChange, which takes effect once the caller writes it back.boolean-
wrapperClassNameClass name of the span that wraps the trigger when a wrapper is needed.string-
wrapperIdid of the popup element, referenced by the trigger's ARIA attributes. Generated when omitted.string-
wrapWhenSpecialWhether a disabled or loading trigger is wrapped in a span so that it still triggers the popup.booleantrue

Methods

MethodDescriptionType
focusTriggerFocuses the trigger. Only works when returnFocusOnClose is true and trigger is not custom.Type() => void
getPopupIdReturns the id of the popup.Type() => string
hideCloses the popup at once without moving focus and calls onVisibleChange(false). With a controlled visible it only calls onVisibleChange(false), and the popup closes once the caller writes back.Type() => void
rePositionRecomputes the popup position from the trigger's current position at once. The popup already follows scrolling, window resizing, trigger moves and size changes of the trigger or popup, so this is rarely needed; following a trigger move lands a frame late, so call this to align within the same frame.Type() => void

Tooltip has no root DOM element: class and style land on the popup, and other undeclared attributes and events are discarded. The trigger attributes are added to the trigger element only, and ref returns the component methods.

Accessibility

ARIA

  • Tooltip has a tooltip role, following the definition of Tooltip in the WAI-ARIA specification
  • Tooltip's content and children
    • about content
      • The wrapper of content will be automatically added with id attribute to match the aria-describedby of children and associate content with children
    • about children
      • There should be an explicit connection between the content of the Tooltip and its children. Tooltip will automatically add the aria-describedby attribute to the children element, the value is the id of the content wrapper.
      • If the children of your Tooltip are Icon and do not contain visible text, we recommend that you add the aria-label attribute to the children to describe accordingly
// Good practices, add aria-label to the tooltip trigger
<Tooltip content={<p id="description">Edit your setting</p>}>{(trigger) => <IconSetting aria-label="Settings" {...trigger()} />}</Tooltip>

Content Guidelines

  • Only display information description and guidance, do not display error information
  • Only extra links and buttons not in tooltip
  • Try to simplify the description to one sentence without showing punctuation marks

Design Tokens

These are the component's internal Sass variables and cannot be overridden. To customize styles, override the --semi-* CSS variables (see the Design Token page) or write unlayered CSS rules.
Sass variableValueUsage
$animation_duration-tooltip_out100mstooltip-显示及收起动画-动画持续时间
$animation_function-tooltip_outcubic-bezier(0.215, 0.61, 0.355, 1)tooltip-显示及收起动画-动画插值函数
$horizontal-rate0.24水平方向矫正因子
$vertical-rate0垂直方向矫正因子
$motion-zoom_scale-from0.8进出场动画的初始缩放
$color-tooltip-bg-defaultvar(--semi-grey-7)工具提示默认背景色
$color-tooltip-text-defaultvar(--semi-color-bg-0)工具提示默认文字颜色
$color-tooltip-icon-defaultvar(--semi-grey-7)工具提示默认图标颜色
$color-tooltip_arrow-icon-default$color-tooltip-icon-default工具提示默认小三角箭头颜色
$radius-tooltipvar(--semi-border-radius-medium)工具提示默认圆角
$spacing-tooltip_content-paddingLeft$spacing-base-tight工具提示内容左侧内边距
$spacing-tooltip_content-paddingRight$spacing-base-tight工具提示内容右侧内边距
$spacing-tooltip_content-paddingTop$spacing-tight工具提示内容顶部内边距
$spacing-tooltip_content-paddingBottom$spacing-tight工具提示内容底部内边距
$font-tooltip-fontSize$font-size-regular工具提示文本字号
$width-tooltip240px工具提示宽度 - 默认
$width-tooltip_arrow24px工具提示小三角箭头宽度 - 水平
$height-tooltip_arrow7px工具提示小三角箭头高度 - 水平
$height-tooltip_arrow_vertical24px工具提示小三角箭头高度 - 垂直
$width-tooltip_arrow_vertical7px工具提示小三角箭头宽度度 - 垂直
$spacing-tooltip_arrow_offset-x1px水平方向渲染会有缝隙,所以加个偏移量
$spacing-tooltip_arrow_offset-y1px垂直方向渲染会有缝隙,所以加个偏移量
$spacing-tooltip_arrow_adjusted_offset-y6px垂直方向上的校正偏移量,主要修正三角形有棱角的问题
$spacing-tooltip_arrow_adjusted_offset-xmath.round($horizontal-rate * $width-tooltip_arrow)水平方向上的校正偏移量,主要修正三角形有棱角的问题
$filter-tooltip-bgnone

FAQ

  • Why do some forms of content not wrap when the content in Tooltip is very long?
    The Tooltip popup sets overflow-wrap to break-word, so an overlong word breaks onto the next line. Different language content (e.g. English, Chinese, combination of English and Chinese) needs different line breaking; if the default does not fit, adjust the line break related CSS properties through style and className (class in Vue).