sdxc

Type to search, or start from one of these:

SharedElement

A plain host that carries a stable view-transition identity across separate page loads.

Pick a cover.
View code
let openSlug: string | null = null;

/** The same call viewTransition() makes for a Frame reload. */
function open(slug: string | null) {
	openSlug = slug;

	if (typeof document.startViewTransition !== "function") {
		void handle.update();
		return;
	}

	document.startViewTransition(() => handle.update());
}

openCover === undefined ? (
	<div mix={[vstack({ gap: 3, align: "stretch" })]}>
		<Text>Pick a cover.</Text>
		<div mix={[grid(), gridTemplate({ columns: "repeat(3, 1fr)" }), gap(3)]}>
			{covers.map((cover) => (
				<Button
					key={cover.slug}
					variant="ghost"
					mix={[p(0), bs("5rem"), on<HTMLButtonElement, "click">("click", () => open(cover.slug))]}
				>
					<SharedElement
						id={`preview-cover-${cover.slug}`}
						mix={[
							is("100%"),
							bs("100%"),
							rounded("lg"),
							bg(`${cover.tint}.tint`),
							fg(cover.tint),
							p(2),
							text("xs"),
							weight("medium"),
							css({ display: "grid", placeItems: "center", textAlign: "center" }),
						]}
					>
						{cover.title}
					</SharedElement>
				</Button>
			))}
		</div>
	</div>
) : (
	<div mix={[vstack({ gap: 3, align: "stretch" })]}>
		<SharedElement
			id={`preview-cover-${openCover.slug}`}
			mix={[
				is("100%"),
				bs("9rem"),
				rounded("lg"),
				bg(`${openCover.tint}.tint`),
				fg(openCover.tint),
				p(4),
				text("xl"),
				weight("semibold"),
				css({ display: "grid", placeItems: "center", textAlign: "center" }),
			]}
		>
			{openCover.title}
		</SharedElement>
		<Text>The same id on both views, so the browser morphs it instead of swapping it.</Text>
		<div mix={[hstack({ gap: 2, align: "center" })]}>
			<Button
				variant="outline"
				size="sm"
				mix={[on<HTMLButtonElement, "click">("click", () => open(null))]}
			>
				Back to the grid
			</Button>
		</div>
	</div>
)

Installation

An ordinary dependency: install it, import the theme once, and import the component where it is used.

npm add @sdxc/ui
import "@sdxc/ui/theme.css";

Usage

import { SharedElement } from "@sdxc/ui";

Renders a plain <div> host whose view-transition-name comes from its own id, so a same-origin, cross-document navigation matching that id on the other side morphs the element instead of replacing it outright.

Examples

// On the book's own page — the matching `id` lets the browser morph
// between the two instead of swapping the image outright.
<SharedElement id={`cover-${book.slug}`}>
	<img src={book.coverUrl} alt={book.title} />
</SharedElement>

Props

Read from the component's own types, so every prop, every default and every allowed value is listed.

Also accepts everything in TagProps<"div">.