Skip to content

Last updated: First published:

The Utensil Drawer

Pick the tools you need to craft the view transitions you want!

The Utensil Drawer is a collection of functions that might come in handy when programming view transitions.

Some functions are meant to be imported into your own scripts, while others can be used directly as scripts on websites. For the latter, the Utensil Drawer also provides bundled, ready-to-use versions.

What is it?

This will be an eclectic collection of snippets for various purposes. Preliminary versions of the @vtbag/utensil-drawer package are currently available on npm, but it is not fully supported yet.

Type Declarations

Copy src/dom-view-transitions-level2.d.ts to your src directory if you are missing declarations for the new types and interfaces.

declare global {
interface PageSwapEvent extends Event {
viewTransition: ViewTransition;
activation: NavigationActivation;
}
type UpdateCallback = undefined | (() => void | Promise<void>);
type StartViewTransitionParameter = { types?: string[] | Set<string>; update?: UpdateCallback };
interface Document {
startViewTransition(param?: StartViewTransitionParameter | UpdateCallback): ViewTransition;
}
interface PageRevealEvent extends Event {
viewTransition: ViewTransition;
}
interface WindowEventMap {
pageswap: PageSwapEvent;
pagereveal: PageRevealEvent;
}
interface NavigationActivation {
entry: NavigationHistoryEntry;
from: NavigationHistoryEntry;
navigationType: NavigationTypeString;
}
interface AnimationEffect {
target: HTMLElement;
pseudoElement?: string;
getKeyframes: () => Keyframe[];
}
interface ViewTransition {
types: Set<string>;
}
interface Window {
navigation: Navigation;
}
interface Navigation {
activation: NavigationActivation;
}
}

Declarative Names

The @vtbag/utensil-drawer/declarative-names script assigns view transition names to HTML elements. This comes in handy if you want to assign names to many elements like all images in a container or all items in a list.

This is similar to

selector {
view-transition-name: auto;
}

but gives you more control over the assigned values. In particular, this is important for cross-document view transitions:

  • Names generated with view-transition-name: auto on the old page will never match a name on the new page. All animations for generated names will either be exit or entry animations, but never morph animations.

  • With the declarative-names script, you have the choice to use different prefixes for exit/entry animations or same prefixes for morph animations.

Examples

This site uses the declarative-names script to make h2 and h3 headings standout when navigating through the pages. And to randomize the entries of table of contents visible in the right part of the screen in the desktop view. Here is the configuration used for this:

data-vtbag-decl=`
:is(h2, h3):not(.no-vtbag-decl *) = vtbag-h-;
:is(starlight-toc span):not(.no-vtbag-decl *) = vtbag-toc~`>

This assigns vtbag-h-0, ... to the h2 and h3 headers of every page, and vtbag-toc-0, ... to the entries of the page navigation bar. It looks a bit complicate due to the :not expressions that are used to hide some DOM subtrees from the script. A version without those much simpler:

data-vtbag-decl="h2, h3 = vtbag-h-; starlight-toc span = vtbag-toc~"

Still looks cryptic? Let’s decompose what it means:

Configuration

You instruct the script with a data-vtbag-decl attribute on a script element. Typically on the very script element that loads the declarative-names code, but any other script element found in the DOM when the script executes will do. You can use this to your advantage to compose the declarations from multiple sources, but make sure that those elements a positioned before the declarative-names script.

Declarations

The syntax for the declaration is as follows:

css-selector=~=prefix;
data-vtbag-decl ::= ( rule ( ';' rule )* )? ;
rule ::= css-selector ( '=' | '~=' ) prefix ;

The data attribute accepts a semicolon separated list of rules. A rule instructs the script to add a view-transition-name that starts with the string prefix to the style attribute of all DOM elements that are matched by the css-selector.

To ensure view transition names remain unique, a number starting at 0 and incremented by 1 is appended to the prefix for each rule. Numbers are assigned based on the order in which elements appear in the DOM, unless the prefix ends in ~, in which case the ~ is replaced by - and the numbers are assigned in some random fashion. If an element’s style attribute already includes a view-transition-name, it will be overridden, unless rule uses the weak assignment operator ~=.

Script Usage

To ensure that the code is executed before the pagereveal event, the script must be placed inside the <head> element of your page as a classical inline script. Do not add type="module" or the defer or async attributes as this would delay the script too much.

Reusable Functions

See comments in src/set-view-transition-names.ts.