START
Page transitions
One import, one attribute, and your navigations animate instead of blinking. No JavaScript, and nothing to fall back to.
The browser does all of it. Two pages that both opt in get captured, cross-faded and swapped by the compositor β no router, no script, no intercepted clicks. A browser that has never heard of any of this navigates the way it always did, and your reader loses an animation rather than a page.
The rail is a client router: it fetches the next page and swaps it in, so the document never changes and this feature never runs. The three links below are marked data-astro-reload, which hands them back to the browser β a real navigation, two real documents, and the library doing the whole thing with no script involved. Press one, then press something in the rail, and you can feel the difference.
Switching it on
It is a subpath, not part of the main import, because @view-transition changes how every navigation on a site behaves and a design system should not switch that on because somebody imported a button. Both the page being left and the page being entered have to opt in, which one import in one stylesheet takes care of.
/* after the main import β see Install for the whole entry file */
@import "@pipejesus/higreg-ds";
@import "@pipejesus/higreg-ds/transitions"; /* β this one */Choosing an effect
One attribute on <html>. Leave it off and the page cross-fades; set it to paper or turn and it arrives that way.
<html data-ds-transition="paper">
β¦
</html>Not on the one you are leaving, and this is the API rather than a quirk of this library. In a cross-document transition the old page is a snapshot and every pseudo-element is built in β and styled by β the document being navigated to. So a page decides how it ARRIVES, and cannot decide how it leaves.
The three effects
Reduced motion is the floor
Everything that moves lives inside prefers-reduced-motion: no-preference. A reader who has asked for less motion gets the cross-fade and nothing else β not a shorter slide, no slide. A fade moves nothing and is what the browser would have done unaided, which is why it is the base case rather than a fourth effect somebody has to remember to select.
Changing one
The durations, the lift, the angle and the shadow are all tokens β --ds-vt-fade, --ds-vt-sheet, --ds-vt-turn, --ds-vt-lift, --ds-vt-angle, --ds-vt-depth and --ds-vt-sheet-shadow β so a re-theme moves them. To change an animation outright, write an unlayered rule after the import: nothing in the library or anything it imports targets these pseudo-elements, so there is no specificity to fight.
/* Unlayered, like the library's own, so source order decides it. */
[data-ds-transition="paper"]::view-transition-new(root) {
animation-duration: 500ms;
}Cross-document transitions are Chrome 126 and up and Safari 18.2 and up. Firefox does not ship them yet, so roughly one reader in four navigates instantly and sees no difference. There is nothing to write for that case: the at-rule is ignored, and no box on the page is positioned, sized or painted differently because of it.
Choosing an effect from the link that was pressed means view-transition types, and types set in pageswap on the outgoing page are explicitly not carried to the incoming one β the destination has to set them itself, in pagereveal, which fires before the first rendering opportunity and therefore has to be listened for from a parser-blocking classic script in <head>.
That is a render-blocking script so a decoration can pick between two fades, on a system whose whole posture is that the script is an enhancement and never the mechanism. It was tried, and refused. The attribute does the same job in the markup, correctly, every time.
Reference
Read from transitions.css when this page was built, so it cannot fall behind the stylesheet.
--ds-vt-angle--ds-vt-depth--ds-vt-ease--ds-vt-fade--ds-vt-lift--ds-vt-sheet--ds-vt-sheet-shadow--ds-vt-turn--ds-vt-turn-shadow