Skip to content

Last updated: First published:

The Turn-Signal

Backward/forward view transitions for intuitive history navigation.

The Turn-Signal package is a set of scripts for enhancement of cross-document view transitions. They automatically detect direction of navigation or let you set view transition types. The current tricks are

  • the directions script that derives directions from the history or a known set of pages
  • the link-type script, that let’s you define view-transition-types on a per link basis
  • the forced-traversal script that jumps back in browser history when you reach a page that’s on your current browser history

PREREQUISITE

The Turn-Signal scripts are designed for use with cross-document view transitions. With the exception of the forced_traversal script, all scripts will even work with none or only small limitations on browsers that do not support the Navigation API. Looking at you, Safari.

Demos

Turn-Signal Demo

signal signal

Try the Turn-Signal Demo to see the directions script in action. Click on the icons to navigate the pages. Then use your browser’s back button to return.

Notice how the slide animation changes direction based on the order of the pages.

Image Viewer Demo

The Image Viewer Demo showcases various animations for the same page: sliding from right or left, resizing, and changing its aspect ratio.

Thanks to the link-types script, you can control which animation to use by adding annotations to the links that navigate to the page.

Fish Pond Demo

Looking for a more dramatic example of how to control view transition types with your links?

Check out the Fishpond Demo! Glide smoothly into the water or take a bold dive.

Explore the pond to learn more about how the demo was implemented.

Forced Traversal

To see the forced traversal script in action alongside the directions script, head over to the Vtbot Demo! No matter how many times you click the tabs in the header, your browser history will always have just three or less entries.

You can skip the Astro-specific details on the demo page. Script details are explained below

Installation

The scripts provided by the turn-signal package run entirely in the browser during navigation. Since they hook into events triggered early in the page load process, they must be included as classic1 JavaScript <script> elements within the <head> of any HTML page you want to enhance with these effects.

All scripts are used the same way:

  1. Install @vtbag/turn-signal from npm.
  2. In your project, add scripts from the package to the <head> element on all pages of your site.

Details depend on your project setup and the frameworks used, but with a bundler like vite it can be as simple as:

import signal from "@vtbag/turn-signal/directions?url";
<html>
<head>
<script src={signal} />
...
</head>
...
</html>;

The scripts offered by the turn-signal package are:

ScriptModule SpecifierPackage Path
directions@vtbag/turn-signal/directionslib/directions.js
link types@vtbag/turn-signal/link-typeslib/link-types.js
forced traversal@vtbag/turn-signal/forced-traversallib/forced-traversal.js
directions + link types@vtbag/turn-signallib/index.js
same as above with debug output@vtbag/turn-signal/debuglib/debug.js

How to Configure the Scripts

The scripts can be configured or guided by attributes on the script element itself or anchor elements in case of the link types script.

Directions Script

The @vtbag/turn-signal/directions script accepts several config options. Those are specified as data-* attributes on the <script> element. They are all optional.

If used without any explicit parameters, the Turn-Signal

  • sets the backward view transition type if it detects backwards history traversals.
  • For navigation to the current page, it sets a view transition type called same.
  • For all other navigation, the forward type is set.
  • On the old page, the old type is added, and on the new page the new type is added.

If you want to tell the script about the order of the pages on your site, use the data-selector attribute to select links to all pages using CSS selectors. This site uses

<script ... data-selector="header a, nav.sidebar li a"></script>

Here the first CSS selector selects the home page and the second all pages from the global site navigation. This selector will fit for all Starlight sites.

Config optionTypeEffect
data-selectora CSS selector

If your site has a concept of previous and next page, use this to tell the Turn-Signal about your pages and their order. One selector to find them all.

The selector should return one element per page of your site, and each element must have an href attribute. Typically, you would select <a> elements from your navigation bar, such as data-selector="nav a".

If your site doesn’t already have a suitable structure, consider generating a list of pages in a non-intrusive way. For example, you could use <a> elements inside a <template> element or <link> elements with a custom rel attribute.

The sequence of selected URLs is used to derive the navigation direction. Navigation to a page that comes later in the sequence of selected URLs will be a forward navigation. Navigation to a page that comes earlier in the sequence of selected URLs will be a backward navigation.

data-direction-typesbackward-value, same-value, forward-value

The Turn-Signal sets view transition types to convey the derived direction in a form that can easily be used for styling your animations.

By default, the types are backward, same, and forward, i.e. as if set using data-direction-types="backward, same, forward".

An alternative would be data-direction-types="reverse,," This would do nothing for same page and forward navigation, but would set the reverse type on backward navigation.

On the old page, the Turn-Signal also sets a type called old and on the new page it sets a type called new. This behavior can not be customized.

data-direction-attributeattribute-name, backward-value, same-value, forward-value

For compatibility with existing code you might want to use attributes rather than view transition types to select different styling for your animations.

You can specify what attribute to use and what values to set. Here is an example of how to make Turn-Signal directions compatible with the CSS Astro generates for transition:animate: data-direction-attribute="data-astro-transition, back, forward, forward".

If you install the link-types script, you can annotate every <a> element with view transition types!

The script finds anchor elements with a data-vtbag-link-types attribute and uses the identifiers to set up view transition types for that navigation. The value can be a space separated list of identifiers. Even better, you can have two or even three of those lists separated by a slash (/). If multiple lists are specified, they are used for backward history navigation and re-visits of the current page

PatternMeaning
zUse z for all navigation types
x/zUse x for backward history traversals, and z for all other navigation types
x/y/zUse x for backward history traversals, y for re-visits to the current page, and z for all other navigation types

Like the directions script, the link types script also applies the old type to the old page and the new type to the new page.

Forced Traversal

The forced-traversal script replaces forward navigation with history traversals if the target pages has been visited before.

Users may be annoyed when a website messes up their history entries, but there are situations where replacing navigation with traversals has its appeal. Use at your own discretion.

There are no configuration options and the forced-traversal script does not take any parameters.

How to Use View Transition Types

You can use the view transition types set by the scripts in your CSS to select different animations and other CSS properties. For information on the basic mechanisms see the styling page.

Select Animations

This example shows how to use a cross-fade effect by default and to replace that with a sliding animation iff a view transition type called shift-right is set.

::view-transition-old(main) {
animation: 200ms both fade-out;
}
::view-transition-new(main) {
animation: 200ms both fade-in;
}
:active-view-transition-type(shift-right) {
&::view-transition-old(main) {
animation: 200ms both slide-out-to-left;
}
&::view-transition-new(main) {
animation: 200ms both slide-in-from-right;
}
}

That way you can have different animations for the same page depending on the specific user action.

CSS for Reverse Animation

Chances are more often then not that you can not simply revert the direction of your animation for the backward effect. Assume you have slideOutToLeft for the old image and slideInFromRight for the new image.

Example for forward animations
::view-transition-old(main) {
animation-name: slideOutToLeft;
}
::view-transition-new(main) {
animation-name: slideInFromRight;
}
@keyframes slideOutToLeft {
to {
transform: translateX(-100%);
}
}
@keyframes slideInFromRight {
from {
transform: translateX(100%);
}
}

Just changing the direction of the animations would lead to something best named slideInFromLeft for the old image and slideOutToRight for the new image. While the animations are about right, just setting animation-direction: reverse would apply them to the wrong images. You do not want to slide in the old image or slide out the new image. So better you swap the keyframes, too …

Example for fitting backward animations
:active-view-transition-type(backward) {
&::view-transition-old(main) {
animation-name: slideInFromRight;
animation-direction: reverse;
}
&::view-transition-new(main) {
animation-name: slideOutToLeft;
animation-direction: reverse;
}
}

… or even explicitly define keyframes for slideOutToRight and slideInFromLeft if you feel that is clearer.

… and you can also define different animations for :active-view-transition-type(same), i.e. for links to the current page, e.g. in a navigation bar by using the same pattern.

Switching Transition Names

The scripts set transition types and direction attributes on the old page and the new page of the cross-document view transition. Even though CSS for animating cross-document view transitions is always taken from the new page, there is an important use case for directions on the old page: You can exclude elements from view transitions depending on the direction.

With directions on the old page you can not only exclude new images but also old images!

How to switch off a view transition group based on direction
main {
view-transition-name: main; /* for forward and backward slides */
}
:active-view-transition-type(same) main {
view-transition-name: none; /* but no transition for links to the same page */
}

The scripts also always sets view transition type old on the old page and new on the new page. So you can be even more specific:

How to switch off the new image, only
:active-view-transition-type(same):active-view-transition-type(new) main {
view-transition-name: none; /* but only an old image for links to the same page */
}

Footnotes

  1. In other words, module scripts can’t be used because their deferred loading would make them too late to capture the early events.