import {
Icon,
IconButton,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { Bell } from '@vega-ui/icons';
<Tooltip>
<TooltipTrigger asChild>
<IconButton variant='secondary' appearance='ghost' aria-label='Notifications'><Icon><Bell /></Icon></IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>Notifications</Text>
</TooltipContent>
</Tooltip>- Поддержка браузеров
- Недавно доступно, 2025
- Chrome 116
- Edge 116
- Firefox 125
- Safari 17
- Safari на iOS 18.3
- Размер в бандле
- Протестирован в
- 20 тестов · 6 нояб.
- История версий
- Добавлен в v1.0.0, последнее изменение в v3.0.0
- Popover
- Oklab and OkLCh — приходит из дизайн-токенов
- Resize observer
- Intersection observer
- Visual viewport API
- Individual transform properties
- :where()
Кратко
Tooltip — составной компонент для лёгких всплывающих описаний, привязанных к триггеру. Он должен нести только дополнительную, несущественную информацию.
Импорт
import {
Tooltip,
TooltipTrigger,
TooltipContent,
TooltipArrow,
type TooltipProps,
} from '@vega-ui/react';Экспортируемые типы
TooltipPropsTooltipTriggerPropsTooltipContentPropsTooltipArrowProps
Минимальная композиция
import {
Button,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
export default function MinimalComposition() {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button>Publish</Button>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>Anyone with the link can view the page</Text>
</TooltipContent>
</Tooltip>
);
}Обязательные части
Tooltip: корневое состояние и провайдер контекстаTooltipTrigger: поверхность триггераTooltipContent: видимое содержимое тултипа
Необязательные части
TooltipArrow: визуальная стрелка всплывающего содержимого
Порядок композиции
TooltipTooltipTriggerTooltipContentTooltipArrow
Варианты
- Простой тултип-метка или более развёрнутый описательный тултип
- Контролируемое состояние открытия, когда это нужно логике потребителя
Пограничные случаи
- Тултипы не должны содержать критичных действий или обязательных инструкций.
- Тултип показывается при наведении мышью или пером и при фокусе с клавиатуры, но никогда по касанию, поэтому пользователям сенсорных экранов та же информация нужна в другом месте.
- Содержимое триггера должно оставаться понятным и без тултипа.
Частые ошибки
- Обязательные инструкции, спрятанные в тултипе.
- Содержимое тултипа как единственное доступное имя контрола.
- Интерактивные сценарии внутри содержимого тултипа.
Примеры
Базовый
Базовый: кнопки-иконки с подсказками
Каждая кнопка-иконка названа собственным aria-label; тултип лишь добавляет подсказку и сочетание клавиш и, пока открыт, связан с кнопкой через aria-describedby. Он появляется при наведении после delayOpen и сразу при фокусе с клавиатуры, но никогда по касанию.
import {
Icon,
IconButton,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { ArchiveIcon, ReplyIcon, Trash2Icon } from '@vega-ui/icons';
const actions = [
{ label: 'Reply', hint: 'Reply to Maria Chen', icon: <ReplyIcon /> },
{ label: 'Archive', hint: 'Move to Archive · E', icon: <ArchiveIcon /> },
{ label: 'Delete', hint: 'Move to Trash · #', icon: <Trash2Icon /> },
];
export default function IconHint() {
return (
<div style={{ display: 'flex', gap: 'var(--spacing-2)' }}>
{actions.map((action) => (
<Tooltip key={action.label}>
<TooltipTrigger asChild>
<IconButton aria-label={action.label} variant='secondary' appearance='ghost'>
<Icon>{action.icon}</Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>{action.hint}</Text>
</TooltipContent>
</Tooltip>
))}
</div>
);
}Контролируемый/с состоянием
Контролируемый/с состоянием: отклик о копировании
Тултип контролируемый, поэтому обработчик клика может открыть его сразу после копирования и сменить текст на «Copied». Закрытие через onChangeOpen возвращает текст для следующего наведения.
import { useState } from 'react';
import {
Icon,
IconButton,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { CheckIcon, CopyIcon } from '@vega-ui/icons';
const inviteLink = 'https://northwind.dev/join/7KQ2-M9XA';
export default function StatefulCopyFeedback() {
const [open, setOpen] = useState(false);
const [copied, setCopied] = useState(false);
const changeOpen = (next: boolean) => {
setOpen(next);
if (!next) setCopied(false);
};
const copy = async () => {
await navigator.clipboard.writeText(inviteLink);
setCopied(true);
setOpen(true);
};
return (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 'var(--spacing-4)',
boxSizing: 'border-box',
width: '100%',
paddingBlock: 'var(--spacing-2)',
paddingInlineStart: 'var(--spacing-8)',
paddingInlineEnd: 'var(--spacing-2)',
background: 'var(--surface-primary)',
border: '1px solid var(--separator-opaque)',
borderRadius: 'var(--radius-5)',
}}
>
<Text size={3} style={{ flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontFamily: 'monospace' }}>{inviteLink}</Text>
<Tooltip delayOpen={0} open={open} onChangeOpen={changeOpen}>
<TooltipTrigger asChild>
<IconButton aria-label='Copy invite link' variant='secondary' appearance='ghost' style={{ flexShrink: 0 }} onClick={copy}>
<Icon>{copied ? <CheckIcon /> : <CopyIcon />}</Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>{copied ? 'Copied' : 'Copy invite link'}</Text>
</TooltipContent>
</Tooltip>
</div>
);
}Контролируемый/с состоянием: состояние открытия в вашем состоянии
onChangeOpen сообщает о запросах от наведения и фокуса, а open решает, что показано: чекбокс закрепляет подсказку открытой, что бы ни делал указатель. Строка ниже показывает оба значения.
import { useState } from 'react';
import {
Button,
Checkbox,
CheckboxCheckedIcon,
CheckboxIndeterminateIcon,
CheckboxIndicator,
CheckboxInput,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
export default function ControlledOpenState() {
const [open, setOpen] = useState(false);
const [pinned, setPinned] = useState(false);
return (
<div style={{ display: 'grid', gap: 'var(--spacing-8)', justifyItems: 'center', width: '100%' }}>
<Checkbox style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-4)' }}>
<CheckboxInput checked={pinned} onChange={(event) => setPinned(event.currentTarget.checked)} />
<CheckboxIndicator>
<CheckboxCheckedIcon />
<CheckboxIndeterminateIcon />
</CheckboxIndicator>
Keep the hint open
</Checkbox>
<Tooltip open={pinned || open} onChangeOpen={setOpen}>
<TooltipTrigger asChild>
<Button variant='secondary' appearance='ghost'>Save draft</Button>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>Drafts are saved automatically every minute</Text>
</TooltipContent>
</Tooltip>
<Text size={2} style={{ color: 'var(--label-secondary)' }}>
Hover or focus requests: {open ? 'open' : 'closed'} · shown: {pinned || open ? 'yes' : 'no'}
</Text>
</div>
);
}Форма/интеграция
Форма/интеграция: подсказка к необязательному полю
Метка остаётся видимой и даёт полю имя; кнопка с иконкой рядом объясняет, когда поле нужно. Тултип годится только для такого необязательного контекста: обязательные инструкции и ошибки должны быть видимым текстом.
import { useId, useState, type FormEvent } from 'react';
import {
Button,
Icon,
IconButton,
Label,
Text,
TextField,
TextFieldInput,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { InfoIcon } from '@vega-ui/icons';
export default function FieldHelper() {
const fieldId = useId();
const [saved, setSaved] = useState('');
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSaved(String(new FormData(event.currentTarget).get('vat')));
};
return (
<form onSubmit={onSubmit} style={{ display: 'grid', gap: 'var(--spacing-3)', width: '100%' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-2)' }}>
<Label htmlFor={fieldId}>VAT number (optional)</Label>
<Tooltip>
<TooltipTrigger asChild>
<IconButton aria-label='About the VAT number' size='xs' variant='secondary' appearance='ghost'>
<Icon size='xs'><InfoIcon /></Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>Printed on invoices for EU business customers</Text>
</TooltipContent>
</Tooltip>
</div>
<TextField>
<TextFieldInput id={fieldId} name='vat' placeholder='DE123456789' pattern='[A-Z]{2}[A-Z0-9]{8,12}' />
</TextField>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-6)', marginBlockStart: 'var(--spacing-9)' }}>
<Button type='submit' style={{ flexShrink: 0 }}>Save billing details</Button>
<Text size={2} role='status' style={{ minWidth: 0, color: 'var(--label-secondary)', overflowWrap: 'anywhere' }}>{saved ? `Saved ${saved}` : ''}</Text>
</div>
</form>
);
}Раскладка/оверлей
Раскладка/оверлей: тултип внутри диалога
Тултип рендерится в верхнем слое после диалога, поэтому показывается поверх модального окна и не обрезается им. delayOpen={0} показывает его сразу, а результат копирования меняет его текст.
import { useId, useState, type CSSProperties } from 'react';
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogTrigger,
Heading,
Icon,
IconButton,
Label,
Text,
TextField,
TextFieldInput,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { CheckIcon, CopyIcon, XIcon } from '@vega-ui/icons';
const apiKey = 'sk_live_51Hx9QaLk2mZ8vR4';
export default function TooltipInsideDialog() {
const titleId = useId();
const fieldId = useId();
const [copied, setCopied] = useState(false);
const copy = async () => {
await navigator.clipboard.writeText(apiKey);
setCopied(true);
};
return (
<Dialog onChangeOpen={() => setCopied(false)}>
<DialogTrigger asChild>
<Button variant='secondary' appearance='ghost'>Show API key</Button>
</DialogTrigger>
<DialogContent aria-labelledby={titleId}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-8)' }}>
<Heading id={titleId} as='h2' size={4} fontWeight={600}>Production API key</Heading>
<DialogClose asChild>
<IconButton
aria-label='Close'
variant='secondary'
appearance='ghost'
style={{ flexShrink: 0, '--icon-button-size': '24px', '--icon-button-br-ratio': 0.5 } as CSSProperties}
>
<Icon size='xs'><XIcon strokeWidth={2.5} /></Icon>
</IconButton>
</DialogClose>
</div>
<div style={{ display: 'grid', gap: 'var(--spacing-3)', marginBlockStart: 'var(--spacing-8)' }}>
<Label htmlFor={fieldId}>Key</Label>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-4)' }}>
<TextField style={{ flex: 1 }}>
<TextFieldInput id={fieldId} value={apiKey} readOnly />
</TextField>
<Tooltip delayOpen={0}>
<TooltipTrigger asChild>
<IconButton aria-label='Copy key' variant='secondary' appearance='ghost' style={{ flexShrink: 0 }} onClick={copy}>
<Icon>{copied ? <CheckIcon /> : <CopyIcon />}</Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>{copied ? 'Copied to clipboard' : 'Copy to clipboard'}</Text>
</TooltipContent>
</Tooltip>
</div>
<Text size={2} style={{ color: 'var(--label-secondary)' }}>Keep this key secret. Anyone with it can charge your account.</Text>
</div>
</DialogContent>
</Dialog>
);
}Раскладка/оверлей: панель инструментов с общей задержкой
Когда один тултип уже показан, при переходе к соседней кнопке её тултип появляется сразу, без повторного ожидания delayOpen.
import {
Icon,
IconButton,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { BoldIcon, ItalicIcon, UnderlineIcon } from '@vega-ui/icons';
const tools = [
{ label: 'Bold', shortcut: '⌘B', icon: <BoldIcon /> },
{ label: 'Italic', shortcut: '⌘I', icon: <ItalicIcon /> },
{ label: 'Underline', shortcut: '⌘U', icon: <UnderlineIcon /> },
];
export default function ToolbarSharedDelay() {
return (
<div
role='toolbar'
aria-label='Formatting'
style={{
display: 'flex',
gap: 'var(--spacing-1)',
padding: 'var(--spacing-2)',
background: 'var(--surface-primary)',
border: '1px solid var(--separator-opaque)',
borderRadius: 'var(--radius-5)',
}}
>
{tools.map(({ label, shortcut, icon }) => (
<Tooltip key={label}>
<TooltipTrigger asChild>
<IconButton aria-label={label} appearance='transparent' variant='secondary'>
<Icon>{icon}</Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>{`${label} ${shortcut}`}</Text>
</TooltipContent>
</Tooltip>
))}
</div>
);
}Ошибка
Ошибка: причина сбоя синхронизации
Само состояние ошибки видно как бейдж, а aria-label кнопки-предупреждения говорит, к чему она относится; тултип добавляет причину. «Reconnect» исправляет строку, и подсказка исчезает вместе с ошибкой.
import { useState } from 'react';
import {
Badge,
Button,
Icon,
IconButton,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { CircleAlertIcon } from '@vega-ui/icons';
const integrations = [
{ name: 'Slack', error: '' },
{ name: 'Google Drive', error: 'Access expired on Nov 1. Reconnect to resume syncing.' },
{ name: 'Jira', error: '' },
];
export default function SyncErrorHint() {
const [fixed, setFixed] = useState<string[]>([]);
return (
<ul
style={{
display: 'grid',
boxSizing: 'border-box',
width: '100%',
margin: 0,
paddingInline: 'var(--spacing-8)',
paddingBlock: 'var(--spacing-2)',
listStyle: 'none',
background: 'var(--surface-primary)',
border: '1px solid var(--separator-opaque)',
borderRadius: 'var(--radius-5)',
}}
>
{integrations.map((integration, index) => {
const failing = integration.error !== '' && !fixed.includes(integration.name);
return (
<li
key={integration.name}
style={{
display: 'flex',
alignItems: 'center',
gap: 'var(--spacing-2)',
minWidth: 0,
paddingBlock: 'var(--spacing-5)',
borderBlockStart: index > 0 ? '1px solid var(--separator-opaque)' : undefined,
}}
>
<Text size={3} style={{ flex: 1, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{integration.name}</Text>
{!failing && <Badge variant='success' size='xs'>Synced</Badge>}
{failing && (
<>
<Badge variant='error' size='xs'>Sync failed</Badge>
<Tooltip>
<TooltipTrigger asChild>
<IconButton aria-label={`Why ${integration.name} sync failed`} size='xs' variant='secondary' appearance='ghost'>
<Icon size='xs'><CircleAlertIcon /></Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>{integration.error}</Text>
</TooltipContent>
</Tooltip>
<Button size='xs' variant='secondary' appearance='ghost' onClick={() => setFixed([...fixed, integration.name])}>Reconnect</Button>
</>
)}
</li>
);
})}
</ul>
);
}Отключённый
Отключённый: причина недоступного действия
Кнопка с disabled не получает ни наведения, ни фокуса, поэтому её тултип не появится. aria-disabled оставляет кнопку фокусируемой и оформленной как отключённая, тултип объясняет причину, а обработчик клика сразу выходит, пока действие недоступно. Отметьте «Billing verified», чтобы включить кнопку.
import { useState } from 'react';
import {
Button,
Checkbox,
CheckboxCheckedIcon,
CheckboxIndeterminateIcon,
CheckboxIndicator,
CheckboxInput,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
export default function DisabledActionReason() {
const [verified, setVerified] = useState(false);
const [invited, setInvited] = useState(0);
const invite = () => {
if (!verified) return;
setInvited(invited + 1);
};
return (
<div style={{ display: 'grid', gap: 'var(--spacing-8)', justifyItems: 'center', width: '100%' }}>
<Checkbox style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-4)' }}>
<CheckboxInput checked={verified} onChange={(event) => setVerified(event.currentTarget.checked)} />
<CheckboxIndicator>
<CheckboxCheckedIcon />
<CheckboxIndeterminateIcon />
</CheckboxIndicator>
Billing verified
</Checkbox>
<Tooltip>
<TooltipTrigger asChild>
<Button aria-disabled={!verified} onClick={invite}>Invite members</Button>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>{verified ? 'Send invites to your team' : 'Verify billing to invite more than 5 members'}</Text>
</TooltipContent>
</Tooltip>
<Text size={2} role='status' style={{ minHeight: '1lh', color: 'var(--label-secondary)', fontVariantNumeric: 'tabular-nums' }}>
{invited > 0 ? `Invites sent: ${invited}` : ''}
</Text>
</div>
);
}Пограничный случай
Пограничный случай: полный текст обрезанного имени
Длинные имена файлов обрезаются многоточием, а тултип показывает полное имя при наведении или фокусе ссылки с клавиатуры. У содержимого есть max-width и overflow-wrap: anywhere, поэтому длинное имя переносится, а не уходит за край экрана.
import {
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
const files = [
'Kestrel Robotics – Master services agreement – signed – final v7 (2).pdf',
'Q3 board deck.pdf',
'Brand guidelines 2026 – typography, colour, photography and tone of voice.fig',
];
export default function TruncatedText() {
return (
<ul
style={{
display: 'grid',
boxSizing: 'border-box',
width: '100%',
margin: 0,
paddingInline: 'var(--spacing-8)',
paddingBlock: 'var(--spacing-2)',
listStyle: 'none',
background: 'var(--surface-primary)',
border: '1px solid var(--separator-opaque)',
borderRadius: 'var(--radius-5)',
}}
>
{files.map((file, index) => (
<li key={file} style={{ minWidth: 0, paddingBlock: 'var(--spacing-5)', borderBlockStart: index > 0 ? '1px solid var(--separator-opaque)' : undefined }}>
<Tooltip delayOpen={300}>
<TooltipTrigger asChild>
<a
href={`#${encodeURIComponent(file)}`}
style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: 'inherit' }}
>
{file}
</a>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2} style={{ display: 'block', maxWidth: 280, overflowWrap: 'anywhere' }}>{file}</Text>
</TooltipContent>
</Tooltip>
</li>
))}
</ul>
);
}Пограничный случай: определения метрик в плотной строке
Маленькие кнопки с иконкой рядом с каждой метрикой сохраняют строку компактной, а определения остаются на расстоянии одного наведения или Tab. Каждая кнопка названа по своей метрике, поэтому скринридер их различает.
import {
Icon,
IconButton,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
import { InfoIcon } from '@vega-ui/icons';
const metrics = [
{ label: 'MRR', value: '$48,210', hint: 'Monthly recurring revenue from active subscriptions, excluding one-off charges' },
{ label: 'Churn', value: '2.4%', hint: 'Share of customers who cancelled in the last 30 days' },
{ label: 'Conversion', value: '6.8%', hint: 'Trials that became paid plans. Updated every 15 minutes' },
];
export default function DenseDataHint() {
return (
<dl
style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
gap: 'var(--spacing-8)',
boxSizing: 'border-box',
width: '100%',
margin: 0,
padding: 'var(--spacing-8)',
background: 'var(--surface-primary)',
border: '1px solid var(--separator-opaque)',
borderRadius: 'var(--radius-5)',
}}
>
{metrics.map((metric) => (
<div key={metric.label} style={{ display: 'grid', gap: 'var(--spacing-1)', minWidth: 0 }}>
<dt style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-1)' }}>
<Text size={2} style={{ color: 'var(--label-secondary)' }}>{metric.label}</Text>
<Tooltip>
<TooltipTrigger asChild>
<IconButton aria-label={`About ${metric.label}`} size='xs' variant='secondary' appearance='ghost'>
<Icon size='xs'><InfoIcon /></Icon>
</IconButton>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2} style={{ display: 'block', maxWidth: 240 }}>{metric.hint}</Text>
</TooltipContent>
</Tooltip>
</dt>
<dd style={{ margin: 0 }}><Text size={6} fontWeight={600} style={{ fontVariantNumeric: 'tabular-nums' }}>{metric.value}</Text></dd>
</div>
))}
</dl>
);
}Анатомия
Обзор
Tooltip владеет отложенным открытием и координацией всплывающего слоя для короткого несущественного описания. Он намеренно узкий: триггер плюс описательное содержимое и необязательная стрелка для визуальной привязки.
Обязательные части
Tooltip
Обязательная. Владеет таймингом открытия и координацией триггера с содержимым.
TooltipTrigger
Обязательная. Задаёт цель для наведения или фокуса с клавиатуры. Касание не показывает тултип, а нажатие на триггер скрывает его.
TooltipContent
Обязательная. Рендерит видимое описательное содержимое.
Необязательные части
TooltipArrow
Необязательная визуальная стрелка, когда всплывающее содержимое должно яснее указывать на триггер.
Порядок композиции
TooltipTooltipTriggerTooltipContentTooltipArrow
Корректные паттерны композиции
import {
Button,
Text,
Tooltip,
TooltipArrow,
TooltipContent,
TooltipTrigger,
} from '@vega-ui/react';
export default function MinimalComposition() {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button>Publish</Button>
</TooltipTrigger>
<TooltipContent>
<TooltipArrow />
<Text size={2}>Anyone with the link can view the page</Text>
</TooltipContent>
</Tooltip>
);
}Некорректные паттерны композиции
Интерактивные контролы внутри TooltipContent
Содержимое тултипа должно оставаться описательным, а не превращаться в мини-поповер.
Тултип как единственная метка контрола
Триггеру всё равно нужен собственный доступный смысл без тултипа.
Обязательные инструкции спрятаны только в тултипе
Если содержимое необходимо для выполнения задачи, используйте встроенную справку или другой паттерн оверлея.