Demos
How to import
<script setup lang="ts">
import { Tooltip } from '@fibrous/semi-ui-vue';
</script>
Cautions
Tooltip applies the trigger attributes and event listeners to the trigger in the default slot, and positions the popup against the real DOM element of that trigger, so the default slot supports the following:
- A single actual DOM element, such as span, div, p, etc.
- A single component whose undeclared attributes reach its root DOM element (the default attrs fallthrough is enough). For a component with several root nodes, such as a nested Tooltip, the first element is the anchor
- Text or several nodes, which Tooltip wraps in a span that becomes the trigger
Code
<script setup lang="ts">
import { Space, Tag, Tooltip } from '@fibrous/semi-ui-vue';
const style = {
border: '2px solid var(--semi-color-border)',
paddingLeft: '4px',
paddingRight: '4px',
borderRadius: '4px',
};
</script>
<template>
<Space>
<Tooltip>
<Tag :style="style">Component</Tag>
<template #content>semi design</template>
</Tooltip>
<Tooltip>
Text
<template #content>semi design</template>
</Tooltip>
<Tooltip>
<span :style="style">DOM</span>
<template #content>semi design</template>
</Tooltip>
</Space>
</template>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
Code
<script setup lang="ts">
import { Tag, Tooltip, type Position } from '@fibrous/semi-ui-vue';
const tops: [Position, string][] = [
['topLeft', 'TL'],
['top', 'Top'],
['topRight', 'TR'],
];
const lefts: [Position, string][] = [
['leftTop', 'LT'],
['left', 'Left'],
['leftBottom', 'LB'],
];
const rights: [Position, string][] = [
['rightTop', 'RT'],
['right', 'Right'],
['rightBottom', 'RB'],
];
const bottoms: [Position, string][] = [
['bottomLeft', 'BL'],
['bottom', 'Bottom'],
['bottomRight', 'BR'],
];
</script>
<template>
<div>
<div :style="{ marginLeft: '80px', whiteSpace: 'nowrap' }">
<Tooltip v-for="[position, label] in tops" :key="position" :arrowPointAtCenter="false" :position="position">
<Tag :style="{ margin: '8px', padding: '20px' }">{{ label }}</Tag>
<template #content>
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
</template>
</Tooltip>
</div>
<div :style="{ width: '80px', float: 'left' }">
<Tooltip v-for="[position, label] in lefts" :key="position" :arrowPointAtCenter="false" :position="position">
<Tag :style="{ margin: '8px', padding: '20px', width: '60px' }">{{ label }}</Tag>
<template #content>
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
</template>
</Tooltip>
</div>
<div :style="{ width: '40px', marginLeft: '300px' }">
<Tooltip v-for="[position, label] in rights" :key="position" :arrowPointAtCenter="false" :position="position">
<Tag :style="{ margin: '8px', padding: '20px', width: '60px' }">{{ label }}</Tag>
<template #content>
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
</template>
</Tooltip>
</div>
<div :style="{ marginLeft: '80px', clear: 'both', whiteSpace: 'nowrap' }">
<Tooltip v-for="[position, label] in bottoms" :key="position" :arrowPointAtCenter="false" :position="position">
<Tag :style="{ margin: '8px', padding: '20px', width: '60px' }">{{ label }}</Tag>
<template #content>
<article>
<p>hi bytedance</p>
<p>hi bytedance</p>
</article>
</template>
</Tooltip>
</div>
</div>
</template>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
<script setup lang="ts">
import { Button, Tooltip } from '@fibrous/semi-ui-vue';
</script>
<template>
<div>
<Tooltip position="topLeft">
<Button :style="{ marginRight: '8px' }" type="secondary">指向元素中心</Button>
<template #content>semi design tooltip</template>
</Tooltip>
</div>
<div :style="{ marginTop: '20px' }">
<Tooltip :arrowPointAtCenter="false" position="topLeft">
<Button :style="{ marginRight: '8px', width: '120px' }" type="secondary">边缘对齐</Button>
<template #content>semi design tooltip</template>
</Tooltip>
</div>
</template>Trigger Timing
- Configure the timing of the trigger display, the default is
hover, optionalhover/focus/click/custom/contextMenu. - When set to
custom, it needs to be used in conjunction with thevisibleattribute, at which point the display is completely controlled - The
contextMenu(right-click) trigger is available
Code
<script setup lang="ts">
import { ref } from 'vue';
import { Button, Input, Radio, RadioGroup, Tooltip, type RadioValue } from '@fibrous/semi-ui-vue';
const visible = ref<RadioValue | undefined>(0);
</script>
<template>
<div>
<Tooltip>
<Button :style="{ marginBottom: '20px' }" theme="solid" type="tertiary">悬停显示</Button>
<template #content>hi bytedance</template>
</Tooltip>
<br />
<Tooltip trigger="click">
<Button :style="{ marginBottom: '20px' }">点击显示</Button>
<template #content>hi bytedance</template>
</Tooltip>
<br />
<Tooltip trigger="focus">
<Input aria-label="聚焦显示" placeholder="聚焦显示" :style="{ width: '100px', marginBottom: '20px' }" />
<template #content>hi bytedance</template>
</Tooltip>
<br />
<Tooltip trigger="contextMenu">
<Button :style="{ marginBottom: '20px' }" theme="solid" type="secondary">右键点击展示</Button>
<template #content>hi bytedance</template>
</Tooltip>
<br />
<Tooltip trigger="custom" :visible="visible === 1">
<span :style="{ display: 'inline-block' }">
<RadioGroup v-model="visible" type="button">
<Radio :value="1">受控显示</Radio>
<Radio :value="0">受控隐藏</Radio>
</RadioGroup>
</span>
<template #content>hi bytedance</template>
</Tooltip>
</div>
</template>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 emits update:visible and visibleChange(false), and the popup closes once the owner sets visible to false.
Code
<script setup lang="ts">
import { computed, ref } from 'vue';
import { Button, Space, Switch, Tooltip, Typography } from '@fibrous/semi-ui-vue';
const { Text } = Typography;
const enabled = ref(true);
const content = computed(() => `condition is ${enabled.value ? 'true' : 'false'}`);
</script>
<template>
<Space align="center">
<Text id="tooltip-condition-label">condition</Text>
<Switch v-model="enabled" aria-labelledby="tooltip-condition-label" />
<Tooltip :condition="enabled" trigger="hover">
<Button>Hover me</Button>
<template #content>{{ content }}</template>
</Tooltip>
<Tooltip :condition="enabled" trigger="click">
<Button>Click me</Button>
<template #content>{{ content }}</template>
</Tooltip>
</Space>
</template>Override Style
Configure specific styles for the pop-up layer through class and style, such as overriding the default maxWidth (240px)
Code
<script setup lang="ts">
import { Tag, Tooltip } from '@fibrous/semi-ui-vue';
</script>
<template>
<div>
<Tooltip class="another-classname" :style="{ maxWidth: '320px' }">
<Tag :style="{ marginRight: '8px' }">Custom Style And ClassName</Tag>
<template #content>hi semi semi semi semi semi semi semi</template>
</Tooltip>
</div>
</template>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. If they are nested directly, the inner component discards the ARIA attributes, tabindex, data-popupid and arrow key handling added by the outer one.
Add a layer of elements (div or span) in the middle as the trigger of the outer one.
Code
<script setup lang="ts">
import { Button, Popconfirm, Tooltip } from '@fibrous/semi-ui-vue';
</script>
<template>
<Popconfirm :style="{ width: '320px' }">
<span :style="{ display: 'inline-block' }">
<Tooltip>
<Button type="danger">删除</Button>
<template #content>删除评价</template>
</Tooltip>
</span>
<template #title>确认</template>
<template #content>是否确认删除</template>
</Popconfirm>
</template>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
<script setup lang="ts">
import { Typography } from '@fibrous/semi-ui-vue';
const { Paragraph, Title, Text } = Typography;
</script>
<template>
<div>
<Title :ellipsis="{ showTooltip: true }" :heading="5" :style="{ width: '250px' }"> 是一个很长很长很长很长5号标题 </Title>
<br />
<Text :ellipsis="{ showTooltip: true, pos: 'middle' }" link :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>
</template>API Reference
Props
| Property | Description | Type | Default |
|---|---|---|---|
| motion | Whether the popup is animated. | boolean | semiDefault("Tooltip", "motion", true) |
| autoAdjustOverflow | Whether the popup flips automatically when it would be clipped. | boolean | semiDefault("Tooltip", "autoAdjustOverflow", true) |
| position | Position of the popup relative to the trigger element. | Type"top" | "bottom" | "left" | "right" | "topLeft" | "topRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom" | "bottomLeft" | "bottomRight" | "leftTopOver" | "rightTopOver" | "leftBottomOver" | "rightBottomOver" | semiDefault("Tooltip", "position", "top") |
| prefixCls | Class prefix of the popup. A custom value drops the Tooltip styles. | string | semiDefault("Tooltip", "prefixCls", cssClasses.PREFIX) |
| spacing | Distance in pixels between the popup and the trigger. An object sets the horizontal and vertical distances. | number | { x: number; y: number; } | semiDefault("Tooltip", "spacing", numbers.SPACING) |
| showArrow | Whether the arrow is shown. | boolean | semiDefault("Tooltip", "showArrow", true) |
| role | ARIA role of the popup. | string | semiDefault("Tooltip", "role", "tooltip") |
| ariaLabel | aria-label of the popup when its role is dialog. | string | semiGlobalDefault("Tooltip", "ariaLabel") |
| ariaLabelledby | aria-labelledby of the popup when its role is dialog. | string | semiGlobalDefault("Tooltip", "ariaLabelledby") |
| ariaDescribedby | aria-describedby of the popup when its role is dialog. | string | semiGlobalDefault("Tooltip", "ariaDescribedby") |
| arrowPointAtCenter | Whether the arrow points at the center of the trigger. Requires showArrow. | boolean | semiDefault("Tooltip", "arrowPointAtCenter", true) |
| wrapWhenSpecial | Whether a disabled or loading trigger is wrapped in a span so that it still triggers the popup. | boolean | semiDefault("Tooltip", "wrapWhenSpecial", true) |
| stopPropagation | Whether click, mousedown and focus in/out events on the popup stop propagating. | boolean | semiGlobalDefault("Tooltip", "stopPropagation") |
| wrapperClassName | Class name of the span that wraps the trigger when a wrapper is needed. | string | semiGlobalDefault("Tooltip", "wrapperClassName") |
| wrapperId | id of the popup element, referenced by the trigger's ARIA attributes. Generated when omitted. | string | undefined |
| keepDOM | Whether the popup content stays mounted while closed. | boolean | semiDefault("Tooltip", "keepDOM", false) |
| clickToHide | Whether a click on the popup or anything inside it closes the popup. | boolean | semiGlobalDefault("Tooltip", "clickToHide") |
| clickTriggerToHide | Whether a click on the trigger closes an open popup. | boolean | semiGlobalDefault("Tooltip", "clickTriggerToHide") |
| closeOnEsc | Whether Esc closes the open popup. With trigger custom it only emits escKeyDown and the caller decides whether to close. | boolean | semiDefault("Tooltip", "closeOnEsc", true) |
| condition | Whether 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 emits update:visible and visibleChange(false), and the popup closes once the caller writes back. | boolean | semiDefault("Tooltip", "condition", true) |
| disableArrowKeyDown | Whether the arrow keys on the trigger are prevented from moving focus into the popup. | boolean | semiDefault("Tooltip", "disableArrowKeyDown", false) |
| disableFocusListener | Whether keyboard focus is ignored when trigger is hover. | boolean | semiDefault("Tooltip", "disableFocusListener", false) |
| guardFocus | Whether Tab cycles inside the popup while focus is in it. | boolean | semiDefault("Tooltip", "guardFocus", false) |
| mouseEnterDelay | Milliseconds before the popup shows after the pointer enters. Applies to the hover and focus triggers. | number | semiDefault("Tooltip", "mouseEnterDelay", numbers.MOUSE_ENTER_DELAY) |
| mouseLeaveDelay | Milliseconds before the popup hides after the pointer leaves. Applies to the hover and focus triggers; not less than mouseEnterDelay. | number | semiDefault("Tooltip", "mouseLeaveDelay", numbers.MOUSE_LEAVE_DELAY) |
| preventScroll | Whether focusing an element avoids scrolling it into view. | boolean | semiGlobalDefault("Tooltip", "preventScroll") |
| returnFocusOnClose | Whether focus returns to the trigger after the popup closes. | boolean | semiDefault("Tooltip", "returnFocusOnClose", false) |
| trigger | When the popup shows. With custom, visible alone decides. | "hover" | "focus" | "click" | "custom" | "contextMenu" | semiDefault("Tooltip", "trigger", "hover") |
| visible | Whether the popup is shown, used with v-model:visible. When given, this value decides. | boolean | semiGlobalDefault("Tooltip", "visible") |
Events
| Event | Description | Callback parameters |
|---|---|---|
| open | Emitted once per opening when the popup is shown, as its enter animation starts, before afterOpen, controlled or not; not emitted for a hover open withdrawn because the pointer already left. | [] |
| update:visible | Emitted when the popup should show or hide, for v-model:visible to write back. | [visible: boolean] |
| visibleChange | Emitted when the visibility of the popup changes; with a controlled visible it reports only requests from user interaction, not changes the parent makes to visible. | [visible: boolean] |
| clickOutSide | Emitted when the mouse is pressed outside of the trigger and the open popup. Only for trigger click, contextMenu or custom, or with clickTriggerToHide. | [event: MouseEvent] |
| escKeyDown | Emitted when Esc is pressed while the popup is open and closeOnEsc is true. | [event: KeyboardEvent] |
| afterOpen | Emitted after the popup has opened and its enter animation has finished, or right after opening when there is no enter animation, controlled or not; not emitted for an open that is closed before the enter animation ends. | [] |
| afterClose | Emitted after the popup has closed and its leave animation has finished. | [] |
Slots
| Slot | Description | Scope parameters |
|---|---|---|
| default | The trigger. A single element or component receives the trigger attributes directly; text or several nodes are wrapped in a span. | any |
| content | Popup content. The scope parameter initialFocusRef binds to the element that receives focus when the popup opens. | TypeRenderContentProps<FocusTarget> |
| arrow | Replaces the arrow. | any |
Exposes
| Name | Description | Type |
|---|---|---|
| focusTrigger | Focuses the trigger element. Only works when returnFocusOnClose is true and trigger is not custom. | Type() => void |
| getPopupId | Returns the id of the popup element. | Type() => string |
| hide | Closes the popup. With a controlled visible it only emits update:visible and visibleChange for the caller to write back. | Type() => void |
| rePosition | Recomputes 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 |
class and style land on the popup; other undeclared attributes are discarded. The trigger attributes are added to the trigger element only. When the subtree is cached by <KeepAlive> (deactivated), the popup is treated as closed and update:visible and visibleChange are emitted with false; a popup whose visible is one-way bound to true is hidden with the cached subtree and shows again once reactivated.
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-describedbyof children and associate content with children
- The wrapper of content will be automatically added with id attribute to match the
- about children
- There should be an explicit connection between the content of the Tooltip and its children. Tooltip will automatically add the
aria-describedbyattribute 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-labelattribute to the children to describe accordingly
- There should be an explicit connection between the content of the Tooltip and its children. Tooltip will automatically add the
- about content
<!-- Good practices, add aria-label to the tooltip trigger -->
<Tooltip>
<IconSetting aria-label="Settings" />
<template #content><p id="description">Edit your setting</p></template>
</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
| Sass variable | Value | Usage |
|---|---|---|
| $animation_duration-tooltip_out | 100ms | tooltip-显示及收起动画-动画持续时间 |
| $animation_function-tooltip_out | cubic-bezier(0.215, 0.61, 0.355, 1) | tooltip-显示及收起动画-动画插值函数 |
| $horizontal-rate | 0.24 | 水平方向矫正因子 |
| $vertical-rate | 0 | 垂直方向矫正因子 |
| $motion-zoom_scale-from | 0.8 | 进出场动画的初始缩放 |
| $color-tooltip-bg-default | var(--semi-grey-7) | 工具提示默认背景色 |
| $color-tooltip-text-default | var(--semi-color-bg-0) | 工具提示默认文字颜色 |
| $color-tooltip-icon-default | var(--semi-grey-7) | 工具提示默认图标颜色 |
| $color-tooltip_arrow-icon-default | $color-tooltip-icon-default | 工具提示默认小三角箭头颜色 |
| $radius-tooltip | var(--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-tooltip | 240px | 工具提示宽度 - 默认 |
| $width-tooltip_arrow | 24px | 工具提示小三角箭头宽度 - 水平 |
| $height-tooltip_arrow | 7px | 工具提示小三角箭头高度 - 水平 |
| $height-tooltip_arrow_vertical | 24px | 工具提示小三角箭头高度 - 垂直 |
| $width-tooltip_arrow_vertical | 7px | 工具提示小三角箭头宽度度 - 垂直 |
| $spacing-tooltip_arrow_offset-x | 1px | 水平方向渲染会有缝隙,所以加个偏移量 |
| $spacing-tooltip_arrow_offset-y | 1px | 垂直方向渲染会有缝隙,所以加个偏移量 |
| $spacing-tooltip_arrow_adjusted_offset-y | 6px | 垂直方向上的校正偏移量,主要修正三角形有棱角的问题 |
| $spacing-tooltip_arrow_adjusted_offset-x | math.round($horizontal-rate * $width-tooltip_arrow) | 水平方向上的校正偏移量,主要修正三角形有棱角的问题 |
| $filter-tooltip-bg | none |
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).