Skip to content

CSS view transition auto name generation #1001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
noamr opened this issue Oct 3, 2024 · 11 comments
Closed
1 task done

CSS view transition auto name generation #1001

noamr opened this issue Oct 3, 2024 · 11 comments
Assignees
Labels
Agenda+ chromium-high-priority Something that the Chromium team would like the TAG to prioritise Resolution: unsatisfied The TAG does not feel the design meets required quality standards Topic: CSS Venue: CSS WG

Comments

@noamr
Copy link

noamr commented Oct 3, 2024

こんにちは TAG-さん!

I'm requesting a TAG review of view transition auto name generation.

Generating names for view-transition based on element identity, to reduce burden of inventing unique names.

You should also know that... this has been discussed thoroughly in the CSSWG.

@martinthomson
Copy link
Contributor

Hi @noamr, I know this is a small thing, but the explainer really needs examples.

@martinthomson
Copy link
Contributor

Discussed in a breakout. This seems fine. There is a web-compat risk "self" and "auto" were not previous proscribed, but now have new meaning.

@jakearchibald
Copy link

jakearchibald commented Mar 25, 2025

I'd like to raise a couple of concerns about this that TAG may not have considered.

To reiterate, the feature is view-transition-name: auto, which generates a name for the element like this:

  1. If the element has an id attribute, then that value is used to generate a view-transition-name that's consistent for that id.
  2. Otherwise, a view-transition-name is generated for that element instance (the same behaviour as view-transition-name: match-element).

A close equivalent is:

.transitioning-list-items {
  view-transition-name: match-element;
  &:has([id]) {
    view-transition-name: attr(id type(<custom-ident>));
  }
}

The problems

  • Typically, adding a unique id to an element is safe, as in it doesn't break other features. This proposal changes that, which is a pretty big shift for the platform.
  • view-transition-name: auto will result in unexpectedly different/broken transitions in cross-document cases, by design.

Hiding these foot-guns behind a friendly name like auto makes things worse. It also means the name auto is taken by a bad feature, when it would be better to reserve it for a better-designed feature.

SPA vs MPA transitions

Almost all view transition CSS features work the same for both cross-document and same-document view transitions.

One exception is view-transition: match-element, which doesn't work cross-document, because there's no element equality across documents. I think this is ok, as the name hints at that behaviour, and it can be documented as same-document only. Ideally, browsers should provide a console warning if it's attempted to be used cross-document.

view-transition-name: auto sits in an unreliable middle ground - the ID matching 'works' across documents, whereas the element matching doesn't. This will result in transitions that are unexpectedly different/broken in cross-document cases.

This will be especially troublesome in sites that perform same-document navigations, except in cases where they know there's an update available, in which case they do a cross-document navigation to pick up the changes. This is a common pattern in larger SPAs. This will likely result in half-broken transitions that users will experience sometimes. This will be hard for developers to debug. This isn't the only case where this will be a problem, but it's one real-world example.

The only way to avoid this is to give every transitioning element an id attribute. But, if you can do that, you could already give it a view-transition-name as an inline style. If you wanted to use an attribute, attr() is right there. So, what's the point?

Adding IDs becomes risky

This feature overloads the id attribute, and means adding an id to an element can break transitions, even if that id is not used anywhere else in the code base. Previous to this, it was safe to add a unique id to an element (except in cases where the CSS had something odd like body:has(li[id]) { display: none }).

This feature assumes that id is only used to mean "intended element equality", whereas in reality ids can change and move to support features like labels, commands, popover targets, reference targets, and even just landmark links.

Here's there detail. I'm using React for brevity, but this issue also exists in plain DOM usage.

This produces a basic list of items, with a button that randomises the order of the list via a transition. This is one of the main use-cases given for this feature.

export const ListOfStuff = () => {
  const [items, setItems] = useState([
    { imgSrc: '…', alt: '…' },
    // …
  ]);

  const onButtonClick = () => {
    document.startViewTransition(() => {
      flushSync(() => {
        const newItems = items.slice();
        randomSortArray(newItems);
        setItems(newItems);
      });
    });
  };

  return (
    <div>
      <ul class="list-of-stuff">
        {items.map(({ imgSrc, alt }) => (
          <li key={imgSrc}>
            <img src={imgSrc} alt={alt} />
          </li>
        ))}
      </ul>
      <button onClick={onButtonClick}>Randomise!</button>
    </div>
  );
};

And let's make this all work with this new enticing feature:

.list-of-stuff > li {
  view-transition-name: auto;
}

It works! Each item moves from its old position to its new position.

But then, sometime later, another developer on the project wants to be able to link folks to the first item of the list, or use one of the many other features that link things to their target via ids. That's a small and easy change!

export const ListOfStuff = () => {
  // …as before…

  return (
    <div>
      <ul class="list-of-stuff">
        {items.map(({ imgSrc, alt }, i) => (
-          <li key={imgSrc}>
+          <li key={imgSrc} id={i === 0 ? 'first-stuff-item' : undefined}>
            <img src={imgSrc} alt={alt} />
          </li>
        ))}
      </ul>
      <button onClick={onButtonClick}>Randomise!</button>
    </div>
  );
};

The developer is smart and careful, so they check that first-stuff-item isn't used elsewhere on the page, or even in the codebase, which it isn't, so they assume things are fine.

But, they just broke the transition. It isn't totally broken, parts of it still kinda work, but it doesn't look right. Specifically, three of the items no longer move, they just fade.

The problem is that, with view-transition-name: auto, adding an id has a behavioural side effect without that id being directly referenced. It now tells the view transition that the first item in the list is the same either side of the transition. It overrides element equality.

Developers will now have to be very careful when adding ids to elements, as it may break transitions on the page.

If the developer used view-transition-name: match-element, they wouldn't hit this issue, as match-element doesn't pay attention to id.


So, view-transition-name: auto is basically the same as match-element but with added foot-guns. If you want to take the view-transition-name from an attribute, CSS already lets you do that via attr(). In fact, it means you can define a particular data- attribute for this, rather than overloading id.

Given this, why do we want this on the platform?

I see why this passed initial review, because it works ok in little codepen demos with a single author and no forward maintenance. But for the wider web, it's a foot-gun.

I raised these details with the CSSWG, but Safari went ahead and shipped, and will not unship. Further concerns in the thread were ignored.

@martinthomson
Copy link
Contributor

Thanks for raising that here Jake. I can see how this is potentially problematic.

Is your point that this is a straight-up misfeature and it shouldn't exist at all because match-element is enough for SPA where as MPA can't rely on auto unless there is exceptional id-allocation discipline?

Or, do you have an alternative approach in mind? Is that alternative as simple as an appropriately-scoped [id] { view-transition-name: attr(id type(<custom-ident>)) }?

(We are given repeated reminders how the TAG and standards groups more generally have no direct ability to affect what ships in browsers, so I doubt we can do anything about Safari plans.)

@jakearchibald
Copy link

jakearchibald commented Mar 26, 2025

Is your point that this is a straight-up misfeature and it shouldn't exist at all because match-element is enough for SPA where as MPA can't rely on auto unless there is exceptional id-allocation discipline?

I like match-element as a feature, but I don't think it's "the best solution". For example, if you're doing a naive vanilla SPA, a common way to do this is grab the page content from somewhere, and set it as the innerHTML of some container. Because of this, you lose element equality. That's how it works in the simple demos I built - the header bar loses element equality here, because it's recreated from markup.

In SPAs built with a framework such as React/Vue/Svelte, element equality is abstracted away from you. It's just an optimisation. And (as far as I know) none of them allow an element to change parent node. Fun example: In NextJS and Remix, each route is a component, so if your <IndexRoute> and <AboutRoute> each contain a <SideNav>, the side nav will be fully rebuilt (and not equal) as you move between those routes, since the parent component type has changed.

You can maintain element equality when reordering siblings using the key prop, which is essentially a sibling-scoped identifier. But, if you're already going to the trouble of assigning a key, it's pretty easy to assign a unique view-transition-name.

At somewhere like Shopify, I'd recommend assigning a view-transition-name ident, as it's more reliable.

That said, I think match-element is a nice addition and low risk. If you understand that elements are linked by referential equality, and you see the transition going wrong, then you know something is going wrong with referential equality.

The problem with view-transition-name: auto is it puts this behaviour behind an unassuming name, and throws in this extra id behaviour that seems more problematic than useful.

Or, do you have an alternative approach in mind? Is that alternative as simple as an appropriately-scoped [id] { view-transition-name: attr(id type(<custom-ident>)) }?

I love that CSS attr() is this expressive now, and that view transitions are used as an example on MDN.

As a codebase maintainer, I'd raise concerns about view-transition-name: attr(id type(<custom-ident>)), because I think it's risky overloading id in this way, and I'd suggest creating a custom data- attribute instead. But this is actually great because:

  • Switching to use another attribute is not only possible, it's very easy
  • As a code reviewer, it was obvious to me that the id attribute was being used to generate the view-transition-name, rather than being hidden behind the auto value.

Given that view-transition-name: attr(id type(<custom-ident>), match-element) is already possible, and expresses intent, I don't see the benefit of obscuring it behind an auto value. Doing so makes it seem 'recommended', and I don't think the platform should be recommending it given the foot-guns.

@jakearchibald
Copy link

jakearchibald commented Apr 1, 2025

As well as "adding IDs becomes risky", removing them becomes risky too. Here's a fairly typical case:

Moving IDs to another element:

  1. A page contains <button commandfor="some-element-thing"> and <div id="some-element-thing">.
  2. A developer decides that the button should target a different element. It seems unlikely that pages are going to link to that particular div, and they search the codebase for some-element-thing and do not find it referenced anywhere else. So they move id="some-element-thing" to another element.
  3. Transitions are broken, because that id was necessary to maintain a connection between the elements for a transition.

Cleaning up IDs:

  1. A page contains <button commandfor="some-element-thing"> and <div id="some-element-thing">.
  2. A developer removes that button, as it's no longer needed. To maintain a clean codebase, they consider removing the id from the other element. It seems unlikely that pages are going to link to that particular div, and they search the codebase for some-element-thing and do not find it referenced anywhere else, so they assume id="some-element-thing" is safe to remove.
  3. Transitions are broken, because that id was necessary to maintain a connection between the elements for a transition.

In both of these cases, the transition may not be broken in all cases. In many instances of the transition, element equality may be enough, so the bug is likely to hit production.

This wouldn't be an issue with <div style="view-transition-name: some-element-thing">, or <div data-vt-name="some-element-thing"> + attr(data-vt-name type(<custom-ident>), match-element).

@jyasskin
Copy link
Contributor

jyasskin commented Apr 1, 2025

The TAG has previously been concerned about CSS using auto for meanings that aren't going to be obvious to authors. See #1011 (comment). This may be another case where the CSSWG forgot to bikeshed the name before approving it. Looking at the history:

The TAG hasn't discussed any consensus preferences yet, and obviously we can't force Safari to do anything in particular, but we could encourage the other browsers to hold off on defining auto and either let authors write out attr(id type(<custom-ident>), match-element) or pick a clearer name for that behavior.

@noamr
Copy link
Author

noamr commented Apr 1, 2025

The TAG has previously been concerned about CSS using auto for meanings that aren't going to be obvious to authors. See #1011 (comment). This may be another case where the CSSWG forgot to bikeshed the name before approving it. Looking at the history:

Look at the minutes: w3c/csswg-drafts#8320 (comment)
The spec change was based on the meeting minutes in which we've discussed and resolved on this, though the official resolution line didn't state the name.

@jyasskin
Copy link
Contributor

jyasskin commented Apr 1, 2025

In those minutes, I see a "PROPOSED: Introduce keyword for element identity, some other syntax for using the element's ID, and auto keyword that switches between the two", followed by "RESOLVED: Add three keywords, one for ID attribute, one for element identity, and one that does fallback between the two." That is, the WG modified the resolution text to drop auto (though without any recorded discussion describing why). You definitely didn't make up the keyword from whole cloth, but that reads to me like the WG knew it hadn't yet confirmed auto was the right name, and then forgot to come back to it.

I wonder if we/the CSSWG should write a CSS design principle about taking extra care when proposing auto as a property value...

@jyasskin jyasskin added chromium-high-priority Something that the Chromium team would like the TAG to prioritise Agenda+ and removed Resolution: satisfied The TAG is satisfied with this design labels Apr 9, 2025
@jyasskin
Copy link
Contributor

We looked at this in a breakout today, and we agree with Jake that auto is not the right keyword for this fallback behavior. We appreciate the CSSWG's attempt to give developers a way to make view transitions that "just work", but it looks like the behavior has more sharp edges than expected. When CSS uses the auto keyword, it should generally be safe enough to be the default, and it doesn't look like this is. We don't feel strongly that there must not be a keyword for this behavior, but because this behavior is achievable using attr(id type(<custom-ident>), match-element), we'd encourage the WG and browsers to spend some more time looking for a safer default behavior before giving this behavior a keyword.

We're still satisfied with the match-element keyword, but switching the Resolution for the auto keyword.

@jyasskin jyasskin added the Resolution: unsatisfied The TAG does not feel the design meets required quality standards label Apr 15, 2025
@Abad1244

This comment was marked as spam.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Agenda+ chromium-high-priority Something that the Chromium team would like the TAG to prioritise Resolution: unsatisfied The TAG does not feel the design meets required quality standards Topic: CSS Venue: CSS WG
Projects
None yet
Development

No branches or pull requests

7 participants