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 scripts and 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?

One day, when it grows up, this will be an eclectic collection of snippets for various purposes. The @vtbag/utensil-drawer package are available on npm.

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.

The Utensil-Drawer offers a bundled script for cross-document view transitions and reusable function for same-document view transitions.

This functionality is similar to assigning dynamic view transition names with the auto value, but it works consistently across browsers and gives you greater control. You specify the names to assign. This allows you to match elements between the old and new pages for cross-document morph animations, which is something not possible with dynamically generated names from view-transition-name: auto.

Script Usage

For use with cross-document view transitions you load the declarative-names.js script on every page that needs declarative view transition names and specify what names to assign where with the data-vtbag-decl attribute.

<html>
<head>
...
<script src="/declarative-names.js" data-vtbag-decl="..."/>
</head>

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

The reusable functions can be imported from @vtbag/utensil-drawer/set-view-transition-names.

import { setSelectedViewTransitionNames } from "@vtbag/utensil-drawer/set-view-transition-names";
setSelectedViewTransitionNames("h2", "headers-");

For details see the comments in the code

Examples

This site uses the declarative-names script for cross-document navigation to make h2 and h3 headings standout when navigating through the pages. It also randomizes the entries of the 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="h2, h3 = vtbag-h-; starlight-toc span = vtbag-toc~"

This assigns vtbag-h-0, ... to the h2 and h3 headers of every page. This makes headings appear to lift off the page during navigation and smoothly slide into place on the next page.

The second declaration assigns vtbag-toc-0, ... in a random fashion to the entries of the page navigation bar: During navigation, the entries swirl into place, seamlessly adjusting to the new structure.

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.

The structure for the declaration is as shown in the syntax diagram:

css-selector=~=prefix;

The data attribute accepts a semicolon separated list of rules. A rule tells the script to add a view-transition-name, starting with the given prefix, to the style attribute of all DOM elements matching the css-selector. If the rule ends right after the selector, it implicitly uses vtbag-decl-{i}- for some i as the prefix. Prefixes can consist of arbitrary characters but must not contain semicolons (;) or equal signs (=). Selectors can be any valid CSS selector, but can’t contain semicolons and ASCII characters other than A-Za-z0-9_- must be escaped.

To ensure unique view transition names, a numeric suffix is appended to the prefix when a rule applies to multiple elements, unless the prefix is empty, none, or auto. The numbering starts at 0 and increments by 1, following the order in which elements appear in the DOM. If the prefix ends with ~, the ~ is replaced with -, and numbers are assigned in a randomized manner instead of sequentially.

If an element’s style attribute already contains a view-transition-name, it will be overridden unless the rule uses the weak assignment operator ~=.

escapeViewTransitionName()

The escapeViewtransitionName() function escapes characters in a string, making it safe to use as a view transition name.

import { escapeViewTransitionName } from "@vtbag/utensil-drawer/escape-view-transition-name";
const escaped = escapeViewTransitionName("my:ident") // => my\:ident

Type Declarations

experimental

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

declare global {
type UpdateCallback = undefined | (() => void | Promise<void>);
type StartViewTransitionParameter = { types?: string[] | Set<string>; update?: UpdateCallback };
interface Document {
startViewTransition?(param?: StartViewTransitionParameter | UpdateCallback): ViewTransition;
}
interface PageSwapEvent extends Event {
viewTransition?: ViewTransition;
activation?: NavigationActivation;
}
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;
}
}
export {};

mayStartViewTransition()

experimental

A wrapper for document.startViewTransition() that provides a consistent interface for view transitions, regardless of native browser support.

This function ensures a unified approach by offering fallback support for browsers without native view transitions or those limited to Level 1. When view transitions aren’t supported, it directly invokes the update function and returns a compatible view transition object with promises.

Additionally, it respects the user’s reduced motion preference. If reduced motion is preferred, the function behaves as if native view transitions are unsupported.

For details see may-start-view-transition.ts