- Before we begin
- The idea
- Scenarios
- 1. When navigating from items_overview to items_detail …
- 2. When navigating from items_overview to items_detail … and then hitting UA-back (so that it’s a navigation from items_detail to items_overview) …
- 3. When navigating from items_detail to items_overview …
- 4. When navigating from items_detail to items_overview … and then hitting UA-back (so that it’s a navigation from items_overview to items_detail) …
- 5. When navigating from <any page> to/from the --about page …
- 6. When navigating from a pagination page x to pagination page y …
All what follows builds on top of https://wicg.github.io/declarative-partial-updates/css-route-matching/ and assumes a routemap of the following (here defined in CSS, but that could as well be <script type=routemap>):
@routes {
--home: urlpattern("/");
--items_overview: urlpattern("/items{/:page_num}?");
--items_detail: urlpattern("/items/:item_id");
--categories_overview: urlpattern("/categories");
--categories_overview: urlpattern("/categories/:category_name{/:page_num}?");
--about: urlpattern("/about");
}The Cross-Document View Transition opt-in is also present in the CSS:
@view-transition {
navigation: auto;
}- Use the route matching from https://wicg.github.io/declarative-partial-updates/css-route-matching/
- Introduce
:navigation-triggerto indicate the element that initiated the navigation – see w3c/csswg-drafts#11801 - Introduce
:link-to()to do link matching:link-to(<route-location>)= Thehrefof the link (or theactionof the form) matches the<route-location>(=<route-name> | <urlpattern()>):link-to(<route-location> with param_name: value)= Thehrefof the link (or theactionof the form) matches the<route-location>and theparam_namein thehrefis equal to the givenvalue.param_name= a<custom-ident>that is the name of URL’s named params, e.g.page_id.value= value to match against- Can be a direct value (e.g.
3) - Can be a
navigation-param()
- Can be a direct value (e.g.
- Also allowed is doing comparisons (numerics only)
:link-to(<route-location> with param_name < value):link-to(<route-location> with param_name > value):link-to(<route-location> with param_name = value)
- Introduce
navigation-param(<route-keyword>, param_name)to extract a route parameter value from thefromortooratroute locations.navigation-param(from, page_id)= extract thepage_idfrom thefromroute location.navigation-param(to, page_id)= extract thepage_idfrom thetoroute location.navigation-param(at, page_id)= extract thepage_idfrom theatroute location.navigation-param(from, page_id, 1)= extract thepage_idfrom thefromroute location, falling back to1if none is set.
- Introduce
withas a<route-keyword>to allow param matching on route locations. What follows afterwith, is a mathematical expression.@navigation ((at: --items_overview) and (with: navigation-param(at, page_id, 1) = 7) { … }= When at the routeitems_overviewwith its parampage_idbeing7.@navigation (with: navigation-param(from, page_id, 1) < navigation-param(to, page_id, 1) ) { … }= When thepage_idin thefromroute is less than thepage_idfrom the to route.
One can find out which element triggered the navigation using the :navigation-trigger selector.
/* When navigating from --items_overview to --items_detail */
@navigation ((from: --items_overview) and (to: --items_detail)) {
/* On the from page … */
@navigation (at: --items_overview) {
/* Capture the image of the link in the list that was clicked */
#list a:navigation-trigger img {
view-transition-name: photo;
}
}
/* On the to page … */
@navigation (at: --items_detail) {
/* Capture the image in the #hero element */
#hero {
view-transition-name: photo;
}
}
}(Curious --about how UA-back would work here? See scenario 4)
2. When navigating from items_overview to items_detail … and then hitting UA-back (so that it’s a navigation from items_detail to items_overview)
For this we use link matching:
/* When navigating from --items_detail to --items_overview */
@navigation ((from: --items_detail) and (to: --items_overview)) {
/* On the from page … */
@navigation (at: --items_detail) {
/* Capture the image in the #hero element */
#hero {
view-transition-name: photo;
}
}
/* On the to page … */
@navigation (at: --items_overview) {
/* Find the image inside the link that
links to the route `items_detail`
with the `item_id` in that link
matching the from route’s `item_id` param.
Now go capture that image as the photo for the view transition.
*/
a:link-to(--items_detail with item_id: navigation-param(from, item_id)) img {
view-transition-name: photo;
}
}
}This is already covered by scenario 2.
4. When navigating from items_detail to items_overview … and then hitting UA-back (so that it’s a navigation from items_overview to items_detail)
There is no :navigation-trigger to use on the items_overview page to make sure the big photo gets captured, but that can be solved with :link-to. Taking the code from scenario 1, the updated code becomes:
/* When navigating from --items_overview to --items_detail */
@navigation ((from: --items_overview) and (to: --items_detail)) {
/* On the from page … */
@navigation (at: --items_overview) {
/* Capture the image in the link that was clicked */
#list a:navigation-trigger img {
view-transition-name: photo;
}
/* When there was nothing clicked, try and get the image
inside the link to `items_detail`
with the `item_id` in that link
matching the to route’s `item_id` param.
*/
#list:not(:has(a:navigation-trigger) {
a:link-to(--items_detail with item_id: navigation-param(to, item_id)) img {
view-transition-name: photo;
}
}
}
/* On the to page … */
@navigation (at: --items_detail) {
/* Capture the image in the #hero element */
#hero {
view-transition-name: photo;
}
}
}… I want a VT to run, but only have it capture the main elements of the site{#…-i-want-a-vt-to-run,-but-only-have-it-capture-the-main-elements-of-the-site}
Nothing fancy here, just a matter of more detailed @navigation matching:
/* When navigating to or from --about */
@navigation ((to: --about) or (from: --about)) {
/* On the --about page … */
@navigation (at: --about) {
header {
view-transition-name: header;
}
main {
view-transition-name: main;
}
}
/* On the not-about page … */
@navigation not (at: --about) {
header {
view-transition-name: header;
}
main {
view-transition-name: main;
}
}
}Or simplified:
/* When navigating to or from --about */
@navigation ((to: --about) or (from: --about)) {
/* Capture these elements on both pages for the View Transition */
header {
view-transition-name: header;
}
main {
view-transition-name: main;
}
}Or using an indirection:
/* When navigating to or from --about */
@navigation ((to: --about) or (from: --about)) {
/* Set the VT Type to simple-fade */
@view-transition {
navigation: auto;
types: simple-fade;
}
}
/* When a VT is active and its type is simple-fade … */
html:active-view-transition-type(simple-fade) {
/* Capture these elements on both pages for the View Transition */
header {
view-transition-name: header;
}
main {
view-transition-name: main;
}
}This is a matter of comparing URL parameters across pages
/* When navigating from --items_overview to --items_overview */
@navigation ((from: --items_overview) and (to: --items_overview)) {
/* The from route’s page_id is smaller than the to route’s page_id: slide everything to the left */
@navigation (with: navigation-param(from, page_id, 1) < navigation-param(to, page_id, 1)) {
@view-transition {
navigation: auto;
types: slide-left;
}
}
/* The from route’s page_id is greater than the to route’s page_id: slide everything to the right */
@navigation (with: navigation-param(from, page_id, 1) > navigation-param(to, page_id, 1)) {
@view-transition {
navigation: auto;
types: slide-right;
}
}
}