PageFade
The content area of a page, cross-faded when the route changes.
Overview
A site where the frame stays put and only the content between pages should fade.
Changing area
import { useState } from "react";
import {
Body,
PageFade,
SegmentedControl,
Stack,
startPageTransition,
} from "@hackcove/ui";
function Example() {
const [value, setValue] = useState("overview");
const items = [
{ value: "overview", label: "Overview" },
{ value: "activity", label: "Activity" },
];
return (
<Stack gap="sm">
<SegmentedControl
label="Page"
items={items}
value={value}
onChange={(next) => startPageTransition(() => setValue(next))}
/>
<PageFade>
<Body>The {value} page.</Body>
</PageFade>
</Stack>
);
}PageFade wraps only the content that changes between pages; the frame around it, with the control, stays put.import { useState } from "react";
import {
Body,
PageFade,
SegmentedControl,
Stack,
startPageTransition,
} from "@hackcove/ui";
function Example() {
const [value, setValue] = useState("overview");
const items = [
{ value: "overview", label: "Overview" },
{ value: "activity", label: "Activity" },
];
return (
<PageFade>
<Stack gap="sm">
<SegmentedControl
label="Page"
items={items}
value={value}
onChange={(next) => startPageTransition(() => setValue(next))}
/>
<Body>The {value} page.</Body>
</Stack>
</PageFade>
);
}Transition
import { useState } from "react";
import { Body, Button, PageFade, Stack, startPageTransition } from "@hackcove/ui";
function Example() {
const [value, setValue] = useState("overview");
return (
<Stack gap="sm">
<Button
variant="outline"
onClick={() =>
startPageTransition(() => setValue(value === "overview" ? "activity" : "overview"))
}
>
Switch page
</Button>
<PageFade>
<Body>The {value} page.</Body>
</PageFade>
</Stack>
);
}startPageTransition, so the browser has the old and the new page to fade between.import { useState } from "react";
import { Body, Button, PageFade, Stack } from "@hackcove/ui";
function Example() {
const [value, setValue] = useState("overview");
return (
<Stack gap="sm">
<Button
variant="outline"
onClick={() => setValue(value === "overview" ? "activity" : "overview")}
>
Switch page
</Button>
<PageFade>
<Body>The {value} page.</Body>
</PageFade>
</Stack>
);
}Usage
- Wrap what changes between pages, once per page, and leave the header and footer outside it.
- Start the change inside a view transition:
startPageTransition(() => setPage(next))for a state change, or the router's own option (TanStack RouterviewTransition, React Router'sviewTransition, Astro'sClientRouter). Without one nothing fades. - Two PageFades on one page each need their own
name. - Portal an overlay such as a dropdown to the body: the fading area is a stacking context, so an overlay inside it cannot rise above a sticky header outside it.
Accessibility
- A PageFade is a plain
<div>with no role, so it changes nothing for assistive technology. - Under
prefers-reduced-motion: reducethe content swaps with no fade, andstartPageTransitionjust runs the update. - Where the browser has no View Transitions it is a plain
<div>and the content swaps.
API
PageFade
The page's content area, named for the View Transitions API so a route change cross-fades it while the frame around it (header, footer) stays put. Wrap what changes between pages, once per page: two on one page need a different name each, or the browser skips the transition. The fade runs only when the navigation is wrapped in a view transition: startPageTransition, or a router's own option (TanStack Router viewTransition, React Router's viewTransition prop, Astro's ClientRouter). Otherwise, or where the browser has no View Transitions, it renders and behaves as a plain <div>. A view-transition-name makes the div a stacking context, so a dropdown inside it cannot rise above a sticky header outside it: portal overlays to the body. Under prefers-reduced-motion: reduce this content swaps with no fade; a router's own transition of the rest of the page is the router's to quiet.
nameThe view-transition name, a CSS identifier (letters, digits, -, _).
- Type
string- Default
hc-page
Functions
startPageTransitionruns a synchronous state change inside a view transition, so a PageFade cross-fades; call it from an event handler.