Tooltip
A short hint shown on hover or keyboard focus.
Melt UI Tooltip
Add to library
<script lang="ts">
import * as Tooltip from '#lib/components/ui/tooltip/index.js';
</script>
<Tooltip.Root>
<Tooltip.Trigger>Hover me</Tooltip.Trigger>
<Tooltip.Content>Add to library</Tooltip.Content>
</Tooltip.Root>
Installation
npx distill-ui add tooltipIt also copies Button, which Tooltip uses.
It installs @floating-ui/domandmelt if you don't have them yet.
- Set up the tokens once, if you haven't: see manual installation.
- Install the packages it needs:
npm install @floating-ui/dom@^1.6.0 melt@0.44.0 - Copy Button too, which it uses.
- Copy these files into your project:
import { getContext, setContext } from 'svelte';
import type { Tooltip } from 'melt/builders';
type TooltipContext = { tooltip: Tooltip };
const key = Symbol('distill-tooltip');
export const setTooltipContext = (ctx: TooltipContext) => setContext(key, ctx);
export const getTooltipContext = () => getContext<TooltipContext>(key);
import Content from './tooltip-content.svelte';
import Trigger from './tooltip-trigger.svelte';
import Root from './tooltip.svelte';
export { Root, Trigger, Content };
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { getTooltipContext } from './context.js';
let {
class: className,
children,
style,
...restProps
}: HTMLAttributes<HTMLDivElement> = $props();
const { tooltip } = getTooltipContext();
</script>
<div
{...restProps}
{...tooltip.content}
style="{tooltip.content.style}; {style ?? ''}"
class={['content', className]}
>
{@render children?.()}
<div {...tooltip.arrow} class="arrow"></div>
</div>
<!--
Options you can set from a parent or on the tooltip content itself:
--dui-tooltip-max-width, --dui-tooltip-radius, --dui-tooltip-bg, --dui-tooltip-fg
-->
<style>
.content {
width: fit-content;
max-width: var(--dui-tooltip-max-width, 20rem);
margin: 0;
padding: var(--dui-space-1-5) var(--dui-space-3);
border: 0;
border-radius: var(--dui-tooltip-radius, var(--dui-radius-md));
background: var(--dui-tooltip-bg, var(--dui-color-primary));
color: var(--dui-tooltip-fg, var(--dui-color-primary-foreground));
font-size: var(--dui-text-xs);
line-height: var(--dui-text-xs-line-height);
text-wrap: balance;
overflow: visible;
transition:
opacity var(--dui-duration-fast) var(--dui-ease-out),
scale var(--dui-duration-fast) var(--dui-ease-out);
@starting-style {
opacity: 0;
scale: 0.95;
}
}
/* Melt positions the arrow; we only draw it, as a rotated square in the tooltip's color. */
.arrow {
width: 0.5rem;
height: 0.5rem;
border-radius: 2px;
background: var(--dui-tooltip-bg, var(--dui-color-primary));
}
</style>
<script lang="ts">
import type { HTMLButtonAttributes } from 'svelte/elements';
import { Button, type ButtonSize, type ButtonVariant } from '../button/index.js';
import { getTooltipContext } from './context.js';
let {
variant = 'outline',
children,
...restProps
}: HTMLButtonAttributes & { variant?: ButtonVariant; size?: ButtonSize } = $props();
const { tooltip } = getTooltipContext();
</script>
<Button {...restProps} {...tooltip.trigger} {variant}>
{@render children?.()}
</Button>
<script lang="ts">
import type { Placement } from '@floating-ui/dom';
import { Tooltip } from 'melt/builders';
import type { Snippet } from 'svelte';
import { setTooltipContext } from './context.js';
let {
open = $bindable(false),
placement = 'top',
openDelay = 300,
children
}: { open?: boolean; placement?: Placement; openDelay?: number; children?: Snippet } = $props();
const tooltip = new Tooltip({
open: () => open,
onOpenChange: (v) => (open = v),
openDelay: () => openDelay,
floatingConfig: () => ({ computePosition: { placement }, offset: { mainAxis: 4 } })
});
setTooltipContext({ tooltip });
</script>
{@render children?.()}
Customizing
Set any of these on the component or on a parent element. See Customizing for how they work.
| Custom property | Default |
|---|---|
--dui-tooltip-max-width | 20rem |
--dui-tooltip-radius | var(--dui-radius-md) |
--dui-tooltip-bg | var(--dui-color-primary) |
--dui-tooltip-fg | var(--dui-color-primary-foreground) |