Skip to content

Preview Card

Shows a preview of what is behind a link while the pointer rests on it.

View source View as Markdown

Preview Card shows what is behind a link before anyone follows it, whether that is a profile, an article or a repository. It opens when the pointer rests on the link and when the link takes focus from the keyboard. It never opens on touch, where there is no hover to read.

The example switches the teleport off, so the popup renders in place inside the sentence, which is why that sentence sits in a <div>: a paragraph cannot legally hold it.

Mirror is built and maintained by Robin Vey
and the team behind Magic as a Service.
App.vue
<template>
  <div class="type-surface-body-md text-surface max-w-md text-pretty">
    Mirror is built and maintained by
    <PreviewCard.Root :options="{ teleport: { disabled: true } }">
      <PreviewCard.Trigger
        class="text-surface-link hover:text-surface-link-hover rounded-component-xs focus-visible:focus-ring underline decoration-1 underline-offset-4 outline-4 outline-transparent transition-all duration-100 ease-linear"
        href="https://maas.engineering"
      >
        Robin Vey
      </PreviewCard.Trigger>

      <PreviewCard.Content
        class="rounded-component-2xl bg-primary-inverted shadow-component-high w-72 p-4 backdrop-blur-[8rem] [&_*]:transition-all [&_*]:duration-100 [&_*]:ease-linear"
      >
        <div class="flex items-center gap-3">
          <Avatar.Root
            class="bg-primary-subtle text-primary-on-subtle rounded-component-lg type-component-xl flex size-12 shrink-0 items-center justify-center overflow-hidden"
          >
            <Avatar.Image src="/robin.jpg" alt="Robin Vey" class="size-full" />
            <Avatar.Fallback
              class="leading-none font-medium tracking-[0.02em]"
            >
              RV
            </Avatar.Fallback>
          </Avatar.Root>

          <div class="flex min-w-0 flex-col gap-0.5">
            <span
              class="type-component-lg text-primary-on-subtle truncate leading-none font-medium"
            >
              Robin Vey
            </span>
            <span class="type-component-2xs text-primary-muted truncate">
              @robinscholz
            </span>
          </div>
        </div>

        <p class="type-component-xs text-primary-muted mt-3 text-pretty">
          Design engineer in Munich, building the tools the rest of Magic as a
          Service runs on.
        </p>
      </PreviewCard.Content>
    </PreviewCard.Root>
    and the team behind Magic as a Service.
  </div>
</template>

<script setup lang="ts">
import { Avatar } from '@maas/mirror/vue/Avatar'
import { PreviewCard } from '@maas/mirror/vue/PreviewCard'
</script>

Usage guidelines

  • Put everything the card shows behind the link as well. The card is never announced to a screen reader and never reachable on touch, so nothing in it can be the only way to that information.must
  • Keep the trigger a link. If the preview hangs off a button, use Popover; if it only shows a label, use Tooltip.should
  • Leave delay.mouseenter where it is unless you have a reason. Six hundred milliseconds is long enough that a pointer crossing the link on its way somewhere else does not open anything.should
  • PreviewCard.Content teleports to the end of <body> unless you turn that off with options.teleport.disabled. Leave it on where the link sits inside anything with overflow: hidden.should
  • The content resolves everything but side and align once, during setup, so change the rest before the popup first renders or key the root to remount it.must