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↗.
stable
Declarative Names
Automatically assigns dynamic view transition names to HTML elements using CSS selectors.
stable
Escaped Custom Identifiers
Escape view transition names to unlock characters beyond basic ASCII letters and digits.
experimental
Interim Type Declarations
A temporary solution until Definitely Typed is ready and Level 2 definitions become available.
experimental
Hardened Start
Wrapper with a non-animating fallback for browsers without native support. Respects the reduced motion preference.
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:
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
↗