# Hover Card

Rich preview card revealed on hover or keyboard focus, with open and close delays, side and align placement, and Title and Description parts wired to its accessible name.

- Package: `@mielui/svelte`
- Component version: `2.0.1`
- Depends on Mielui components: _internal/utils
- Shared utilities: transition, components/_internal/surface, utils.cn, utils.createContext

## Install

~~~~sh
pnpm dlx @mielui/svelte add hover-card
~~~~

## API

This reference is generated at build time from the component manifest, public `index.ts`, and documentation examples below. Changes to those source files are reflected here in the published Markdown. Standard Svelte and HTML attributes accepted by the exported prop types are supported.


### Content

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| surface | "solid" \| "glass" \| undefined | - | No | No | Surface treatment. Omit it to inherit `--mielui-surface` from the theme. |
| sideOffset | number \| undefined | 8 | No | No | Gap between the trigger and the card, in pixels. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |



### Description

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Root

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| open | boolean \| undefined | false | No | Yes | Whether it is open. Bind it to control the state from outside. |
| delay | number \| undefined | 200 | No | No | Milliseconds the pointer rests on the trigger before the card opens. |
| closeDelay | number \| undefined | 150 | No | No | Milliseconds after the pointer leaves before the card closes. |
| placement | HoverCardPlacement \| undefined | 'bottom' | No | No | Preferred side and alignment relative to the trigger, such as `bottom-start`. It flips when there is no room. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Title

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| class | string \| undefined | - | No | No | Classes added to the element. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |



### Trigger

| Prop | Type | Default | Required | Bindable | Description |
| --- | --- | --- | --- | --- | --- |
| href | string \| undefined | - | No | No | Destination of the trigger link. The card previews it. |
| children | Snippet<[]> \| undefined | - | No | No | Content rendered inside. |
| class | string \| undefined | - | No | No | Classes added to the element. |



### Public types

~~~~ts
import type { DefaultProps } from '@mielui/svelte/utils';
import type { Snippet } from 'svelte';
import Root from './hover-card.svelte';
import Content from './hover-card-content.svelte';
import Description from './hover-card-description.svelte';
import Title from './hover-card-title.svelte';
import Trigger from './hover-card-trigger.svelte';

/** A side of the trigger, optionally aligned to its start or end. */
export type HoverCardPlacement =
    | 'top'
    | 'top-start'
    | 'top-end'
    | 'bottom'
    | 'bottom-start'
    | 'bottom-end'
    | 'left'
    | 'left-start'
    | 'left-end'
    | 'right'
    | 'right-start'
    | 'right-end';

export type HoverCardProps = {
    /** Whether it is open. Bind it to control the state from outside. */
    open?: boolean;
    /** Milliseconds the pointer rests on the trigger before the card opens. */
    delay?: number;
    /** Milliseconds after the pointer leaves before the card closes. */
    closeDelay?: number;
    /**
     * Preferred side and alignment relative to the trigger, such as `bottom-start`. It flips when
     * there is no room.
     */
    placement?: HoverCardPlacement;
    /** Content rendered inside. */
    children?: Snippet;
};

export type HoverCardTriggerProps = {
    /** Destination of the trigger link. The card previews it. */
    href?: string;
    /** Content rendered inside. */
    children?: Snippet;
} & DefaultProps;

export type HoverCardContentProps = {
    /** Surface treatment. Omit it to inherit `--mielui-surface` from the theme. */
    surface?: 'solid' | 'glass';
    /** Gap between the trigger and the card, in pixels. */
    sideOffset?: number;
    /** Content rendered inside. */
    children?: Snippet;
} & DefaultProps;

export type HoverCardTitleProps = DefaultProps;
export type HoverCardDescriptionProps = DefaultProps;

export { Content, Description, Root, Title, Trigger };
~~~~

## Examples

### Definition

~~~~svelte
<script lang="ts">
    import * as HoverCard from '@mielui/svelte/components/hover-card';
</script>

<HoverCard.Root>
    <HoverCard.Trigger>polymorphic</HoverCard.Trigger>
    <HoverCard.Content>
        <HoverCard.Description>
            Able to render as different HTML elements while maintaining the same component API.
        </HoverCard.Description>
    </HoverCard.Content>
</HoverCard.Root>
~~~~

### Glass

~~~~svelte
<script lang="ts">
    import * as HoverCard from '@mielui/svelte/components/hover-card';
</script>

<HoverCard.Root>
    <HoverCard.Trigger>polymorphic</HoverCard.Trigger>
    <HoverCard.Content surface="glass">
        <HoverCard.Description>
            Able to render as different HTML elements while maintaining the same component API.
        </HoverCard.Description>
    </HoverCard.Content>
</HoverCard.Root>
~~~~

### Link Preview

~~~~svelte
<script lang="ts">
    import * as HoverCard from '@mielui/svelte/components/hover-card';
</script>

<HoverCard.Root>
    <HoverCard.Trigger href="https://ui.miel.my">Mielui</HoverCard.Trigger>
    <HoverCard.Content>
        <HoverCard.Title>Mielui</HoverCard.Title>
        <HoverCard.Description>
            Accessible Svelte 5 components styled with Tailwind CSS.
        </HoverCard.Description>
    </HoverCard.Content>
</HoverCard.Root>
~~~~

### Placement

~~~~svelte
<script lang="ts">
    import * as HoverCard from '@mielui/svelte/components/hover-card';

    const sides = ['top', 'right', 'bottom', 'left'] as const;
</script>

<div class="flex flex-wrap items-center justify-center gap-x-8 gap-y-4 text-sm">
    {#each sides as side (side)}
        <HoverCard.Root placement={side} delay={100}>
            <HoverCard.Trigger class="underline underline-offset-4">
                {side[0].toUpperCase() + side.slice(1)}
            </HoverCard.Trigger>
            <HoverCard.Content class="w-56">
                <HoverCard.Title>{`Opens on the ${side}`}</HoverCard.Title>
                <HoverCard.Description>
                    The card flips to the opposite side when there is no room.
                </HoverCard.Description>
            </HoverCard.Content>
        </HoverCard.Root>
    {/each}
</div>
~~~~

### User Preview

~~~~svelte
<script lang="ts">
    import {
        Building03Icon as Building,
        Location01Icon as MapPin,
        UserGroupIcon as Users
    } from '@hugeicons/core-free-icons';
    import * as Avatar from '@mielui/svelte/components/avatar';
    import { Button } from '@mielui/svelte/components/button';
    import * as HoverCard from '@mielui/svelte/components/hover-card';
    import HugeiconsIcon from '@mielui/svelte/hugeicons-icon';

    let following = $state(false);

    function toggleFollow() {
        following = !following;
    }
</script>

<div class="flex items-center justify-center">
    <div class="text-sm text-foreground-muted">
        Reviewed by
        <HoverCard.Root>
            <HoverCard.Trigger href="https://github.com/mielsense">@mielsense</HoverCard.Trigger>
            <HoverCard.Content class="w-60">
                <div class="flex flex-col gap-3">
                    <div class="flex items-center gap-3">
                        <Avatar.Root size="md">
                            <Avatar.Fallback>MS</Avatar.Fallback>
                        </Avatar.Root>
                        <div class="flex flex-col">
                            <HoverCard.Title class="text-base">mielsense</HoverCard.Title>
                            <p class="text-sm text-foreground-muted">@mielsense</p>
                        </div>
                    </div>

                    <HoverCard.Description class="text-sm leading-relaxed text-pretty">
                        Maintains the shared components and reviews accessibility fixes.
                    </HoverCard.Description>

                    <div class="flex flex-col gap-1 text-xs text-foreground-muted">
                        <div class="flex items-center gap-1.5">
                            <HugeiconsIcon icon={MapPin} size={14} />
                            <span>Seattle, WA</span>
                        </div>
                        <div class="flex items-center gap-1.5">
                            <HugeiconsIcon icon={Building} size={14} />
                            <span>Mielui Labs</span>
                        </div>
                    </div>

                    <div class="flex items-center gap-1 py-1 text-xs text-foreground-muted">
                        <HugeiconsIcon icon={Users} size={14} />
                        <span>1.2k followers · 89 following</span>
                    </div>

                    <Button
                        variant="secondary"
                        size="md"
                        class="w-full"
                        aria-pressed={following}
                        onclick={toggleFollow}
                    >
                        {following ? 'Following' : 'Follow'}
                    </Button>
                </div>
            </HoverCard.Content>
        </HoverCard.Root>
        3 days ago.
    </div>
</div>
~~~~

For the rendered reference, visit [/docs/components/hover-card](/docs/components/hover-card).
