view transitions – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Thu, 25 Jul 2024 16:15:30 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 view transitions – CSS-Tricks https://css-tricks.com 32 32 45537868 CSS Stuff I’m Excited After the Last CSSWG Meeting https://css-tricks.com/css-stuff-im-excited-after-the-last-csswg-meeting/ https://css-tricks.com/css-stuff-im-excited-after-the-last-csswg-meeting/#comments Fri, 19 Jul 2024 17:16:57 +0000 https://css-tricks.com/?p=379180 From June 11–13, the CSS Working Group (CSSWG) held its second face-to-face meeting of the year in Coruña, Spain, with a long agenda of new features and improvements coming to language. If 2023 brought us incredible advances like …


CSS Stuff I’m Excited After the Last CSSWG Meeting originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
From , the CSS Working Group (CSSWG) held its second face-to-face meeting of the year in Coruña, Spain, with a long agenda of new features and improvements coming to language. If 2023 brought us incredible advances like out-of-the-box nesting, container and style queries, or the has: selector, then 2024 is going to be even more packed with even more ground-breaking additions. Whether a new feature like inline conditionals is just starting or long-term projects are wrapping up, 2024 is already filled with exciting developments — and we’re still in July!

I wanted to share what I think are some of the most interesting and significant features coming to CSS that were examined in the meeting. However, I don’t want you to take the following as an exact recap of the discussions. Instead, I want to bring up the broader topics coming to CSS that had a spotlight at the last meeting. In reality, the features examined have been cooking up for even years and the discussions are geared towards specific cases and new enhancements, rather than defining a whole specification; a work that would be impossible in one meeting.

You can see the exact issues discussed on the CSSWG meeting agenda.

Feature 1: What if we get if()?

Since CSS custom properties gained reliable support around 2016, there have been many attempts to apply certain styles depending on a custom property value without, of course, appealing to JavaScript. One of the earliest workarounds for conditional styles was posted by Roman Komarov back in 2016 in “Conditions for CSS Variables”. From there, many other hacks have been documented for making conditional declarations in CSS (including this extremely clever one by Ana Tudor here on CSS-Tricks). In fact, you can find a full list that discusses and compares those workarounds by CSSWG member Lea Verou in her recent article, “Inline conditionals in CSS, now?”.

What’s for sure is that the community has craved a conditional way to apply styles using custom properties. Nowadays, we have a specification for Style Queries that’s capable of the task, but they come with limitations not related to browser support. The biggest of those limitations? We can’t directly style the container that’s queried, so we need some sort of wrapper element around that wrapper in HTML.

<div class="news-container" style="--variant: info">
  <p>Here is some good <strong>news</strong></p>
</div>

…in addition to writing the style query:

.news-container {
  container-name: news-container;
}

@container news-container style(--variant: info) {
  p {
    color: blue;
    border: 1px solid blue;
  }
}

What if() might look like

On the CSSWG side, there have been discussions about adding an if() function as far back as 2018. It was of this year — yes, six years later — that the CSSWG resolved to begin working on if() for CSS. As good as it may look, don’t expect to see if() in a browser in at least two years! (That’s Lea’s unofficial estimate.) We’ll likely need to wait even longer for enough browser support to begin using it reliably in production. The spec draft is only barely getting started and many things have to pass a test first. For context, the CSS variables working draft began in 2012 and only received wide browser support in 2016.

Syntax-wise, if() is probably going to borrow the ternary operator from JavaScript and other programming languages, structured like this:

if(a ? b : c)

…where a is the custom property we are checking and b are c are the possible conditional return values. To check for styles, an inline style(--my-property: value) would be used.

.forecast {
  background-color: if(style(--weather: clouds) ? var(--clouds-color): var(--default-color));
}

Even if ? isn’t used in CSS and : has a different meaning everywhere else, I think this syntax is the one most people are familiar with, not to mention it also allows seamless conditional chaining.

.forecast {
  background-color: if(
    style(--weather: clouds) ? var(--clouds-color): 
    style(--weather: sunny) ? var(--sunny-color);
    style( --weather: rain) ? var(--rain-color): var(--default-color)
  );
}

Future if() improvements

Although these probably won’t make it in the initial release, it’s interesting to see how if() might change between now and sometime further in the future:

  • Support for other inline conditionals. We are supposed to check for custom properties using the style() query, but we may as well check for media features with an inline media() query or if a user agent supports a specific property with an inline support().
.my-element {
  width: if(media(width > 1200px) ? var(--size-l): var(--size-m));
}
  • Using conditional inside other CSS functions. In future drafts, we may use ternaries inside other functions without having to wrap them around if(), e.g. just as we can make calculations without calc() if we are inside a clamp() or round() function.

Feature 2: Cross-document view transitions

Last year, the View Transition API gave us the power to create seamless transitions when navigating between web pages and states. No components or frameworks, no animation libraries — just vanilla HTML and CSS with a light sprinkle of JavaScript. The first implementation of View Transitions was baked into browsers a while back, but it was based on an experimental function defined by Chrome and was limited to transitions between two states (single-page view transitions) without support for transitioning between different pages (i.e., multi-page view transitions), which is what most of us developers are clamoring for. The possibilities for mimicking the behavior of native apps are exciting!

That’s why the CSS View Transitions Module Level 2 is so amazing and why it’s my favorite of all the CSS additions we’re covering in this article. Yes, the feature brings out-of-the-box seamless transitions between pages, but the real deal is it removes the need for a framework to achieve it. Instead of using a library — say React + some routing library — we can backtrack into plain CSS and JavaScript.

Of course, there are levels of complexity where the View Transition API may fall short, but it’s great for countless cases where we just want page transitions without the performance cost of dropping in a framework.

Opting into view transitions

View transitions are triggered when we navigate between two pages from the same-origin. In this context, navigation might be clicking a link, submitting a form, or going back and forth with browser buttons. By contrast, something like using a search bar between same-origin pages won’t trigger a page transition.

Both pages — the one we’re navigating away from and the one we’re navigating to — need to opt into the transition using the @view-transition at-rule and setting the navigation property to auto

@view-transition {
  navigation: auto;
}

When both pages opt into a transition, the browser takes a “snapshot” of both pages and smoothly fades the “before” page into the “after” page.

Transitioning between “snapshots”

In that video, you can see how the old page fades into the new page, and it works thanks to an entire tree of new pseudo-elements that persist through the transition and use CSS animations to produce the effect. The browser will group snapshots of elements with a unique view-transition-name property that sets a unique identifier on the transition that we can reference, and which is captured in the ::view-transition pseudo-element holding all of the transitions on the page.

You can think of ::view-transition as the :root element for all page transitions, grouping all of the parts of a view transition on the same default animation.

::view-transition
├─ ::view-transition-group(name)
│  └─ ::view-transition-image-pair(name)
│     ├─ ::view-transition-old(name)
│     └─ ::view-transition-new(name)
├─ ::view-transition-group(name)
│  └─ ::view-transition-image-pair(name)
│     ├─ ::view-transition-old(name)
│     └─ ::view-transition-new(name)
└─ /* and so one... */

Notice that each transition lives in a ::view-transition-group that holds a ::view-transition-image-pair that, in turn, consists of the “old” and “new” page snapshots. We can have as many groups in there as we want, and they all contain an image pair with both snapshots.

Quick example: let’s use the ::view-transition “root” as a parameter to select all of the transitions on the page and create a sliding animation between the old and new snapshots.

@keyframes slide-from-right {
  from {
    transform: translateX(100vw);
  }
}

@keyframes slide-to-left {
  to {
    transform: translateX(-100vw);
  }
}

::view-transition-old(root) {
  animation: 300ms ease-in both slide-to-left;
}

::view-transition-new(root) {
  animation: 300ms ease-out both slide-from-right;
}

If we navigate between pages, the entire old page slides out to the left while the entire new page slides in from the right. But we may want to prevent some elements on the page from participating in the transition, where they persist between pages while everything else moves from the “old” snapshot to the “new” one.

That’s where the view-transition-name property is key because we can take snapshots of certain elements and put them in their own ::view-transition-group apart from everything else so that it is treated individually.

nav {
  view-transition-name: navigation;

  /* 
    ::view-transition
    ├─ ::view-transition-group(navigation)
    │  └─ ::view-transition-image-pair(navigation)
    │     ├─ ::view-transition-old(navigation)
    │     └─ ::view-transition-new(navigation)
    └─ other groups...
  */
}

You can find a live demo of it on GitHub. Just note that browser support is limited to Chromium browsers (i.e., Chrome, Edge, Opera) at the time I’m writing this.

There are many things we can look forward to with cross-document view transitions. For example, If we have several elements with a different view-transition-name, we could give them a shared view-transition-class to style their animations in one place — or even customize the view transitions further with JavaScript to check from which URL the page is transitioning and animate accordingly.

Feature 3: Anchor Positioning

Positioning an element relative to another element in CSS seems like one of those no-brainer, straightforward things, but in reality requires mingling with inset properties (top, bottom, left, right) based on a series of magic numbers to get things just right. For example, getting a little tooltip that pops in at the left of an element when hovered might look something like this in HTML:

<p class="text">
  Hover for a surprise
  <span class="tooltip">Surprise! I'm a tooltip</span>
</p>

…and in CSS with current approaches:

.text {
  position: relative;
}


.tooltip {
  position: absolute;
  display: none;

  /* vertical center */
  top: 50%;
  transform: translateY(-50%);

  /* move to the left */
  right: 100%;
  margin-right: 15px; */
}

.text:hover .tooltip {
  display: block;
}

Having to change the element’s positioning and inset values isn’t the end of the world, but it sure feels like there should be an easier way. Besides, the tooltip in that last example is extremely fragile; if the screen is too small or our element is too far to the left, then the tooltip will hide or overflow beyond the edge of the screen.

CSS Anchor Positioning is yet another new feature that was discussed in the CSSWG meetings and it promises to make this sort of thing much, much easier.

Creating an anchor

The basic idea is that we establish two elements:

  • one that acts as an anchor, and
  • one that is a “target” anchored to that element.

This way, we have a more declarative way to associate one element and position it relative to the anchored element.

To begin we need to create our anchor element using a new anchor-name property.

Changing our markup a little:

<p>
  <span class="anchor">Hover for a surprise</span>
  <span class="tooltip">Surprise! I'm a tooltip</span>
</p>

We give it a unique dashed-indent as its value (just like a custom property):

.anchor {
  anchor-name: --tooltip;
}

Then we relate the .tooltip to the .anchor using the position-anchor property with either fixed or absolute positioning.

.toolip {
  position: fixed;
  position-anchor: --tooltip;
}

The .tooltip is currently positioned on top of the .anchor, but we ought to move it somewhere else to prevent that. The easiest way to move the .tooltip is using a new inset-area property. Let’s imagine that the .anchor is placed in the middle of a 3×3 grid and we can position the tooltip inside the grid by assigning it a row and column.

Three by three grid with yellow element in the center tile labeled 'anchor'.

The inset-area property takes two values for the .tooltip‘s in a specific row and column on the grid. It counts with physical values, like left, right, top and bottom, as well logical values depending on the user’s writing mode, like start and end, in addition to a center shared value. It also accepts values referencing x- and y-coordinates, like x-start and y-end. All these value types are ways of representing a space on the 3×3 grid.

For example, if we want the .tooltip to be positioned relative to the top-right edge of the anchor, we can set the inset-area property like this:

.toolip {
  /* physical values */
  inset-area: top right;

  /* logical values */
  inset-area: start end;

  /* mix-n-match values! */
  inset-area: top end;
}

Lastly, if we want our tooltip to span across two regions of the grid, we can use a span- prefix. For example, span-top will place the .tooltip in the grid’s top and center regions. If instead we want to span across an entire direction, we can use the span-all value.

Three by three grid with a yellow element in the center labeled 'anchor' surrounded by three tooltip examples demonstrating how tooltips are placed on the grid, including code examples for each example.

One of the problems with our anchor-less example is that the tooltip can overflow outside the screen. We can solve this using another new property, this time called position-try-options, in combination with a new inset-area() function.

(Yes, there is inset-area the property and inset-area() the function. That’s one we’ll have to commit to memory!)

The position-try-options property accepts a comma-separated list of fallback positions for the .tooltip when it overflows outside the screen. We can provide a list of inset-area() functions, each holding the same values that the inset-area property would. Now, each time the tooltip goes out off-screen, the next declared position is “tried”, and if that position causes an overflow, the next declared position is tried, and so on.

.toolip {
  inset-area: top left;
  position-try-options: inset-area(top), inset-area(top right);
}

This is a pretty wild concept that will take some time to grok. CSSWG member Miriam Suzanne sat down to discuss and tinker with anchor positioning with James Stuckey Weber in a video that’s well worth watching.

Geoff Graham took notes on the video if you’re looking for a TL;DW.

There are still many aspects to anchor positioning we aren’t covering here for brevity, notably the new anchor() function and @try-position at-rule. The anchor() function returns the computed position of the edge of an anchor, which provides more control over a tooltip’s inset properties. The @try-position at-rule is for defining custom positions to set on the position-try-options property.

My hunch is that using inset-area will be plenty robust for the vast majority of use cases.

The CSSWG is a collective effort

Earlier I said that this article wouldn’t be an exact retelling of the discussions that took place at the CSSWG meetings, but rather a broad representation of new specs coming to CSS that, due to their novelty, were bound to come up in those meetings. There are even some features that we simply hadn’t the time to review in this roundup that are still subject to debate (cough, masonry).

One thing is for sure: specs aren’t made in some vacuum over one or two meetings; it takes the joined effort of tens of amazing authors, developers, and user agents to bring to life what we use every day in our CSS work — not to mention the things we will use in the future.

I also had the opportunity to talk with some amazing developers from the CSSWG, and I found it interesting what their biggest takeaways were from the meetings. You might expect if() is at the top of their lists since that’s what is buzzing in socials. But CSSWG member Emilio Cobos told me, for example, that the letter-spacing property is essentially flawed and there isn’t a simple solution for fixing it that’s copasetic with how letter-spacing is currently defined by CSS and used in browsers. That includes the fact that converting normal properties into shorthand properties can be dangerous to a codebase.

Every tiny detail we might think of as trivial is carefully analyzed for the sake of the web and for the love of it. And, like I mentioned earlier, this stuff is not happening in a closed vacuum. If you’re at all interested in the future of CSS — whether that simply keeping up with it or getting actively involved — then consider any of the following resources.


CSS Stuff I’m Excited After the Last CSSWG Meeting originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-stuff-im-excited-after-the-last-csswg-meeting/feed/ 4 https://www.youtube.com/embed/76hIB2L_vs4 CSS Anchor Positioning in Practice - Winging It Live nonadult 379180
::view-transition-image-pair https://css-tricks.com/almanac/selectors/v/view-transition-image-pair/ Fri, 14 Jun 2024 16:50:48 +0000 https://css-tricks.com/?page_id=378681 The CSS ::view-transition-image-pair pseudo-element is part of the View Transitions API that lets us select the “before-and-after” snapshots in a view transition.

::view-transition-image-pair(root) {
  animation-duration: 1s;
}

For context’s sake, ::view-transition-image-pair is one pseudo-element in a tree of pseudo-elements the …


::view-transition-image-pair originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The CSS ::view-transition-image-pair pseudo-element is part of the View Transitions API that lets us select the “before-and-after” snapshots in a view transition.

::view-transition-image-pair(root) {
  animation-duration: 1s;
}

For context’s sake, ::view-transition-image-pair is one pseudo-element in a tree of pseudo-elements the browser establishes when a view transition is registered on the page. The ::view-transition pseudo-element is the tree’s “root” and ::view-transition-image-pair is two levels deep, contained in the ::view-transition-group pseudo-element.

::view-transition
├─ ::view-transition-group(name-1)
│  └─ ::view-transition-image-pair(name-1)
│     ├─ ::view-transition-old(name-1)
│     └─ ::view-transition-new(name-1)
├─ ::view-transition-group(name-2)
│  └─ ::view-transition-image-pair(name-2)
│     ├─ ::view-transition-old(name-2)
│     └─ ::view-transition-new(name-2)
│ /* and so one... */

This makes ::view-transition-image-pair an ancestor of the root ::view-transition and a direct child of the ::view-transition-group. In turn, ::view-transition-image-pair contains two child pseudo-elements of its own:

These two pseudos represent the “old” state before the view transition begins and the “new” state it transitions to, respectively. So, ::view-transition-image-pair becomes a handy way to select and style both of those states together.

The specification calls this the pseudo “View Transition Image Pair Isolation” which means that it adds isolation to a view transition’s old and new states. In other words, ::view-transition-image-pair is set to isolation: isloate by default. This establishes a stacking context that allows the view transition’s old and new states to sit on top of everything else to prevent it from being obscured by other elements.

Syntax

::view-transition-image-pair(<pt-name-selector>) {
  /* Styles */
}

The pseudo-element accepts a <pt-name-selector> in its argument, which is equal to one of the following:

  • <custom-ident>: Use this to select a specific transition image pair in the ::view-transition pseudo tree. For example, if a particular element has a view-transition-name of gallery, then you would use ::view-transition-image-pair(gallery) to select that transition group.
  • root: This value matches html::view-transition-image-pair(*) which is a selector set up by the browser to match any view transition image pair that is not assigned to a specific view transition via the CSS view-transition-name property.
  • Universal selector (*): Use this to select all view transition image pairs on a page.

Default styles

The specification defines the default styles for browsers to set on ::view-transition-image-pair like this:

:root::view-transition-image-pair(*) {
  position: absolute;
  inset: 0;

  animation-duration: inherit;
  animation-fill-mode: inherit;
}

Notice that the default styles target all view transitions with the Universal Selector (*). We can override these by selecting a specific view-transition-name:

::view-transition-pair(gallery) {
  animation-duration: 500ms;
}

Specification

The CSS ::view-transition-image-pair pseudo-element is defined in the CSS View Transitions Module Level 1 specification. The specification is labeled a Candidate Recommendation Snapshot, meaning it’s been widely reviewed and intended to become an official W3C Recommendation, but it is still being tested in the wild.

The specification further states:

This document is intended to become a W3C Recommendation; it will remain a Candidate Recommendation at least until 5 December 2023 to gather additional feedback.

That date has passed as of this writing, so keep an eye on the document’s status as it becomes a recommended feature.

Browser support

Data on support for the view-transitions feature across the major browsers from caniuse.com


::view-transition-image-pair originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378681
::view-transition-image-new https://css-tricks.com/almanac/selectors/v/view-transition-image-new/ Fri, 14 Jun 2024 16:50:16 +0000 https://css-tricks.com/?page_id=378713 The CSS ::view-transition-image-new pseudo-element is part of the View Transitions API that lets us select the “new” snapshot that is transitioned to in a view transition.

::view-transition-image-new(*) {
  animation-duration: 700ms;
}

For context’s sake, ::view-transition-image-new is one pseudo-element in a …


::view-transition-image-new originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The CSS ::view-transition-image-new pseudo-element is part of the View Transitions API that lets us select the “new” snapshot that is transitioned to in a view transition.

::view-transition-image-new(*) {
  animation-duration: 700ms;
}

For context’s sake, ::view-transition-image-new is one pseudo-element in a tree of pseudo-elements the browser establishes when a view transition is registered on the page. The ::view-transition pseudo-element is the tree’s “root” and ::view-transition-image-pair is two levels deep, contained in the ::view-transition-group pseudo-element.

And inside the ::view-transition-image-pair is where we’ll find ::view-transition-image-new:

::view-transition
├─ ::view-transition-group(name-1)
│  └─ ::view-transition-image-pair(name-1)
│     ├─ ::view-transition-old(name-1)
│     └─ ::view-transition-new(name-1)
├─ ::view-transition-group(name-2)
│  └─ ::view-transition-image-pair(name-2)
│     ├─ ::view-transition-old(name-2)
│     └─ ::view-transition-new(name-2)
│ /* and so one... */

This makes ::view-transition-image-new a direct child of the ::view-transition-image-pair. The image pair also contains the view traditions “old” snapshot, ::view-transition-image-old.

These two pseudos represent the “old” state before the view transition begins and the “new” state it transitions to, respectively. So, ::view-transition-image-pair becomes a handy way to select and style both of those states together.

The specification calls this the pseudo “View Transition New State Image” which is pretty on the nose considering that it is indeed an image of the view transition that the browser captures before the transition runs and then transitions to the new state.

Syntax

::view-transition-image-new(<pt-name-selector>) {
  /* Styles */
}

The pseudo-element accepts a <pt-name-selector> in its argument, which is equal to one of the following:

  • <custom-ident>: Use this to select a specific transition “new” image in the ::view-transition pseudo tree. For example, if a particular element has a view-transition-name of gallery, then you would use ::view-transition-image-new(gallery) to select that transition group.
  • root: This value matches :root::view-transition-new(*) which is a selector set up by the browser to match any view transition “new” image that is not assigned to a specific view transition via the CSS view-transition-name property.
  • Universal selector (*): Use this to select all “new” states of a view transition on a page.

Default styles

The specification defines the default styles for browsers to set on ::view-transition-image-new like this:

:root::view-transition-old(*),
:root::view-transition-new(*) {
  position: absolute;
  inset-block-start: 0;
  inline-size: 100%;
  block-size: auto;

  animation-duration: inherit;
  animation-fill-mode: inherit;
  animation-delay: inherit;
}

Notice that the default styles target all view transitions with the Universal Selector (*). We can override these by selecting a specific view-transition-name:

::view-transition-new(gallery) {
  animation-duration: 500ms;
}

Specification

The CSS ::view-transition-image-new pseudo-element is defined in the CSS View Transitions Module Level 1 specification. The specification is labeled a Candidate Recommendation Snapshot, meaning it’s been widely reviewed and intended to become an official W3C Recommendation, but it is still being tested in the wild.

The specification further states:

This document is intended to become a W3C Recommendation; it will remain a Candidate Recommendation at least until 5 December 2023 to gather additional feedback.

That date has passed as of this writing, so keep an eye on the document’s status as it becomes a recommended feature.

Browser support

Data on support for the view-transitions feature across the major browsers from caniuse.com


::view-transition-image-new originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378713
::view-transition-image-old https://css-tricks.com/almanac/selectors/v/view-transition-image-old/ Fri, 14 Jun 2024 16:49:48 +0000 https://css-tricks.com/?page_id=378707 The CSS ::view-transition-image-old pseudo-element is part of the View Transitions API that lets us select the “before” snapshot in a view transition.

::view-transition-image-old(*) {
  animation-duration: 700ms;
}

For context’s sake, ::view-transition-image-old is one pseudo-element in a tree of pseudo-elements the …


::view-transition-image-old originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The CSS ::view-transition-image-old pseudo-element is part of the View Transitions API that lets us select the “before” snapshot in a view transition.

::view-transition-image-old(*) {
  animation-duration: 700ms;
}

For context’s sake, ::view-transition-image-old is one pseudo-element in a tree of pseudo-elements the browser establishes when a view transition is registered on the page. The ::view-transition pseudo-element is the tree’s “root” and ::view-transition-image-pair is two levels deep, contained in the ::view-transition-group pseudo-element.

And inside the ::view-transition-image-pair is where we’ll find ::view-transition-image-old:

::view-transition
├─ ::view-transition-group(name-1)
│  └─ ::view-transition-image-pair(name-1)
│     ├─ ::view-transition-old(name-1)
│     └─ ::view-transition-new(name-1)
├─ ::view-transition-group(name-2)
│  └─ ::view-transition-image-pair(name-2)
│     ├─ ::view-transition-old(name-2)
│     └─ ::view-transition-new(name-2)
│ /* and so one... */

This makes ::view-transition-image-old a direct child of the ::view-transition-image-pair. The image pair also contains the view traditions “new” snapshot, ::view-transition-image-new.

These two pseudos represent the “old” state before the view transition begins and the “new” state it transitions to, respectively. So, ::view-transition-image-pair becomes a handy way to select and style both of those states together.

The specification calls this the pseudo “View Transition Old State Image” which is pretty on the nose considering that it is indeed an image of the view transition that the browser captures before the transition runs and then transitions to the new state.

Syntax

::view-transition-image-old(<pt-name-selector>) {
  /* Styles */
}

The pseudo-element accepts a <pt-name-selector> in its argument, which is equal to one of the following:

  • <custom-ident>: Use this to select a specific transition “old” image in the ::view-transition pseudo tree. For example, if a particular element has a view-transition-name of gallery, then you would use ::view-transition-image-old(gallery) to select that transition group.
  • root: This value matches :root::view-transition-old(*) which is a selector set up by the browser to match any view transition “old” image that is not assigned to a specific view transition via the CSS view-transition-name property.
  • Universal selector (*): Use this to select all “old” states of a view transition on a page.

Default styles

The specification defines the default styles for browsers to set on ::view-transition-image-old like this:

:root::view-transition-old(*),
:root::view-transition-new(*) {
  position: absolute;
  inset-block-start: 0;
  inline-size: 100%;
  block-size: auto;

  animation-duration: inherit;
  animation-fill-mode: inherit;
  animation-delay: inherit;
}

Notice that the default styles target all view transitions with the Universal Selector (*). We can override these by selecting a specific view-transition-name:

::view-transition-old(gallery) {
  animation-duration: 500ms;
}

Specification

The CSS ::view-transition-image-old pseudo-element is defined in the CSS View Transitions Module Level 1 specification. The specification is labeled a Candidate Recommendation Snapshot, meaning it’s been widely reviewed and intended to become an official W3C Recommendation, but it is still being tested in the wild.

The specification further states:

This document is intended to become a W3C Recommendation; it will remain a Candidate Recommendation at least until 5 December 2023 to gather additional feedback.

That date has passed as of this writing, so keep an eye on the document’s status as it becomes a recommended feature.

Browser support

Data on support for the view-transitions feature across the major browsers from caniuse.com


::view-transition-image-old originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378707
::view-transition-group https://css-tricks.com/almanac/selectors/v/view-transition-group/ Wed, 12 Jun 2024 12:58:14 +0000 https://css-tricks.com/?page_id=378612 The ::view-transition-group pseudo-element selects one or more containers that group the pieces of an individual view transition.

::view-transition-group(transition-name) {
  animation-timing-function: ease-in-out;
}

See that transition-name identifier in the pseudo-element’s argument? That’s how we can select a particular view transition. View …


::view-transition-group originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The ::view-transition-group pseudo-element selects one or more containers that group the pieces of an individual view transition.

::view-transition-group(transition-name) {
  animation-timing-function: ease-in-out;
}

See that transition-name identifier in the pseudo-element’s argument? That’s how we can select a particular view transition. View transitions are grouped together in a single ::view-transition pseudo-element, where each transition is contained in a ::view-transition-group:

::view-transition
├─ ::view-transition-group(name-1)
├─ ::view-transition-group(name-2)
│ /* and so one... */

And inside each ::view-transition-group is another pseudo-element that holds the “before-and-after” snapshots that the view transitions between.

::view-transition
├─ ::view-transition-group(name-1)
│  └─ ::view-transition-image-pair(name-1)
│     ├─ ::view-transition-old(name-1)
│     └─ ::view-transition-new(name-1)
├─ ::view-transition-group(name-2)
│  └─ ::view-transition-image-pair(name-2)
│     ├─ ::view-transition-old(name-2)
│     └─ ::view-transition-new(name-2)
│ /* and so one... */

That essentially makes ::view-transition-group the “root” element of an individual view transition. It’s a wrapper for selecting the ::view-transition-image-pair, typically for positioning that particular view transition or defining its animation properties. In fact, the specification calls it the “View Transition Named Subtree Root.”

Syntax

::view-transition-group(<pt-name-selector>) {
  /* Styles */
}

The pseudo-element accepts a <pt-name-selector> in its argument, which is equal to one of the following:

  • <custom-ident>: Use this to select a specific transition group in the ::view-transition pseudo tree. For example, if a particular element has a view-transition-name of gallery, then you would use ::view-transition-group(gallery) to select that transition group.
  • root: This value matches html::view-transition-group(*) which is a selector set up by the browser (see “Default styling” below) to match any view transition that is not assigned to a specific view transition group via the CSS view-transition-name property.
  • Universal selector (*): Use this to select all view transition groups on a page.

Default styling

Like many semantic HTML elements and pseudo-elements, browsers apply a set of default styles to ::view-transition-group.

html::view-transition-group(*) {
  position: absolute;
  top: 0;
  left: 0;

  animation-duration: 0.25s;
  animation-fill-mode: both;
}

So, if we want to position and configure the transition’s animation behavior, these are the styles we have to override in our own stylesheet.

Accessibility

It’s always a good idea to consider users with motion sensitivities any time we’re working with animations and transitions. We can use ::view-transition-group to reduce the amount of motion using the prefers-reduced-motion media query.

According to the default styles we learned about above, the browser applies an animation-duration of 0.25s. We can override that by selecting all of the view transition groups on the page by passing the universal selector in the pseudo-element’s argument and either removing or reducing that duration to accommodate motion sensitivities.

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*): {
    animation-duration: 0;
  }
}

Specification

The ::view-transition-group pseudo-element is defined in the View Transitions Module Level 1 specification. It’s labeled a Candidate Recommendation Snapshot, meaning it’s been widely reviewed and intended to become an official W3C Recommendation, but it is still being tested in the wild.

The specification further states:

This document is intended to become a W3C Recommendation; it will remain a Candidate Recommendation at least until 5 December 2023 to gather additional feedback.

That date has passed as of this writing, so keep an eye on the document’s status as it becomes a recommended feature.

Browser support

Data on support for the view-transitions feature across the major browsers from caniuse.com

If you need to support older browsers that do not support ::view-transition, try using @supports to check whether or not the browser recognizes the view-transition-name property:

/* Apply these styles only if View Transitions are *NOT* supported */
@supports not (view-transition-name: none) {
  /* Fallback Styles */
}

More information


::view-transition-group originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378612
::view-transition https://css-tricks.com/almanac/selectors/v/view-transition/ Fri, 07 Jun 2024 17:53:32 +0000 https://css-tricks.com/?page_id=378574 The CSS ::view-transition pseudo-element is the top-level — or “root” — containing all animated transitions defined as CSS view transitions on the page.

::view-transition {
  position: fixed;
  inset: 0;
}

Syntax

::view-transition {
  /* Styles */
}

It’s worth noting …


::view-transition originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The CSS ::view-transition pseudo-element is the top-level — or “root” — containing all animated transitions defined as CSS view transitions on the page.

::view-transition {
  position: fixed;
  inset: 0;
}

Syntax

::view-transition {
  /* Styles */
}

It’s worth noting that ::view-transition holds all view transitions on the page (we’ll get to that in a bit), so you’re likely to see it used most often used unchained to a selector like the example above rather than, say, this:

/* 👎 */
blockquote::view-transition {
  /* Styles */
}

Left as-is, ::view-transition is scoped to the <html> element:

html::view-transition {
  /* Styles */
}

Default styles

Like many semantic HTML elements and pseudo-elements, browsers apply a set of default styles to ::view-transition.

::view-transition {
  position: fixed;
  inset: 0;
}

So, if we want to position ::view-transition ourselves, these are the styles we have to override in our own stylesheet.

It’s a “tree-abiding” pseudo-element

In other words, it’s effectively a wrapper that serves as the parent pseudo-element for others below it. The spec calls this a “tree-abiding pseudo-element” which means it contains other pseudos that can be used to style view transitions directly in CSS.

In this case, ::view-transition contains all of the view transitions on the page, organized in groups.

::view-transition
├─ ::view-transition-group(name)
│  └─ ::view-transition-image-pair(name)
│     ├─ ::view-transition-old(name)
│     └─ ::view-transition-new(name)
├─ ::view-transition-group(name)
│  └─ ::view-transition-image-pair(name)
│     ├─ ::view-transition-old(name)
│     └─ ::view-transition-new(name)
│ /* and so one... */

This way, we can define styles that apply to all view transitions together with ::view-transition and individually with ::view-transition-group.

How it fits into the view transitions process

It’s a good idea to be familiar with the steps that a view transition takes for context on where this ::view-transition tree fits in with everything.

It’s right there in Step 4 that we’re talking about.

It introduces a new stacking layer!

You know how we can use the CSS z-index property to layer elements one on top of another, like in a three-dimensional space?

We call this a “stacking context” and it happens when we declare a position on an element. In this case, both .child elements are in a stacking context where we’re using z-index to determine which element is above the other in the stack. CSS uses this information to determine in what order it applies styles:

  1. Elements with a negative stack level, typically elements with z-index: -1
  2. Elements with a position value of static.
  3. Elements with a stack level of 0, typically positioned elements with a z-index value of auto.
  4. Elements with positive stack levels, e.g. a positioned element with a z-index value of 1 or higher.

That third layer is called the “top layer” since it’s right smack dab in between layers positive and negative layers. This is where the <html> and <body> sit.

Well, it turns out that ::view-transition introduces a new stacking layer of its own, that is applied after everything else, i.e., it comes after the layer with the highest number:

  1. Elements with a negative stack level, typically elements with z-index: -1
  2. Elements with a position value of static.
  3. Elements with a stack level of 0, typically positioned elements with a z-index value of auto.
  4. Elements with positive stack levels, e.g. a positioned element with a z-index value of 1 or higher.
  5. View transitions

Here’s exactly how the specification describes it:

The ::view-transition pseudo-element generates a new stacking context, called the view transition layer, which paints after all other content of the document (including any content rendered in the top layer), after any filters and effects that are applied to such content. (It is not subject to such filters or effects, except insofar as they affect the rendered contents of the ::view-transition-old() and ::view-transition-new() pseudo-elements.)

CSS View Transitions Module Level 1, 4.2. View Transition Painting Order

What’s the big deal, you ask? This means that a view transition’s styles are painted after everything else, ensuring that they are never buried beneath other layers. This way, we never have to juggle between view transition styles and the styles of the elements on a page with z-index to make sure they’re visible. But this only takes effect if (1) there are view transitions on the page and (2) when the view transition is running. Once the transition ends, its stacking context is gone.

Specification

The CSS ::view-transition pseudo-element is defined in the View Transitions Module Level 1 specification. It’s labeled a Candidate Recommendation Snapshot which means it’s been widely reviewed and is intended to become an official W3C Recommendation, but is still being tested in the wild.

The specification further states:

This document is intended to become a W3C Recommendation; it will remain a Candidate Recommendation at least until 5 December 2023 to gather additional feedback.

That date has passed as of this writing, so keep an eye on the document’s status as it makes its way toward becoming a recommended feature.

Browser support

Data on support for the view-transitions feature across the major browsers from caniuse.com

If you need to support older browsers that do not support ::view-transition, try using @supports to check whether or not the browser recognizes the view-transition-name property:

/* Apply these styles only if View Transitions are *NOT* supported */
@supports not (view-transition-name: none) {
  /* Fallback Styles */
}

More information


::view-transition originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378574
view-timeline-name https://css-tricks.com/almanac/properties/v/view-timeline-name/ Thu, 30 May 2024 21:21:07 +0000 https://css-tricks.com/?page_id=378389 The CSS view-timeline-name property allows us to identify and reference an element we want to animate based on its scroll position when it enters the visible area (i.e., scrollport) of a scroll container (also called the “source”).

.element {
  view-timeline-name: 


view-timeline-name originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The CSS view-timeline-name property allows us to identify and reference an element we want to animate based on its scroll position when it enters the visible area (i.e., scrollport) of a scroll container (also called the “source”).

.element {
  view-timeline-name: --image-zoom;
}

It’s a declarative way to trigger a scroll animation; otherwise, the element (also called the “subject”) will begin its scroll-based animation when it enters the scrollport of its nearest ancestor that has one.

The view-timeline-name property can also be declared using the view-timeline property, which is a shorthand that combines view-timeline-name and view-timeline-axis in a single declaration.

.element {
  view-timeline: --image-zoom y;

  /* Equivalent to: */
  view-timeline-name: --image-zoom;
  view-timeline-axis: y;
}

Syntax

view-timeline-name: [ none | <dashed-ident> ]#
  • Initial value: none
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: none or a list of CSS identifiers
  • Canonical order: By grammar
  • Animation: Not animatable

Property Values & Descriptions

/* Keyword value: none */
view-timeline-name: none;

/* <dashed-ident> examples */
view-timeline-name: --my-timeline;
view-timeline-name: --myTimeline;
view-timeline-name: --my_timeline;
  • none: The timeline does not have a name.
  • <dashed-ident>: A custom name given to a view progress timeline, like a <custom-ident>, that declaratively identifies it and distinguishes it from other timelines. The value is called a “dashed” ident because it must be prefixed with two dashes (--) exactly like we do for CSS custom properties, e.g. --image-zoom.

Scroll progress timelines vs. View progress timelines

The difference may seem small, but a scroll progress timeline is an animation we want to run on an element visible in the current scrollport, i.e. its scroll position whether scrolling vertically (the y-axis) or horizontally (the x-axis). When the element reaches a scroll position, the animation triggers and completes at the end of the scroll container. In other words, it’s an animation timeline that progresses from 0% to 100% as the user scrolls, whereas a typical animation timeline often happens when the page has been rendered.

That’s different from a view progress timeline which is a segment or part of a scroll progress timeline. In other words, a view progress timeline is sort of like the starting ignition of a scroll progress timeline, triggering the scroll progress timeline once the element (i.e., the “subject”) is currently in view.

Illustrating the pieces of an animation timeline, scroll progress timeline, and view progress timeline.

So, what we’re talking about is a scroll progress timeline that’s scoped to the element’s visibility in the current view. Once the element’s starting edge intersects with its nearest ancestor’s scrollport — which is a fancy way of saying “the visible part of a scroll container” — the animation is handed off to the scroll progress timeline and progresses by the user’s scrolling along whichever axis — x or y — is set.

Illustration of a scroll container on a webpage, including the visible scroll port and the element waiting to animate on scroll when entering the scroll port.

The <body> element can be considered a scroll container as long as it overflows what is currently in view. That means we can trigger or start an animation when the element we want to animate enters the visible area of the <body> after which point, the animation progresses based on the element’s scroll position.

A little terminology

It’s worth noting that there are some specific words that the specification uses to describe the various pieces of a scroll progress timeline.

  • Subject: The element we want to animate.
  • Source: The subject’s nearest ancestor (such as a parent or grandparent) with a scroll container that triggers and contains the animation up until the subject’s starting edge intersects with the source’s ending edge.

It’s cool that we have this sort of context, so we’ll continue using these terms along the way.

It’s a declarative way to define a view progress timeline

We don’t have to give a name to a view progress timeline. If we don’t it’s called an anonymous timeline and the subject we want to animate enters the progress timeline when it intersects with its nearest source. In other words, the animation is triggered when the element enters the visible part of the first scroll container it encounters.

Conversely, we can explicitly give the view progress timeline an identifying name with the view-timeline-name property to establish a declarative timeline that can be called and referenced by name. It’s what we reach for when we want the subject’s animation to kick off when it enters a super-specific source’s visible area.

Anonymous view progress timelines with view()

Even if we decide not to name a view progress timeline explicitly with scroll-timeline-name, it’s still possible to configure the view timeline behavior with the view() function. The function doesn’t take a scroll-timeline-name parameter, but it does instruct the browser what a subject should do when it intersects with its source.

.subject {
  animation-timeline: view(y 10dvh);
}

So, even without an explicit view-transition-name set on the subject element, we can still direct which view-timeline-axis the animation runs on and fudging exactly when it starts it starts with a view-timeline-inset parameter.

Specification

The view-timeline-name property is defined in the Scroll-driven Animations Module Level 1 specification, which is currently in Editor’s Draft status at the time of this writing. That means the work is under discussion and could change between now and when the proposal becomes a formal Candidate Recommendation.

Browser support

IEChromeEdgeFirefoxSafariOpera
No115115111No101
iOS SafariAndroid ChromeAndroid FirefoxAndroid BrowserOpera Mobile
No125No12580

Source: caniuse (retrieved on May 30, 2024)


view-timeline-name originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378389
view-transition-name https://css-tricks.com/almanac/properties/v/view-transition-name/ Wed, 29 May 2024 21:46:06 +0000 https://css-tricks.com/?page_id=378365 The CSS view-transition-name property uniquely identifies an element that can be used in a view transition.

card {
  view-transition-name: card-transition;
}

We give the element a name (i.e., <custom-ident) that we reference or set the property to none to …


view-transition-name originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The CSS view-transition-name property uniquely identifies an element that can be used in a view transition.

card {
  view-transition-name: card-transition;
}

We give the element a name (i.e., <custom-ident>) that we reference or set the property to none to exclude the element from view transitions.

The specification is not a standard just yet but describes view-transition-name as a way of “tagging individually transitioning subtrees” which is a super fancy way of saying we want a certain element to be captured in a view transition. When we give an element a view-transition-name, we’re effectively giving it access to a set of pseudo-elements for creating a view transition.

Syntax

view-transition-name: none | <custom-ident>;
  • Initial value: none
  • Applies to: All elements
  • Inherited: No
  • Percentages: N/A
  • Computed value: As specified
  • Canonical order: By grammar
  • Animation type: discreet

Property Values & Descriptions

/* <custom-ident> */
view-transition-name: banner;
view-transition-name: banner-transition;
view-transition-name: bannerTransition;

/* Keyword */
view-transition-name: none;
  • <custom-ident>: A unique identifier used to define an element that can participate in a view transition, whether it is the “old” element or the “new” one that is transitioned to. We can’t use the words none or auto for the value, as they are reserved global keyword values.
  • none: Removes the element from taking part in a view transition.

What the heck is a “captured element”?

It’s worth knowing because that’s exactly how the specification describes an element with a view-transition-name declared on it. The basic idea of view transitions is that we “capture” an existing state as an image and transition between that snapshot and a new element.

When we “capture” an element, we’re sort of tracking it during the lifetime of the view transition, during which the element takes multiple forms. Here’s the process that a captured element follows, as provided by the spec, which goes further by calling a captured element a “struct” or a set of items:

  • old image: A 2D bitmap or null. Initially null.
  • old width / old height: an unrestricted double, initially zero.
  • old transform: a <transform-function>, initially the identity transform function.
  • old writing-mode: Null or a writing-mode, initially null.
  • old direction: Null or a direction, initially null.
  • old text-orientation: Null or a text-orientation, initially null.
  • old mix-blend-mode: Null or a mix-blend-mode, initially null.
  • old backdrop-filter: Null or a backdrop-filter, initially null.
  • old color-scheme: Null or a color-scheme, initially null.
  • new element: An element or null. Initially null.

Those are all the pieces of a captured element used in a view transition. We capture a set of information about an element’s current (or “old”) state and transition it into a new element.

The name has to be unique

We can name a view transition anything we want (none and auto being the only exceptions) but it has to be unique in the sense that it is only applied to one rendered element on the current page.

The key word there is “rendered” because we can technically repurpose the same view-transition-name on multiple elements. For example, you might want the same transition on a set of images but wouldn’t want to go through the work of naming each and every one. We can instead declare view-transition-name on a certain class:

.image-transition {
  view-transition-name: image-zoom;
}

Then we can set up JavaScript that that sets a variable for each image in the set, adds the .image-transition class to the images, then triggers the transition with some action — like a click handler — on the image. From there, the script waits for the transition to wrap up, then removes the .image-transition class so it can be applied to another clicked image.

// Create a variable for img elements in a .gallery
const image = document.querySelectorAll(".gallery img");

// When an image is clicked...
image.onclick = () => {
  // If view transitions are *not* supported
  if (!document.startViewTransition) {
    // Run a fallback function
    doSomething(image);
  // If view transitions are supported
  } else {
    // Add the .image-transition class to the clicked image
    image.classList.add("image-transition");

    // Start the transition and save its instance in a variable
    const transition = document.startViewTransition(() => doSomething(image));

    // Wait for the transition to finish.
    await transition.finished;

    // Remove the class when finished
    image.classList.remove("image-transition");
  }
};

Another approach is to set view-transition-name to a <custom-ident> on the clicked image, wait for the transition to run, then swap out the <custom-ident> with the none value instead.

// Create a variable for img elements in a .gallery
const image = document.querySelectorAll(".gallery img");

// When an image is clicked...
image.onclick = () => {
  // First, check if view transitions are supported
  if (!document.startViewTransition) {
    // Fallback if View Transitions API is not supported.
    doSomething(image);
  } else {
    // Set `view-transition-name` with a <custom-ident>
    image.style.viewTransitionName = "image-zoom";

    // Start the transition and save its instance in a variable
    const transition = document.startViewTransition(() => doSomething(image));

    // Wait for the transition to finish.
    await transition.finished;

    // Set the transition name to `none` when finished
    image.style.viewTransitionName = "none";
  }
};

Specification

The view-transition-name property is just one of several view-* properties that are used together to register and manage view transitions that are part of the View Transitions Module Level 1 specification, which is currently a Candidate Recommendation Snapshot meaning it can be used where available, but is in the process of collecting feedback before formal recommendation. In other words, view-transition-name and other related properties are experimental and not part of the standards just yet.

Browser support

It’s only Chrome and other Chromium-powered browsers at the moment. Keep an eye on the Caniuse pages for the most current support information for the view-transition-name property and support for the none value.

Data on support for the view-transitions feature across the major browsers from caniuse.com

As far as browser support goes, we can use the view-transition-name property to check whether or not the user’s browser supports the View Transitions API and provide a fallback for browsers that lack support. We could use a <custom-ident> value to check support, but the none value is probably a better bet since it is a consistent keyword and we definitely want that value supported if we’re working with view trasnsitions anyway.

/* If the View Transitions API is supported */
@supports (view-transition-name: none) {

}

/* If the View Transitions API is *not* supported */
@supports not (view-transition-name: none) {
  /* Use a simple CSS animation if possible */
}

Similarly, we can check for support via JavaScript as well:

// If the View Transitions API is *not* supported.
if (!document.startViewTransition) {
  doSomething());

// Otherwise, start the view transition 
} else {
  document.startViewTransition(() => doSomething());
}

view-transition-name originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
378365
Basic Multi-Page View Transition https://css-tricks.com/snippets/css/basic-view-transition/ Mon, 15 Jan 2024 19:20:08 +0000 https://css-tricks.com/?page_id=378042 @view-transition { navigation: auto; }

That goes right in your stylesheet and it’s the most basic of basic view transitions. The effect is a cross-fade between two pages.

The Chrome team had initially released a meta tag we could …


Basic Multi-Page View Transition originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
@view-transition { navigation: auto; }

That goes right in your stylesheet and it’s the most basic of basic view transitions. The effect is a cross-fade between two pages.

The Chrome team had initially released a meta tag we could plop into the <head> to opt into view transitions:

<meta name="view-transition" content="same-origin" />

That was a temporary way to access view transitions, but then the Chrome team shipped an update in May 2024 that enables multi-page view transitions by default and only requires us to opt into them with the CSS snippet above.


Basic Multi-Page View Transition originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/wp-content/uploads/2024/05/simple-view-transition.mp4 View Transitions Archives - CSS-Tricks nonadult 378042