From 84755aae9703b3882a8fdeb20f2dae58fd44c364 Mon Sep 17 00:00:00 2001 From: Martin Trapp <94928215+martrapp@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:29:56 +0200 Subject: [PATCH] update --- astro.config.ts | 5 +- bin/build | 4 +- package-lock.json | 64 ++-- package.json | 10 +- public/_headers | 8 +- src/components/Logo.astro | 41 +++ src/components/NHead.astro | 46 ++- src/content/docs/basics/styling.mdx | 299 ++++++++++++++++++ src/content/docs/tips/css.mdx | 8 +- src/content/docs/tips/over-exposure.mdx | 85 ++++- src/content/docs/tools/cam-shaft.mdx | 62 +--- src/content/docs/tools/element-crossing.mdx | 61 +--- .../docs/tools/inspection-chamber/index.mdx | 62 +--- src/content/docs/tools/turn-signal.mdx | 124 ++++---- 14 files changed, 558 insertions(+), 321 deletions(-) create mode 100644 src/components/Logo.astro create mode 100644 src/content/docs/basics/styling.mdx diff --git a/astro.config.ts b/astro.config.ts index 2810df9..837ac3b 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -36,7 +36,7 @@ export default defineConfig({ integrations: [starlight({ title: '@vtbag', components: { - Head: "./src/components/Head.astro", + Head: "./src/components/NHead.astro", PageTitle: "./src/components/PageTitle.astro" }, plugins: [starlightImageZoom()], @@ -101,7 +101,8 @@ function sidebar() { { label: 'View Transition API', link: "/basics/api/" }, { label: 'View Transition Examples', link: "/basics/examples/" }, { label: 'Structure of Pseudo-Elements', link: "/basics/pseudos/" }, - { label: 'Mechanics of Default Animations', link: "/basics/default-animations/" } + { label: 'Mechanics of Default Animations', link: "/basics/default-animations/" }, + { label: 'Styling View Transition', link: "/basics/styling/" } ] }, { label: 'CSS Tips & Tricks', diff --git a/bin/build b/bin/build index 9946fe2..3fe5919 100755 --- a/bin/build +++ b/bin/build @@ -10,4 +10,6 @@ fi [ -n "$GS" ] && echo -n "google-site-verification: ${GS}" > ./dist/${GS} find dist -type f -exec grep -l 'script src="/chamber.js/"' {} \; | xargs sed -i 's|script src="/chamber.js/"|script src="/chamber.js"|' -find dist -type f -exec grep -l 'source src="/video.mp4"' {} \; | xargs sed -i 's|source src="/video.mp4"|source src="https://cdn.pixabay.com/video/2021/02/21/65860-515617507_tiny.mp4"|' \ No newline at end of file +find dist -type f -exec grep -l 'source src="/video.mp4"' {} \; | xargs sed -i 's|source src="/video.mp4"|source src="https://cdn.pixabay.com/video/2021/02/21/65860-515617507_tiny.mp4"|' + +echo "/*\n cache-control: max-age=60, stale-while-revalidate=120, stale-if-error=86400\n last-modified: `date -u +'%a, %d %b %Y %H:%M:%S GMT'`\n\n/\n cache-control: max-age=60, stale-while-revalidate=120, stale-if-error=86400\n last-modified: `date -u +'%a, %d %b %Y %H:%M:%S GMT'`" > dist/_header \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index aa9fae6..3581d07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,15 @@ "@astrojs/starlight": "^0.28.3", "@playwright/test": "^1.48.1", "@splidejs/splide": "^4.1.4", - "@types/node": "^22.7.6", + "@types/node": "^22.7.9", "@vtbag/cam-shaft": "^1.0.1", "@vtbag/element-crossing": "^1.0.2", - "@vtbag/inspection-chamber": "^1.0.16", - "@vtbag/turn-signal": "^1.0.3", - "astro": "^4.16.6", + "@vtbag/inspection-chamber": "^1.0.17", + "@vtbag/turn-signal": "^1.1.0", + "astro": "^4.16.7", "astro-breadcrumbs": "^3.2.0", "astro-d2": "^0.6.0", - "astro-vtbot": "^1.10.4", + "astro-vtbot": "^1.10.5", "rehype-autolink-headings": "^7.1.0", "rehype-external-links": "^3.0.0", "rehype-slug": "^6.0.0", @@ -2108,9 +2108,9 @@ } }, "node_modules/@types/node": { - "version": "22.7.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.6.tgz", - "integrity": "sha512-/d7Rnj0/ExXDMcioS78/kf1lMzYk4BZV8MZGTBKzTGZ6/406ukkbYlIsZmMPhcR5KlkunDHQLrtAVmSq7r+mSw==", + "version": "22.7.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.9.tgz", + "integrity": "sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==", "dev": true, "license": "MIT", "dependencies": { @@ -2257,9 +2257,9 @@ } }, "node_modules/@vtbag/inspection-chamber": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@vtbag/inspection-chamber/-/inspection-chamber-1.0.16.tgz", - "integrity": "sha512-90qz1rcIlrNyajH2HpDHYR/FiwsMQjTA8URgDtC0XZx1Ny2gpmSYSRVFzbGPt3qPD8A8fuGQS31IJa0h1fuSgw==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@vtbag/inspection-chamber/-/inspection-chamber-1.0.17.tgz", + "integrity": "sha512-MDtO+qNtd4AkxjkJpQD3N2YhMQUaEdSkdArwk7Cy/81iMbOXhZek6cRQu06jn6JnrRv4andtb1oHtl5rYuNSSQ==", "dev": true, "license": "ISC", "dependencies": { @@ -2271,9 +2271,9 @@ } }, "node_modules/@vtbag/turn-signal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@vtbag/turn-signal/-/turn-signal-1.0.3.tgz", - "integrity": "sha512-qa+dVrjJrg6LIcz22oI7X3QpS16qImZx/WscpMaghfpOF5pvHVH5O9zJkha5qxdqAELNm2gwk5O0+3q7I389yw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vtbag/turn-signal/-/turn-signal-1.1.0.tgz", + "integrity": "sha512-2WoBbs3v7hWxu6BXvWab4G8MJIFr21vEcJ5Y4mhXlYifVzHLSf5M+aHxEEk62DMOeVmjHwqt8DK47GYnFrEqmg==", "dev": true, "license": "ISC", "funding": { @@ -2282,10 +2282,11 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -2434,9 +2435,9 @@ } }, "node_modules/astro": { - "version": "4.16.6", - "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.6.tgz", - "integrity": "sha512-LMMbjr+4aN26MOyJzTdjM+Y+srpAIkx7IX9IcdF3eHQLGr8PgkioZp+VQExRfioDIyA2HY6ottVg3QccTzJqYA==", + "version": "4.16.7", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.16.7.tgz", + "integrity": "sha512-nON+8MUEkWTFwXbS4zsQIq4t0Fs42eulM4x236AL+qNnWfqNAOOqAnFxO1dxfJ1q+XopIBbbT9Mtev+0zH47PQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2451,7 +2452,7 @@ "@rollup/pluginutils": "^5.1.2", "@types/babel__core": "^7.20.5", "@types/cookie": "^0.6.0", - "acorn": "^8.12.1", + "acorn": "^8.13.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", @@ -2490,7 +2491,7 @@ "rehype": "^13.0.2", "semver": "^7.6.3", "shiki": "^1.22.0", - "tinyexec": "^0.3.0", + "tinyexec": "^0.3.1", "tsconfck": "^3.1.4", "unist-util-visit": "^5.0.0", "vfile": "^6.0.3", @@ -2577,16 +2578,16 @@ } }, "node_modules/astro-vtbot": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/astro-vtbot/-/astro-vtbot-1.10.4.tgz", - "integrity": "sha512-F/0D5i8PTytq7rS+ngtNRPMOC4AY7K7e5hZRjyjmhcEK1TYqDegeWkbUU6uSolChH+DSwyesLlIR8o7hV2PdTQ==", + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/astro-vtbot/-/astro-vtbot-1.10.5.tgz", + "integrity": "sha512-1w5gviyjDn2bh8FGoK0bnULemLkQzkB4MzWUeUxs67u2ZfHaqc+R8O66ztjgSulr334N5RpRmuW11z6ciW+v2g==", "dev": true, "license": "ISC", "dependencies": { "@vtbag/cam-shaft": "^1.0.1", "@vtbag/element-crossing": "^1.0.2", - "@vtbag/inspection-chamber": "^1.0.16", - "@vtbag/turn-signal": "^1.0.3" + "@vtbag/inspection-chamber": "^1.0.17", + "@vtbag/turn-signal": "^1.1.0" }, "funding": { "type": "github", @@ -7136,10 +7137,11 @@ } }, "node_modules/tinyexec": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", - "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", - "dev": true + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", + "dev": true, + "license": "MIT" }, "node_modules/to-fast-properties": { "version": "2.0.0", diff --git a/package.json b/package.json index 3198128..e860d44 100644 --- a/package.json +++ b/package.json @@ -13,15 +13,15 @@ "@astrojs/starlight": "^0.28.3", "@playwright/test": "^1.48.1", "@splidejs/splide": "^4.1.4", - "@types/node": "^22.7.6", + "@types/node": "^22.7.9", "@vtbag/cam-shaft": "^1.0.1", "@vtbag/element-crossing": "^1.0.2", - "@vtbag/inspection-chamber": "^1.0.16", - "@vtbag/turn-signal": "^1.0.3", - "astro": "^4.16.6", + "@vtbag/inspection-chamber": "^1.0.17", + "@vtbag/turn-signal": "^1.1.0", + "astro": "^4.16.7", "astro-breadcrumbs": "^3.2.0", "astro-d2": "^0.6.0", - "astro-vtbot": "^1.10.4", + "astro-vtbot": "^1.10.5", "rehype-autolink-headings": "^7.1.0", "rehype-external-links": "^3.0.0", "rehype-slug": "^6.0.0", diff --git a/public/_headers b/public/_headers index 2e2eed9..533796d 100644 --- a/public/_headers +++ b/public/_headers @@ -1,4 +1,4 @@ -/basics/pseudos/ - Cache-Control: no-cache - Etag: W/"13d5c-c2EQ2YBn07GjDUKtkkQ7DaNzk70" - Rumpel: "Stielzchen" \ No newline at end of file +/* + cache-control: max-age=60, stale-while-revalidate=120, stale-if-error=86400 + last-modified: Sat, 19 Oct 2024 11:07:00 GMT + diff --git a/src/components/Logo.astro b/src/components/Logo.astro new file mode 100644 index 0000000..c8d11a3 --- /dev/null +++ b/src/components/Logo.astro @@ -0,0 +1,41 @@ +--- +import {Image} from "astro:assets"; +export interface Props { + alt: string; + image: any; +} +--- + +
+
+ +
+
+ diff --git a/src/components/NHead.astro b/src/components/NHead.astro index 4440ac8..8439d0c 100644 --- a/src/components/NHead.astro +++ b/src/components/NHead.astro @@ -8,11 +8,14 @@ import "./vtbag-bar.css"; +{ // @ts-ignore +} + {/* Decent transitions for scrolled down pages */} {/* Make animation direction based on the order of the pages in the sidebar */} - + { /* Make headings stand out during view transitions. Can be used to declaratively add view transition names which open a door to all kinds of funny thing. */ @@ -37,24 +40,28 @@ import "./vtbag-bar.css"; main { view-transition-name: main; } + :active-view-transition-type(same) main { + view-transition-name: none; + } ::view-transition-old(main) { - animation: forwardsSwingOut 400ms ease-in-out; + animation: forwardsSwingOut 500ms ease-in-out; } ::view-transition-new(main) { - animation: forwardsSwingIn 400ms ease-in-out; + animation: forwardsSwingIn 500ms ease-in-out; } :active-view-transition-type(backward)::view-transition-old(main) { - animation: backwardsSwingOut 400ms ease-in-out; + animation: backwardsSwingOut 500ms ease-in-out; } :active-view-transition-type(backward)::view-transition-new(main) { - animation: backwardsSwingIn 400ms ease-in-out; + animation: backwardsSwingIn 500ms ease-in-out; } - :active-view-transition-type(same)::view-transition-old(main) { - animation: shake-old 400ms ease-in-out; + :active-view-transition-type(same)::view-transition-old(root) { + animation: shake-old 500ms ease-in-out; } - :active-view-transition-type(same)::view-transition-new(main) { - animation: shake-new 400ms ease-in-out; + :active-view-transition-type(same)::view-transition-new(root) { + animation: shake-new 500ms ease-in-out; } + @keyframes forwardsSwingOut { 0% { transform: translateX(0); @@ -96,28 +103,15 @@ import "./vtbag-bar.css"; } } @keyframes shake-old { - 25% { - transform: rotateZ(-2deg); - } - 50% { - transform: rotateZ(0deg); - opacity: 0.5; - } - 51%, 100% { + to { + transform: scale(25); opacity: 0; } } @keyframes shake-new { - 0%, 49% { + from { opacity: 0; - } - 50% { - transform: rotateZ(0deg); - opacity: 0.5; - } - 75% { - transform: rotateZ(2deg); - opacity: 1; + transform: scale(0); } } ::view-transition-image-pair(main) { diff --git a/src/content/docs/basics/styling.mdx b/src/content/docs/basics/styling.mdx new file mode 100644 index 0000000..0b7b619 --- /dev/null +++ b/src/content/docs/basics/styling.mdx @@ -0,0 +1,299 @@ +--- +title: Styling View Transition +description: Styling view transitions with :active-view-transition-type and view-transition-class +--- + +import { Steps } from "@astrojs/starlight/components"; + + +The animations introduced by the View Transition API are controlled by CSS rules. The way to make the animations fit your expectations is to alter the [default animation](/basics/default-animations/) by overriding the styles for the [pseudo elements](/basics/pseudos/). + +To do this efficiently you should know what the View Transition API defines as defaults and how to easily change these styles if needed. To simplify styling, level 2 of the API introduced helpers in form of the `view-transition-class` property and the `:active-view-transition*` pseudo-classes. + +## Styling Tasks for View Transitions + +There are three parts that you influence with CSS when it comes to view transitions: +* Tell the View Transition API which DOM elements to setup for individual animations +* Setup animations for elements participating in a view transition +* Trigger CSS for things other than animations + +### Add DOM Elements to View Transition +If you want a DOM element to participate in view transitions with an individual animation, you assign a value to the `view-transition-name` CSS property of the element. The browser will generate three or four pseudo-elements for each DOM element with a view transition name. These are [a transition group, an image pair](/basics/pseudos/#groups-and-image-pairs) and at least one of [the old or new image pseudos](/basics/pseudos/#old-and-new-image-pseudos). + +At the start of the view transition, the **view transition names have to be unique** in the DOM. You can exempt an element from this uniqueness check by setting its (or one of its parent's) styling to `visibility: hidden` or `display: none`. For those elements, the browser won't generate pseudos. + +The browser automatically assigns the view transition name `root` to the `:root` element, i.e. the top-level `` element of the DOM. If you prefer another name, you can override it. If you do not want the `` element to participate in the view transition with own pseudo-elements, you explicitly have to remove the `root` name with either +```css +:root { + view-transition-name: none; +} +``` +or +```html + +``` + +Of course view transition names do not need to be static. You can assign them conditionally, e.g. inside a media query or with a selector that checks for a special CSS class further up the DOM. Another interesting way for conditional assignment of view transition names is to let the m [depend on view transition types](/basics/styling/#conditional-view-transitions). + +Because they are animatable CSS properties, view transition names can even be changed using other animations. So if you like you could automatically change a name over time or depending on the scroll position with scroll-driven animations. + +### Define Animations for View Transitions + +For a single view transition name, `x`, there are different animations that you might want to style. + +If your image-pair has both, an old and a new image, you can control how the old image is replaced by the new image during the transition. Three animations are involved: + +* the animation of the `view-transition-old(x)` pseudo-element, +* the animation of the `view-transition-new(x)` pseudo-element and +* the animation of the `view-transition-group(x)` pseudo-element + +The first two typically work together in a cross-fade or in a combined effect that removes the old image and inserts the new one. The animation of the `view-transition-group()` aligns differences in size, transformation, and position of the old and new image. + +If your 'image-pair' has only a single image, the browser places the `view-transition-group()` at the same position and does not define a morphing animation because there is nothing to align. You will still have the animation for the old or new image, respectively. Instead of a morph animation you have an entry animation if the old image is missing or an exit animation if the new image is missing. + +Sometimes you want the animations for new and old images behave different for morph and entry/exit animations. this two cases can be distinguished with the :only-child CSS pseudo-class. + +For example, assume you have an element that pops in with an entry animation and pops out with an exit animation. You do not want an animation if the element is on both sides of the transition, i.e. neither exit nor entry. + +```css +:is(::view-transition-old(x), ::view-transition-new(x)):not(:only-child) { + animation-name: none; +} +``` + +There might be good reasons for defining completely new animations for your view-transitions. +But when you assign to shorthand properties, most important `animation`, you override the definitions from the user agent stylesheet. And if you do only specify some values for the shorthand, you have to know browser defaults for the values you do not specify explicitly. + +Often it is better to use simple CSS properties rather than the shorthands. That way you keep the former definitions and only change what is important to you. For an example [see the notes on reusing user agent stylesheets](/tips/over-exposure/#reusing-the-user-agent-stylesheet) when redefining fade animations. + +### Styling Beyond Animations + +While the typical task will be redefining animations when styling view transitions, you can also use view transitions to trigger other style changes. You can statically set the properties of the pseudo-elements created by the View Transition API. For example you could add a border. +```css +::view-transition-old(x) { + border: 1pt solid red; +} +``` + +You can also style other DOM elements that are not created by the View Transition API, but keep in mind that those elements are typically [hidden behind the view transition pseudo-elements](/basics/pseudos/#the-theater-curtain) during a view transition. + + + +## The User Agent Stylesheet + +When the View Transition API inserts an animation, it has to give some default values to them. +Here is what the API automatically assign in its user agent style sheet for a view transition name `x`. In keyframe names, `-ua` stands for _user agent_. + +### Group / Morph Animation +If both, the old image and the new image for `x` exist, the API defines a [morph animation from the old to the new image](/basics/default-animations/#morphing-animation-details). The browser generates specific keyframes for each view transition group: + +```css +::view-transition-group(x) { + animation: 0.25s ease 0s 1 normal both running -ua-view-transition-group-anim-x +} +@keyframes -ua-view-transition-group-anim-x { + 0% { + backdrop-filter: ; + transform: ; + height: ; + width: ; + } + 100% { + backdrop-filter: ; + transform: ; + height: ; + width: ; + } +} +``` +The `transform` is used to move the deck with the new image on top of the old image from the old image's size, transform, and position to the new image's size, transform, and position. I.e. if the original element of the view-transition-name has some CSS transformation applied on the old or new page, like rotate or skew, this will also be honored by the generated transform. + +### Cross-Fade of the images + +The typical cross-fade effect of view transitions is implemented by animations defined on the pseudo-elements for the old and new images. The definitions are the same for all view transition names. + +```css +::view-transition-old(x) { + animation: ease 1 normal running + animation-name: -ua-view-transition-fade-out, -ua-mix-blend-mode-plus-lighter +} +::view-transition-new(x) { + animation: ease 1 normal running + animation-name: -ua-view-transition-fade-in, -ua-mix-blend-mode-plus-lighter +} +@keyframes -ua-view-transition-fade-out { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} +@keyframes -ua-view-transition-fade-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} +@keyframes -ua-mix-blend-mode-plus-lighter { + 0% { + mix-blend-mode: plus-lighter; + } + 100% { + mix-blend-mode: plus-lighter; + } +} +``` +The inherited values come from the view transition image pair, which inherits them from the view transition group. `-ua-mix-blend-mode-plus-lighter` is only added if the image pair has both images. For some background on `-ua-mix-blend-mode-plus-lighter` see the [section about mix blend modes](/tips/over-exposure/#set-the-correct-mix-blend-mode). + +### Non-Animation Properties + +Of course styling view transition pseudo-elements is not limited to animations. The browser's user agent stylesheet also contains examples for such styling: + +```css +:root::view-transition { + position: fixed; + inset: 0; +} + +:root::view-transition-group(*) { + position: absolute; + top: 0; + left: 0; +} + +:root::view-transition-image-pair(*) { + position: absolute; + inset: 0; +} + +:root::view-transition-old(*), +:root::view-transition-new(*) { + position: absolute; + inset-block-start: 0; + inline-size: 100%; + block-size: auto; +} +``` + + +## Selecting Pseudo-Elements + + + +### … with Names + +In the examples above you saw the patterns for addressing an individual pseudo-element: The view transition name is used as a parameter of the pseudo-element selector: + +```css +::view-transition-() { + /* property definitions */ +} +``` + +Beside addressing individual pseudo-elements it is also possible to address the pseudo elements of all view transition names at once using `*` instead of a view transition name: +```css +::view-transition-(*) { + /* property definitions */ +} +``` + +Also seen above: You can address the root of all view transition pseudo-elements, although it has no name: +```css +::view-transition { + /* property definitions */ +} +``` + +### … with Classes + +When you have CSS rules for a large number of view transition names, you can use the `(*)` pattern to address a pseudo-element for _all_ transition names and use specific rules with the (`(name-x)`) pattern to handle the exception. There might be situations where you wish for a better developer experience. This is where view transition classes come in. + +Similar to the `view-transition-name` property, you can assign `view-transition-class` values to DOM elements. While view transition names have to be unique in the DOM, view transition class values need not and typically will not be unique. + +When looking at how the view transition classes are used, the similarity to CSS classes is rather obvious. You use them inside the pseudo-element selectors, prefixed by a dot (`.`). +If multiple class names are used to identify a pseudo element, all must match. The following CSS rule can be used to address all view transition groups that have the `nav-link` view transition class: + + +```css +::view-transition-group(*.nav-link) { + /* ... */ +} +``` +This is an example on how to assign the class: +```css +#navbar a { + view-transition-class: nav-link; +} +#prev, #next { + view-transition-class: nav-link some-other-class; +} +``` +This might best be combined with dynamically (per script) added view transition names or tooling where you declaratively assign view transition names. + +### … with Types + +Often you want to support different animations for a given view transition pseudo-element and select one of those alternatives depending on some condition. The concept that the View Transition API provides for this use case is called _active view transition types_. + +While it is active, a view transition might have a set of identifiers called _types_ that can be used in CSS pseudo-classes to select different rules. During the view transition, the set can be altered at any time by assigning to the `types` property of the viewTransition object. The initial set of types can be set when calling `startViewTransition()` or it might be specified using the `types` property inside a `@view-transition` rule. + +The pseudo-class that can check for types is `:active-view-transition-type()`. It takes a comma separated list of types. If at least one of these types is set on the active view transition, the pseudo-class matches the documents root element. + +Thus, the typical patterns to use `:active-view-transition-type()` are: + +Directly prepend the pseudo-class to a pseudo-element selector … +```css +:active-view-transition-type(type)::view-transition-group(main) { + /* ... */ +} +``` +… or equivalently use it with a nested CSS rule. +```css +:active-view-transition-type(type) { + &::view-transition-group(main) { + /* ... */ + } +} +``` + +There is an additional pseudo-class that does not take type parameters. +```css +:active-view-transition { + /* ... */ +} +``` +This selector will match the the `:root` element while a view transition is active. You can use this for example to [change the shape of the pointer](/tips/pointer/) during view transitions. + +When you navigate forth and back through the pages of this site, you see an example on how types are used to select different animation. + + + + +1. Go to the page about [flickers during morph animations](/tips/over-exposure/). You'll see that it slides in from the right, as you go forward within the site's order of pages. +2. Select the page again from the global site navigation (have first to open it on mobile). You see a different effect. +3. Press the browser's back key or button to return to this page. The page slides in from the left as you go backwards in the site's order of pages. + + + +See the [Turn-Signal documentation](/tools/turn-signal/#css-for-reverse-animation) on how this effect is achieved and what pitfalls to avoid. + +#### Conditional View Transitions + +The pseudo-classes can not only be used to define different animations for different situations. It is also possible to let the existence of a view transition group depend on view transitions types. So on some transitions, an element can have its own animation and on others it is just part of some parent and its animation. + +You can find an example for this behavior on this site. When you visit the page about [flickers during morph animations](/tips/over-exposure/#root-causes-for-flashes) and visit it directly again be selecting it in the site navigation, you will see that for navigations to the same page, the `
` section does not have its own view transition. Also when you click the images on that page, you start view transitions where the `
` section is not animated. +See the [Turn-Signal documentation](/tools/turn-signal/#switching-transition-names) on how this effect is achieved and how you could get even more specific and cancel single images instead of whole transition groups. + +## Prefix Pseudo-Elements with `:root`? + +The user agent stylesheet example above is an excerpt from the [View Transition Draft Spec](https://drafts.csswg.org/css-view-transitions-1/). There the pseudo-elements are prefixed with the the `:root` pseudo-class selector. + +The scope of view transitions is the whole document. Until this might get changed in future versions, all pseudo-elements that the API generates are children of the document's root element, addressable using the CSS `:root` selector. So currently `::view-transition-group(x)` and `:root::view-transition-group(x)` are the same thing. And even if some future might bring pseudo-elements that are rooted at different elements, `:root::view-transition*` will still select only pseudo-elements of the document root. + +When you use these patterns in your own stylesheets, be aware that the specificities differ: `:root::view-transition-group(x)` with a specificity of _(0, 1, 1)_ is more specific than `::view-transition-group(x)` with a specificity of _(0, 0, 1)_. + +Now you might conclude that you also need to prefix your view transition pseudo-elements with `:root` if you want to override the defaults. That is not the case. The user agent stylesheet has the least important origin and is overridden by user stylesheet rules — independent of specificity. + + diff --git a/src/content/docs/tips/css.mdx b/src/content/docs/tips/css.mdx index 91bce44..3d2362e 100644 --- a/src/content/docs/tips/css.mdx +++ b/src/content/docs/tips/css.mdx @@ -5,14 +5,16 @@ title: Where to Place CSS for Cross-Document View Transitions? You're aware that enabling cross-document view transitions is as simple as adding the view transition at-rule to your pages. ```css -@view-transition{navigation: auto} +@view-transition { + navigation: auto; +} ``` You also know that both pages, the old one and the new one, must opt in by including this at-rule. Additionally, you might know that this only works if both pages share the same origin, meaning the same protocol, host, and port. However, when you start adding more CSS to introduce additional view-transition names or customize the animations, you might wonder: Do I need this CSS on both pages? ## View Transition Names -View transition names are tied to the DOM, whether they're set directly on elements or added via CSS rules. You must define the names for the old images on the old page and for the new images on the new page. +View transition names are tied to the DOM, whether they're set directly on elements or added via CSS rules. You must define the names for the old images on the old page and for the new images on the new page. This does not need to be static. You can use view transition types to add or remove view transition names via CSS, see the [Turn-Signal example](/tools/turn-signal/#direction-based-control-over-transitions). The last chance to set these names on the old page using JavaScript is during the pageswap event, which occurs right before the screenshots for the old images are taken and navigation leaves the page. @@ -31,4 +33,4 @@ If you’re accustomed to building with components, it might not always be clear I'm still exploring paths and alternatives but this would be my current recommendations: 1. For complex pages, ensuring the uniqueness of view transition names might be easier if you assign them dynamically using JavaScript. 2. Placing your view transition animation definitions in a global CSS file can be effective. This way, both the old and new pages share the same definitions, eliminating the need to worry about where to place them. -3. Utilizing view transition types and classes can help decouple usage from definitions, making your code more flexible." \ No newline at end of file +3. Utilizing [view transition classes and types](/basics/styling/#selecting-pseudo-elements-with-classes) can help decouple usage from definitions, making your code more flexible." diff --git a/src/content/docs/tips/over-exposure.mdx b/src/content/docs/tips/over-exposure.mdx index ee72cfb..45c0618 100644 --- a/src/content/docs/tips/over-exposure.mdx +++ b/src/content/docs/tips/over-exposure.mdx @@ -5,7 +5,6 @@ description: Why custom fade animations for view transitions might flash. Defining custom fade animations for view transitions is not as straightforward as it might initially seem. Typical issues, where the combined old an new image appears brighter or darker as expected during view transitions can be caused by a [wrong `mix-blend-mode`](#set-the-correct-mix-blend-mode) or by [reverting an asymmetric timing function](#avoid-reverse-with-asymmetric-timings). - You can create custom fade animations for view transitions similar to how the browser applies default animations when no custom animations are specified for the old or new image: ```css @@ -21,7 +20,6 @@ You can create custom fade animations for view transitions similar to how the br } ``` - ## Root Causes for Flashes However, this seemingly straightforward approach can result in flashes, sometimes similar to an overexposed effect or a sudden darkening of the animation. The image flickers during the transition, making the animation appear brighter than either the old or new image. With a dark background, the effect is just the opposite: halfway through the animation, the combined image appears darker than either the old or new image. @@ -30,16 +28,17 @@ This effect is most noticeable during a cross-fade involving the same image. \ **See it in action: click the image to trigger a view transition** using the definition above. - + ## PREREQUISITE @@ -48,16 +35,12 @@ If you want to use the Cam-Shaft with an Astro project, see [astro-vtbot](https: To use the Cam-Shaft, you can install the npm package in your project. Alternatively you can load the script from one of the global content delivery networks that provide npm packages. -import { Tabs, TabItem } from "@astrojs/starlight/components"; - - - - -1. Install `@vtbag/cam-shaft@latest` from npm. + +1. Install `@vtbag/cam-shaft from npm 2. In your project, add `@vtbag/cam-shaft/index.js` as an inline script at the beginning of the `` element on all pages of your site. -Details depend on your project setup and the frameworks used, but it can be as simple as: +Details depend on your project setup and the frameworks used, but with a bundler like `vite` it can be as simple as: ```jsx import shaft from "@vtbag/cam-shaft?url"; @@ -67,42 +50,11 @@ import shaft from "@vtbag/cam-shaft?url"; ... ... -; -``` - - - -You can load the script from CDNs such as jsdelivr.net or unpkg.com. Place the script inline at the beginning of the `` element on all pages where you need avoid pseudo-smooth-scrolling. - -Using `jsdeliver.net`: - -```html - - - +``` +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. + +For a detailed description of `data-selector` and on how to instruct Turn-Style to set different types or even set attributes on the root element, see the [config options](/config/options/) below. + +Let's see how you can use these view transition types to style different animations dependent on the direction of the navigation or even switch transition groups on and off. ### 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. -```css + +```css title="Example for forward animations" ::view-transition-old(main) { animation-name: slideOutToLeft; } @@ -154,7 +119,8 @@ Chances are more often then not that you can not simply revert the direction of } ``` 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 … -```css + +```css title="Example for fitting backward animations" :active-view-transition-type(backward) { &::view-transition-old(main) { animation-name: slideInFromRight; @@ -166,16 +132,38 @@ Just changing the direction of the animations would lead to something best named } } ``` -… or even explicitly define keyframes for slideOutToRight and slideInFromLeft if you feel that is clearer. +… 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 + +Transition types and direction attributes are determined 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](/tips/css/#animations), 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! + +```css title="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 Turn-Signal also always sets view transition type `old` on the old page and `new` on the new page. So you can be even more specific: + +```css title="How to switch off the old image, only" +:active-view-transition-type(same):active-view-transition-type(old) main { + view-transition-name: none; /* but only a new image for links to the same page */ +} +``` -… 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. ### Config Options |Config option|Type|Effect| |--|--|--| |data-selector|a 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 `` 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 `` elements inside a `