- From: Jake Archibald <notifications@github.com>
- Date: Tue, 25 Mar 2025 04:35:28 -0700
- To: w3ctag/design-reviews <design-reviews@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <w3ctag/design-reviews/issues/1001/2750966335@github.com>
jakearchibald left a comment (w3ctag/design-reviews#1001) 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: ```css .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. 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. ```js 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: ```css .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! ```diff 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 `id`s 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](https://github.com/w3c/csswg-drafts/issues/10995#issuecomment-2454252345), but Safari went ahead and shipped, and will not unship. Further concerns in the thread were ignored. -- Reply to this email directly or view it on GitHub: https://github.com/w3ctag/design-reviews/issues/1001#issuecomment-2750966335 You are receiving this because you are subscribed to this thread. Message ID: <w3ctag/design-reviews/issues/1001/2750966335@github.com>
Received on Tuesday, 25 March 2025 11:35:32 UTC