Patterns

Rich origins

The origin is the element the viewer flies from and back to. By default it is the trigger's first image or video, or the trigger itself. A card can mark a different element, and a scroller gets its closing item scrolled back into view.

Loading the live example…

origins.tsx
import { itemLabel } from "@ogdakke/fluidbox";
import { Fluidbox } from "@ogdakke/fluidbox/react";
import { product, strip } from "./data";

const products = [product];

/** Only the photo flies: the card marks it with data-fluidbox-origin. */
export function ProductCard() {
  return (
    <Fluidbox.Root items={products}>
      <Fluidbox.Trigger item={product} asChild>
        <button type="button" className="pattern-card">
          <span data-fluidbox-origin>
            <img src={product.src} alt={itemLabel(product)} />
          </span>
          <span>
            <strong>Coral runner</strong>
            <br />
            €120
          </span>
        </button>
      </Fluidbox.Trigger>
    </Fluidbox.Root>
  );
}

/** Moving through the viewer scrolls each trigger into view, so the close lands on it. */
export function Scroller() {
  return (
    <Fluidbox.Root items={strip}>
      <div className="pattern-scroller">
        {strip.map((photo) => (
          <Fluidbox.Trigger
            key={photo.src}
            item={photo}
            className="pattern-thumb"
          >
            <img src={photo.src} alt={itemLabel(photo)} />
          </Fluidbox.Trigger>
        ))}
      </div>
    </Fluidbox.Root>
  );
}
  • data-fluidbox-origin on any element inside the trigger makes it the origin. The flight copies its box, crop (object-fit, object-position) and corner radius.
  • Reveal on close. Each time the selection settles, and on close if it hasn't yet, the viewer scrolls the item's trigger into view (nearest edge), so the closing flight lands on it. To control this yourself, give the viewer an origins adapter with revealOrigin(id, index, { signal, reason }) (see The origin adapter).
  • As child. The framework Trigger parts render a button by default, and with asChild decorate your own element instead. See each framework page. Angular's fluidboxTrigger is an attribute, so it always decorates. In plain JavaScript, fluidbox.bind() always decorates the element you give it.