Last updated: First published:
Guide: Add View Transitions to Starlight
This guide describes what has to be done to add view transitions to an existing Starlight site using the Starlight support of the 👜 Bag of Tricks ✨.
Otherwise stay tuned for how to add Astro’s <ViewTransitions>
support to Starlight and enjoy view transitions on all browsers and thanks to Astro’s simulation even on Firefox and other browsers that do not yet have native support for the View Transition API.
Don’t be afraid that this will turn your beautiful website into a blinking jukebox. This Jotter is just an exaggerated example to show what is possible. If you follow the instructions below, you will be rewarded with a very subtle, SPA-like transition effect. And yes, if you want, you can of course later turn the whole thing into a … blinking jukebox.
The introduction page on The Bag’s view transition support for Starlight defines three categories of features for The Bag’s view transition support. This guide currently covers level 1, the mandatory features.
Enable View Transitions
To enable view transitions, you will extend Starlight’s Head
component. You create a replacement component and instruct Starlight to use it as a substitute for its Head1. The replacement component covers all aspects classified as mandatory actions. Components to cover the more optional things are in the works!
Step 1: Install astro-vtbot
The 👜 Bag of Tricks ✨ is available on npm. The package is named astro-vtbot
.
It can be installed as a package or as an Astro integration. See the installation page for details and differences. Let’s assume you just want to add it as a package:
Make sure that you have the latest version installed.
Step 2: Create a Head Component
Create a new component inside your project. The file name does not matter. For this example, we choose ./src/components/starlight/Head.astro
. You can copy & paste the content for this file:
Step 3: Use the New Head Component
Now you tell Starlight to replace its Head
component with yours. Open the astro.config.mjs
2 file from your project directory. Make it look like shown below: Make sure that there is an entry named components
in the options of your Starlight integration. It must specify a Head
entry with the path of the file you have just created.
You already had a different entry for Head:
in your astro.config.mjs
file?
In this case you have to change the
import
ofStarlightHead
in your newHead
component. Change the import so that it does not load Starlight’s standardHead
, but the component you had in your components mapping before:
Try it out for yourself!
Start your project. No matter what browser you use, you should see that there are no full page loads when navigating. The scroll bar and category toggles do not reset when you go to the next page. The search bar remembers your last entry.
In Chromium browsers, you will see the browser’s standard animations for few transitions, i.e. a very quick fade-over of the complete viewport. Browser without native support for view transition will not yet show animations, but just seamlessly swap pages.
Having no fancy, whole viewport animation on a browser without native view transition support is a good thing here. As the exit and entry animations are required to play in sequence, you can not get a real cross-fade. But see here on how to add a cool animation to the main section.
In the next section, we will extend the view transition effect by adding an animation to the main content area and optimizing the styling.
Configuring View Transitions
You can configure several aspects of the view transition effect in your new Head
component.
Animating the Main Section
You can add a transition:animate=...
directive↗ to the <VtbotStarlight>
component. The Bag will teleport the directive from there to the <main>
element of the Starlight page. This is the way to enable view transition animations for the main content area of a Starlight site for all browsers.
If you want to add further styling to the transition group, you can explicitly name it by accompanying transition:animate
with transition:name="..."
. This way you replace the random name that Astro automatically gereted for you with a chosen name that you can use in CSS rules, see the extended configuration example below.
Fallback Control
What behavior↗ do you want for browsers that do not support view transitions natively? Choose from none
, swap
or animate
. Use the viewTransitionFallback
attribute of the <VtbotStarlight>
component to tell The Bag what you have chosen:
This only serves as an example. It is not necessary to set viewTransitionsFallback
to animate
as this is the default setting anyway.
Sidebar Behavior
By the default, the sidebar content will not change on navigation. Only the current page marker will be changed, opened, and scrolled into view. There are several ways how you can change this behavior.
Additional Styling
You can also add a <style is:global>
section to the file to style the ::view-transition-*
pseudo elements for browsers with native view transition support.
Extended Configuration Example
Let’s revisit our Head
component and see how it looks with the extensions described above:
Optional Features
This section shows how to add features from the optional feature list. This has just two entries right now but will soon grow further.
Loading indicator
Independent of the Starlight support, The Bag provides loading indicators for you to choose from. The above example contains
- the indicator that makes your favicon flash happily in the top right corner during loading (
<LoadingIndicator />
) - as well as the
<ProgressBar />
with some basic styling.
Choose one of those.
Eliminate Pseudo-Scrolling
The default morph animations might look irritating when applied to elements that are larger than the current viewport. This is typical the case if you add an animation to your main area. There is a whole section on the effect and possible solution in this Jotter.
If you just want to get rid of it, add the following to your Head
component:
For details on the PageOffset component see its description.
Page Order Directions
In the overview, I teased that it might be nice if the direction of the view transition would depend on the page order. Normally, whenever you click on a link, Astro plays a forward animation and when you hit the browser back key, you see a backward animation.
- If you have previous and next page links on the bottom of your page, clicking them starts forward animations.
- Clicking page 1, page 2, page 1 in the sidebar in this order gives you three forward animations.
This would not make a difference if you use the default fade animation as it looks the same forwards and backwards. But the default slide animation for example has a direction. And maybe your special Starlight animation has one, too.
The Bag has a component called <PageOrder />
that you can add to your Head
component. It automatically changes the order of normal navigation and history traversals. If you navigate to a page that is further down in the sidebar, that will be a forward navigation. If you navigate to a page further up, this is back. If not both, the from and the to page are found in the sidebar, direction retains its original value.
All Astro animations support forward and backward out of the box. But what, if you click on a link to your current page? Then the <PageOrder />
component sets a third direction called stay. The default animations interpret unknown directions as forward. This is usually fine and you are all set!
If you want to add a special animation for your the stay direction, …
… you can do so by adding CSS for it:
Another page in the Jotter explains why you need four definitions and what purpose they serve. The rules with ::view-transition-...(main)
address the transition named “main” for browsers that do have native view transition support. The last two rules address the <main>
element for browsers that don’t.
Refrain from merging rules with pseudo-elements with rules without pseudo-elements. Browsers treat unknown elements or properties as errors and discard the entire rule, even if they could understand half of the selector.
Additional Morph Effects
Now it’s getting funny! I made a component that I could use to make the headings of the pages stand out during view transitions. It is called AutoNameSelected
.
Without any parameters it selects all headings on all pages and assigns the view transition names vtbot-hx-0
, vtbot-hx-1
, …. This has the effect that the n-th heading on the current page and the n-th heading on the next page from a view-transition-group. The headings are cut out of the page context and the old heading morphs into the new heading when navigating.
You see this effect on the Jotter pages.
After I added some configuration options to the component, I saw much more potential. Have a look at the image gallery demo, which uses AutoNameSelected
to shuffle image tiles around. Or take a look at the table of contents to the right which has a similar effect. Here is how that is done:
Event though this component was crafted as part of The Bag’s Stalivht support, it is a general purpose component that can well be used beyond Astro.
Customize the Sidebar
In case you want to change the default behavior of the sidebar, the 👜 Bag of Tricks ✨ offers some switches and utility functions.
Replace the Sidebar Content
You can have the content of the sidebar replaced on each navigation. To do this, add the replaceSidebarContent
attribute to the VtbotStarlight
tag in your Head
component.
This will swap in the sidebar content from the next page when you navigate. This allows you to have different sidebar content on your pages. It also allows you to control the state of the categories, i.e. whether they are shown open or collapsed, with your static page content. Auto-opening and auto-scrolling to the current page marker is not disabled by this switch.
Retain the Current Page Marker
If you want to take control over the current page marker yourself, you can switch of the default update by adding the retainCurrentPageMarker
attribute to the VtbotStarlight
tag in your Head
component. This will also disable auto-opening and auto-scrolling to the current page marker.
On navigation, this leaves the current page marker as is. If you already set the replaceSidebarContent
attribute, you do not need retainCurrentPageMarker
. To assist you in updating the page marker yourself, The Bag offers these utility functions:
Here best fit in general means exact match, but if there is none, it also looks for the longest common prefix.
To use the utility functions, import one ore more from astro-vtbot/components/starlight/utils
:
Re-initialize Starlight Pages
For most Starlight sites, the following topic is not relevant. We talk about an issue with re-initialization of Starlight pages when there are view transitions that originate from outside the Starlight pages. To be affected by this problem you must fulfill the following conditions:
- Under the same origin (= protocol, hostname, port): You have additional pages that are not part of your current Starlight content, or yiu even have a second Starlight site.
- Those pages also have view transitions enabled.
The issue here is that the Starlight pages are perfectly initialized on the first load, which is a full page load. Later navigation within the Starlight realm is also fine, as we use the <ReplacementSwap />
component to keep the app state up to date. But view transitions that come from outside the Starlight’s realm are soft reloads that cannot properly re-initialize the app state.
The Bag’s website has this structure. If that also sounds like your setup, use the <BorderControl />
component to define and protect your Starlight realm by forcing full page loads on incoming view transitions.
Here is the setting used by the Jotter to protect it from incoming view transitions from demos or reusable components.
Make Some Noise
I apologize if the sounds of the jotter bother you, but I just couldn’t resist triggering something other than animations through the life cycle events of the astro view transitions. Maybe you have a more subtle sound file than the one the jotter uses. If you want to try it out, it’s really easy:
For further details see the description of the <SwapSound />
component.
Custom Combo Options
What follows is a yet more extended version of the extended configuration example, but without the styling examples. It is not meant to be copied over into your Head
but rather as a showcase what optional components you might mix and match in your own Head
component. The section with the optional components is highlighted.
Friendly Neighbor
Currently, The Bag’s support for view transitions is aware of and compatible with the following plugins you might use with your starlight site: *
HiDeoo/starlight-image-zoom
(https://starlight-image-zoom.vercel.app/↗)@lorenzo_lewis/starlight-utils
(f.k.a.starlight-mul ti-sidebar
) (https://starlight-utils.pages.dev/↗)HiDeoo/starlight-blog
(https://starlight-blog-docs.vercel.app/↗)@astrojs/starlight-docsearch
(https://starlight.astro.build/guides/site-search/#algolia-docsearch↗)
I plan to continue to support compatibility with these packages in future versions of the 👜 Bag of Tricks ✨.
Known Issues
There are some facts that you should be aware of. None of them is a real obstacle on using The Bag’s Starlight support.
View Transitions into Your Starlight Site from the Outside
If you link to a page of your Starlight site from the outside, you should not use view transitions, but do a full page load instead. This way, your Starlight site will be properly initialized This is also important if you create a link between two Starlight sites.
The website of The Bag automatically intercepts all links that return to the /jotter/
and does a full page load. The Bag will soon offer a reusable component to facilitate that.
Not tested with arbitrary Starlight options
Starlight supports a lot of different config options. The Bags view transition support is so far only tested with a few typical combinations.
Multilingual Starlight sites force a complete reload of the page if you select a different language. This is a good thing, as the app frame changes completely and has to be reloaded anyway.
As always, feedback is very welcome!
I look forward to your feedback! Did it work for you? What were the biggest hurdles? What are you looking for?
Get in touch on Discord or discuss on the 👜 Bag of Tricks ✨‘s github page↗. And of course I would be more than happy if you would like to sponsor my projects↗! Made with 💖👜✨!
Footnotes
-
Here is a pointer↗ to background information on how overriding components works. ↩
-
Assuming
.mjs
as the extension here but.ts
would also be fine, of course! ↩