🧹 The Linter
The <Linter />
component helps you to avoid common issues when using view transitions.
Contents
Why should I use this component?
View transitions can fail or show strange effects if you set transition attributes the wrong way. These situations are often not easy to debug. The <Linter>
component helps you to ensure the following rules when using view transitions.
- View transition names must be unique on a page
- Identifiers used with
transition:persist={identifier}
must be unique on a page - Elements that define
transition:persist
must not be nested - Elements that define
transition:persist
should specify an identifier - Elements that define
data-vtbot-replace
must specify an identifier - Identifiers used with
data-vtbot-replace={identifier}
must be unique on a page - Elements that define
data-vtbot-replace
must not be nested - Elements copied by
transition:persist
ordata-vtbot-replace
should not lose their transition name - Elements copied by
transition:persist
ordata-vtbot-replace
should not lose their scoped styles - View-transition-names must be valid CSS identifiers↗.
- Check for script types that are not executed by the view transition router
Example output
The <Linter/>
component writes its results to the browser console while you navigate your pages.
Unique view transition names
Unique view transitions names are a requirement of the view transition API. Chrome will display an error message in the console - even without the linter - if this rule is violated. But at least as of version 120, Chrome only reports violations for the page you are navigating away from, not the one you are navigating to. The <Linter>
component always reports both sides of a navigation.
Where do view transition names come from?
When looking for view transition names, the linter considers these sources:
- Astro’s
transition:name
directive with or without an explicit identifier - The implicite definition of a
view-transition-name
via Astro’stransition:animate
directive - CSS rules that define
view-transition-name: identifier
in style attributes of HTML elements or style sheets inlined or linked on the page
Nested elements
Nested elements that define transition:persist
or data-vtbot-replace
can lead to strange results and non-deterministic behavior. It is best to avoid them.
Identifiers for transition:persist
In Astro, transition:persist
can be used without an explicit identifier. Then the name of the view transition element is used, which is fine if it was set by transition:name=...
. If neither transition:name
nor an identifier for transition:persist
is specified, Astro implicitly uses the identifier of the transition area, which looks like this: astro-ya3eq23s-4
. Often this matches the desired element on the target page, but it is also quite fragile. It is best practice to use explicit names. So if you use transition:persist
with an implicit, generated identifier, the <Linter>
component will issue a warning.
Loss of transition names and scoped styles
Astro emits scoped styles by default, with different strategies to bind styles to elements. What they all have in common is that they use special identifiers for CSS classes (astro-xxxxxxxx
) or custom data attributes (data-astro-cid-xxxxxxxxxx
). If a DOM element gets copied to another DOM with transition:persist
or data-vtbot-replace
, these identifiers are copied together with the element. This typically results in the link to the style sheet being broken if the style sheet to which the identifier refers is not also copied. If you want to use the same style sheets on different pages, you can define global styles with the is:global
directive, for example.
The way Astro links elements with their view-transition-name uses a very similar mechanism. Elements that are copied to another DOM often lose their view-transition-name if the name is defined with transition:name
. Alternatively, you can define the name of the view transition with the style attribute: <elem style="view-transition-name: identifier">
. In this way, it remains a part of the persisted element.
The <Linter>
component will issue a warning in these situations. They are probably errors, but the <Linter>
does not know if the effects are intended.
You can make the linter keep its mouth shut for false positives by adding a meta element that lists transition names that should be ignored to the <head>
of your page:
Valid identifiers for view-transition-names
When you use transition:name
or transition:persist
, Astro normalizes the specified identifier to ensure that it is a valid CSS <custom-ident>
value. But it is also possible to define view-transition-names
directly in style attributes or style sheets. In this case, the <Linter>
component will issue a warning if the identifier is not valid.
Valid custom identifiers must not start with a number, i.e. a digit, or a “-
” sign followed by a digit. Characters beside letters, digits, underscore and the minus-sign must be properly escaped. The reserved names unset
, initial
, inherit
and none
are also invalid identifiers for view-transition-names
.
Script types not handled by the router
<script>
elements might have a type
attribute. The most common value for this attribute is "module"
to indicate that the script is an ESM. Other valid values are ""
and text/javascript
. Both indicate that this script is JavaScript. As this is the default anyways, authors are encouraged to not use these values.
If the type
attribute is set to anything else, the script is ignored by the browser. This is fine for e.g. "text/partytown"
where the partytown lib searches and executes such scripts. Some optimization techniques that instrument HTML might also use there own values. Even though the script will be executed eventually, this typically happens after the astro:page-load
event. If you define an event listener for astro:page-load
in those scripts, it will not be called. The <Linter>
component will issue a warning in these situations.
Usage
Install astro-vtbot in your project as an Astro integration with npx astro add astro-vtbot
or as a node package with npm install astro-vtbot
. If you choose the integration option, vtbot
will automatically add the <Linter/>
component to all the pages that use <ViewTransitions>
.
If you do not choose the integration, you have to add the <Linter/>
component to your pages manually: Insert the <Linter/>
component near the <ViewTransitions />
component into the <head>
of your page.
Properties
The <Linter/>
component offers two properties to control its behavior:
Property | Type | Effect |
---|---|---|
production | boolean | Activate the Linter in production mode. Typically, the linter is active in DEV mode, only. Set this property if you want to activate the Linter in production, too. |
expanded | boolean | In the log that is written to the browsers console, the top level groups are collapsed by default. With expand you can force them open from the start. |