Preview Card

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless preview_card
Source View Source

Overview of the Phoenix and LiveView Headless Preview Card Component

The Mishka Chelekom Headless Preview Card ships zero opinionated styling - only the semantic markup, anchored popup positioning (side, align, offset, edge-flip and viewport clamp), and paired-presence state (data-open/data-closed). Every part - trigger, popup, and arrow - is addressable through a *_class attribute, so you style it entirely with your own Tailwind classes while the anchoring and behavior keep working underneath. It is non-modal and never steals focus from the page.

Base UI Preview Card example (Phoenix headless)

The principles of good
remain in the digital age.
heex
<p class="m-0 text-base text-neutral-950 text-balance dark:text-white">
  The principles of good
  <.preview_card
    id="baseui-preview_card-hero"
    side_offset={8}
    class="inline"
    trigger_class="cursor-pointer text-neutral-950 underline decoration-neutral-950/60 decoration-1 underline-offset-2 outline-0 hover:decoration-neutral-950 data-[popup-open]:decoration-neutral-950 focus-visible:no-underline focus-visible:outline-2 focus-visible:outline-neutral-950 dark:text-white dark:decoration-white/60 dark:hover:decoration-white dark:data-[popup-open]:decoration-white dark:focus-visible:outline-white"
    popup_class="group relative h-[var(--popup-height,auto)] w-[var(--popup-width,auto)] origin-[var(--transform-origin)] border border-neutral-950 bg-white text-neutral-950 shadow-[0.25rem_0.25rem_0] shadow-black/12 transition-[transform,opacity] duration-100 ease-out data-[ending-style]:[transform:scale(0.98)] data-[ending-style]:opacity-0 data-[starting-style]:[transform:scale(0.98)] data-[starting-style]:opacity-0 dark:border-white dark:bg-neutral-950 dark:text-white dark:shadow-none"
    arrow_class="relative block h-1.5 w-3 overflow-clip group-data-[side=bottom]:top-[-6px] group-data-[side=left]:right-[-9px] group-data-[side=left]:rotate-90 group-data-[side=right]:left-[-9px] group-data-[side=right]:-rotate-90 group-data-[side=top]:bottom-[-6px] group-data-[side=top]:rotate-180 before:absolute before:bottom-0 before:left-1/2 before:h-[calc(6px*sqrt(2))] before:w-[calc(6px*sqrt(2))] before:border before:border-neutral-950 before:bg-white before:content-[''] before:[transform:translate(-50%,50%)_rotate(45deg)] dark:before:border-white dark:before:bg-neutral-950"
  >
    <:trigger>typography</:trigger>
    <:arrow></:arrow>
    <div class="flex w-min flex-col gap-2 p-2">
      <img
        width="224"
        height="150"
        class="block max-w-none"
        src="https://images.unsplash.com/photo-1619615391095-dfa29e1672ef?q=80&w=448&h=300"
        alt="Station Hofplein signage in Rotterdam, Netherlands"
      />
      <p class="m-0 text-sm text-pretty">
        <strong>Typography</strong> is the art and science of arranging type to make written
        language clear, visually appealing, and effective in communication.
      </p>
    </div>
  </.preview_card>
  remain in the digital age.
</p>

Parts & data attributes

Each part carries a stable data-part and a structural chelekom-preview_card__* class you can target, plus paired-presence state for open/closed transitions.

Partdata-partState / ARIAPurpose
Root-data-open, data-closed, data-side, data-alignThe wrapping element that carries the open/closed state.
Triggertriggeraria-expanded, aria-controls, data-popup-openThe hovered/focused element (usually a link) that reveals the preview.
Popuppopuprole="dialog", data-open, data-closed, data-side, data-align, data-starting-styleThe floating preview panel, anchored to the trigger.
Arrowarrowaria-hiddenOptional pointer arrow rendered inside the popup; carries data-side.

Accessibility

Follows the WAI-ARIA Authoring Practices Guide for a non-modal hover card with anchored positioning: Escape closes the popup, moving the pointer or focus away closes it after close_delay, and moving the pointer into the popup keeps it open. Focus is never stolen - it stays on the trigger, and the popup is reachable by keyboard and hoverable by mouse.