Open jakearchibald opened 1 year ago
As @chriscoyier pointed out recently, this solution doesn't really give authors a way to define transitions for the unique names that are generated. How would an author write ::view-transition-*
selectors to target these elements with dynamically generated names? In your example, you use the *
name selector, but that would target all view transitions on the page. Ideally, we'd be able to target just list-items. Maybe I'm missing something that already makes that possible?
I think that's a separate feature https://github.com/w3c/csswg-drafts/issues/8319, but yes, you would need that feature before this one. I should have mentioned it in the OP.
I don't think we actually want this to be a generic feature, for a few reasons.
So I instead recommend having view-transitions define a custom way of saying "this thing has a name, but also is unique based on element identity in a way that's not exposed directly but does affect before/after matching". Maybe just view-transition-name: foo unique;
or foo per-element
?
I'm happy to sit on this one for a bit. https://github.com/w3c/csswg-drafts/issues/8319 is higher priority.
Suggesting that a solution to this should take https://github.com/w3c/csswg-drafts/issues/8319#issuecomment-1756046626 into account, and build upon #9141 as a way to dynamically generate unique idents.
For example, given a playlist of songs, all of the songs can have a song
view-transition-name, but also each one of them needs a unique one (if for example this is a sort animation). So the song would need something like a song-123
name.
The idea in #9141 is to concat it from attributes, e.g. view-transition-name: ident("song-" attr(id))
.
The big advantage over using something like element-uuid()
is that this can work across documents - you generate the ID from existing HTML data rather than from some internal browser state that can't carry over between documents.
This is also flexible in a way that DOM order can be used instead of attributes, e.g. with the counter()
function:
view-transition-name: ident("item-" counter(list-foo))
or any result of a calc: ident("item-" mod(var(--something), 3)
.
@tdresser shared an interesting observation today, frameworks like React have a concept of a unique key for list items : https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key. It seems like instead of a native API, where we can only assume element identify via the corresponding Node, it would be cool to leverage such concepts within the frameworks. Maybe add an API which makes hooking up a custom attribute to the CSS property value easier?
As someone who is very often not using one of these frameworks, it doesn't feel like a great solution to me.
@mirisuzanne are you otherwise happy with a feature that would only work same-document transitions?
I wonder what people think about an idea like this:
section.list li.item[:id] img { view-transition-name: item-[:id] }
Where the attributes are captured in the selector chain and then used to generate a name.
Oh, my happiness. Interesting… :)
I'm not actually opposed to a feature that makes it easier for third-party tools to assist in the cross-document functionality. I just hope we also keep working towards a solution that doesn't rely on frameworks?
@noamr I think your proposal still has the cross document issue? Those IDs would be specific to a given document?
Oh, my happiness. Interesting… :)
I'm not actually opposed to a feature that makes it easier for third-party tools to assist in the cross-document functionality. I just hope we also keep working towards a solution that doesn't rely on frameworks?
@noamr I think your proposal still has the cross document issue? Those IDs would be specific to a given document?
You can have elements with the same IDs on the new document, eg if the IDs come from some database.
(#) I wonder what people think about an idea like this:
section.list li.item[:id] img { view-transition-name: item-[:id] }
Where the attributes are captured in the selector chain and then used to generate a name.
Interesting. I like how this allows you to capture an attribute from a parent element and use that on a child. With attr()
that’s not immediately possible, unless you jump through some hoops using a custom property.
Two thoughts:
I would use something different than :
to capture it, as it could be confused with pseudos. Am thinking of an @
right now:
section.list li.item[@id] { … }
To use it in an ident, I think we’d still need somethink like #9141 to dynamically construct the resulting ident.
section.list li.item[@id] {
view-transition-name: ident("item-" @id);
}
(Am using @id
to refer to the local variable here)
(#) I wonder what people think about an idea like this:
section.list li.item[:id] img { view-transition-name: item-[:id] }
Where the attributes are captured in the selector chain and then used to generate a name.Interesting. I like how this allows you to capture an attribute from a parent element and use that on a child. With
attr()
that’s not immediately possible, unless you jump through some hoops using a custom property.Two thoughts:
- I would use something different than
:
to capture it, as it could be confused with pseudos. Am thinking of an@
right now:section.list li.item[@id] { … }
- To use it in an ident, I think we’d still need somethink like [css-values] A way to dynamically construct custom-ident and dashed-ident values #9141 to dynamically construct the resulting ident.
section.list li.item[@id] { view-transition-name: ident("item-" @id); }
Isn't the ident
function redundant though? why not "item-" @id
or item-(@id)
or something like that?
One issue with that syntax is it allows you to capture an attribute named n from only one element in the chain. As in, you can't do: .one[@foo] .two[@foo]
, because now you have two @foo
and no way to differentiate them.
I don't like the naming/syntax here, but you could do something like:
:capture-for-attr(.one, 'one') :capture-for-attr(.two, 'two') {
view-transition-name: captured-attr('one', 'foo') captured-attr('two', 'foo');
}
:capture-for-attr(selector, name)
- where selector
is processes as normal, but the selected element is stored as name
.
captured-attr(name, attribute)
- like attr()
, but the first arg is the name
of something captured earlier in the selector.
This all feels very complicated though.
I think that if we wanted to support multiple attributes with the same name in the future we could add something like:
.one[@id1:id] .two[@id2:id] { view-transition-name: "something" @id1 @id2 }
(JS destructuring assignments have the same problem and a similar sollution)
If we go with verbose function-style I'd rather use the more conservative attr+CSS-variables.
A while back I ran a poll (Twitter, Mastodon) asking authors what is missing from View Transitions. Out of the 33 replies, 6 requested this feature, making it the number 1 request (along with retargetable transitions and scoped transitions)
I'm curious what the .foo[@id]
selector syntax adds in this proposal. On the other end, item-[@id]
seems like a nice concise syntax equivalent to e.g. item- attr(id, ident))
. If we can access attributes as idents (or interpolate them into idents), I'm not sure if the selector-side syntax bring anything new to the equation? Having a value-side shorthand for accessing attributes as idents might still be useful - it's certainly simpler to write.
I'm curious what the
.foo[@id]
selector syntax adds in this proposal. On the other end,item-[@id]
seems like a nice concise syntax equivalent to e.g.item- attr(id, ident))
. If we can access attributes as idents (or interpolate them into idents), I'm not sure if the selector-side syntax bring anything new to the equation? Having a value-side shorthand for accessing attributes as idents might still be useful - it's certainly simpler to write.
Which ID would you pick, given:
section[id] > li[id] label * { view-transition-name: item-[@id] }`
Without @
at the selector we'd have to assume that you mean the attribute on the selected element, but that's not always the case.
I'm making a simple demo that uses View Transitions to animate enlarging an item that's laid out with CSS Grid, and shifting the other items in the grid around. I was surprised to learn that 1) I have to use JavaScript to use View Transitions, and 2) that I have to give each item in the Grid a unique view-transition-name
, and there's no mechanism for applying the functionality to all items.
This caused me to have to write this code:
.card:nth-child(1) { view-transition-name: card-1; }
.card:nth-child(2) { view-transition-name: card-2; }
.card:nth-child(3) { view-transition-name: card-3; }
.card:nth-child(4) { view-transition-name: card-4; }
.card:nth-child(5) { view-transition-name: card-5; }
.card:nth-child(6) { view-transition-name: card-6; }
.card:nth-child(7) { view-transition-name: card-7; }
.card:nth-child(8) { view-transition-name: card-8; }
.card:nth-child(9) { view-transition-name: card-9; }
.card:nth-child(10) { view-transition-name: card-10; }
.card:nth-child(11) { view-transition-name: card-11; }
.card:nth-child(12) { view-transition-name: card-12; }
.card:nth-child(13) { view-transition-name: card-13; }
.card:nth-child(14) { view-transition-name: card-14; }
.card:nth-child(15) { view-transition-name: card-15; }
.card:nth-child(16) { view-transition-name: card-16; }
.card:nth-child(17) { view-transition-name: card-17; }
.card:nth-child(18) { view-transition-name: card-18; }
.card:nth-child(19) { view-transition-name: card-19; }
.card:nth-child(20) { view-transition-name: card-20; }
.card:nth-child(21) { view-transition-name: card-21; }
.card:nth-child(22) { view-transition-name: card-22; }
.card:nth-child(23) { view-transition-name: card-23; }
.card:nth-child(24) { view-transition-name: card-24; }
.card:nth-child(25) { view-transition-name: card-25; }
.card:nth-child(26) { view-transition-name: card-26; }
.card:nth-child(27) { view-transition-name: card-27; }
.card:nth-child(28) { view-transition-name: card-28; }
.card:nth-child(29) { view-transition-name: card-29; }
.card:nth-child(30) { view-transition-name: card-30; }
.card:nth-child(31) { view-transition-name: card-31; }
.card:nth-child(32) { view-transition-name: card-32; }
.card:nth-child(33) { view-transition-name: card-33; }
.card:nth-child(34) { view-transition-name: card-34; }
.card:nth-child(35) { view-transition-name: card-35; }
.card:nth-child(36) { view-transition-name: card-36; }
.card:nth-child(37) { view-transition-name: card-37; }
.card:nth-child(38) { view-transition-name: card-38; }
.card:nth-child(39) { view-transition-name: card-39; }
.card:nth-child(40) { view-transition-name: card-40; }
.card:nth-child(41) { view-transition-name: card-41; }
.card:nth-child(42) { view-transition-name: card-42; }
.card:nth-child(43) { view-transition-name: card-43; }
.card:nth-child(44) { view-transition-name: card-44; }
.card:nth-child(45) { view-transition-name: card-45; }
.card:nth-child(46) { view-transition-name: card-46; }
.card:nth-child(47) { view-transition-name: card-47; }
.card:nth-child(48) { view-transition-name: card-48; }
.card:nth-child(49) { view-transition-name: card-49; }
.card:nth-child(50) { view-transition-name: card-50; }
Which is not robust — what if there are more than 50 items on the Grid?? The experience will break.
It seems View Transitions was designed with the expectation that websites are JavaScript first — that it's fine if every items needs to be uniquely named, a developer can just use JS to create all the HTML, and inline styles with JS-created names.
This would be a much better solution: .card { view-transition-name: auto; }
This would be a much better solution:
.card { view-transition-name: auto; }
See discussion above - the problem with this solution is that it doesn't work for cross-document, or if the framework recreates the element. Generating the name from attributes is perhaps more verbose but works for all those use-cases.
It seems View Transitions was designed with the expectation that websites are JavaScript first
This is only the case for Same-Document View Transitions. For Cross-Document View Transitions JavaScript is not mandatory.
(And, in the future, there could always be worked on defining some non-JS based triggers for Same-Document View Transitions)
This would be a much better solution:
.card { view-transition-name: auto; }
While that could solve some Same-Document View Transitions, this solution won’t work for:
This is also discussed earlier in this thread.
Also see the discussions in https://github.com/w3c/csswg-drafts/issues/9639 and https://github.com/w3c/csswg-drafts/issues/9141 which are concerned with solving this naming problem.
Another syntax option for generating the name from an ID:
section[--id: id] img.thumbnail {
view-transition-name: "thumb-" var(--id);
}
So instead of using attr
or a special syntax for concating the ident, we only have a special syntax in the selector that assigns the attribute value to a CSS custom property, and view-transition-name
can accept a concatentation of literal strings and functions that generate strings/numbers.
I don't really like the idea of having extra syntax here specifically for view transitions. I also don't like that selectors then suddenly have side effects.
section[--id: id] img.thumbnail {
view-transition-name: "thumb-" var(--id);
}
could be :
section[id]:has(img.thumbnail) {
--id: attr(id);
}
section[id] img.thumbnail {
view-transition-name: "thumb-" var(--id);
}
@jensimmons
Which is not robust — what if there are more than 50 items on the Grid?? The experience will break.
It will also break if you remove the first item in the grid, because it'll assume the first item in the grid before and after the change are the same grid item, but they are not. This is why you need an identity-based system.
In SPAs, you could say that the element instance is the identity. However, that isn't how it ends up in many SPA implementations, and it's never how it ends up in MPA, since no elements are the same instance across documents.
I was surprised to learn that 1) I have to use JavaScript to use View Transitions
Out of curiosity, how were you modifying the grid in this example?
I don't really like the idea of having extra syntax here specifically for view transitions.
It can be used in the future for any other feature that uses idents.
I also don't like that selectors then suddenly have side effects.
That's fair
section[--id: id] img.thumbnail { view-transition-name: "thumb-" var(--id); }
could be :
section[id]:has(img.thumbnail) { --id: attr(id); } section[id] img.thumbnail { view-transition-name: "thumb-" var(--id); }
Yea, this was proposed as an alternative earlier in the thread.
It's a bit more verbose, but I'm fine with it if we go down this more conservative path.
What always felt off to me about attr
is that attributes are an HTML concept, which already exists in selectors, and here we put it into the declaration, creating a bit of a redundancy. But since attr()
already (sort of) exists perhaps that ship has sailed?
sidetrack I also can't help but feel that user should be able to use the full selector syntax to select view transition pseudo elements and that we are coming at this from the wrong angle.
As I see it we actually want to have classes, attributes and id's on pseudo elements which is not possible because authors can add these in markdown.
But what if we allow authors to push this information onto them?
/* ignore the exact naming and syntax */ .foo { view-transition-id: "something-unqiue"; view-transition-class: "class-a" "class-b" "class-d"; view-transition-attributes: ["foo" "bar"] ["fooz" "baz"] }
We already have
view-transition-name
(unique) andview-transition-class
(which is kind of like how you put it here). We can discuss having attributes as well but perhaps it's indeed a new issue. Determining the name by element is not about pseudo-elements, it's about matching the old and new states.
@romainmenke
I also can't help but feel that user should be able to use the full selector syntax to select view transition pseudo elements
Here's an example of a selector for a view transition pseudo element: ::view-transition-new(header)
- can you describe how that isn't 'full'? I don't know if this bit of detail is useful but: Remember that view transition pseudos may be representing something no longer in the document.
But what if we allow authors to push this information onto them?
/* ignore the exact naming and syntax */ .foo { view-transition-id: "something-unqiue"; view-transition-class: "class-a" "class-b" "class-d"; view-transition-attributes: ["foo" "bar"] ["fooz" "baz"] }
Can you describe what the effect of this code is? As in, how would you explain it to a developer? I'm happy to ignore the syntax and naming but I have no idea what it does.
Can you describe what the effect of this code is? As in, how would you explain it to a developer? I'm happy to ignore the syntax and naming but I have no idea what it does.
It would assign classes, id's and attributes onto the pseudo elements.
::view-transition-new(.class-a[foo=bar]) {}
::view-transition-new(.class-b[foo~="bar"]) {}
::view-transition-new(.class-a:is([foo], [fooz])) {}
I really do not want to drag this too much off topic :) But if the issue is that authors need more powerful tools to select view transitions then I do wonder why we aren't leaning into the full selector syntax.
This is mostly in response to string concatenation suggested in view-transition-name: "thumb-" var(--id);
This is actually a key/value pair. thumb
and id
: [thumb=id]
How would this work in terms of matching old/new element? Regardless of pseudo-element selection, you still have to have a way to match the old and new element 1:1. @romainmenke
@romainmenke the class part has already been done https://github.com/w3ctag/design-reviews/issues/938. I'm not sure what attributes add.
Classes aren't key/value pairs. You can't change part of it or select part of without a string concatenation mechanic, which CSS currently doesn't have.
But I labeled it as a sidetrack because I am really unsure if my analysis is correct here. Maybe these aren't key/value pairs at all. Maybe string concatenation is the correct solution.
Classes aren't key/value pairs.
I really don't think that matters. People make classes that are thing--state
all the time. There are whole patterns built around it. I don't see what problem is solved by adding a 'real' key-value system.
Whichever method we land on here, I would like to call out that we would need the ident()
function from #9141 when dynamically constructing an ident.
It might not be relevant for view-transition-name
, but would be for other properties, specifically shorthands.
Cross-posting this comment from #9141 to clarify:
Parsing purposes. Think of shorthands, such as
scroll-timeline
.With
ident()
:
scroll-timeline: inline ident("tl-" var(--id))
scroll-timeline: ident("tl-" var(--id)) inline
Without
ident()
:
scroll-timeline: inline "tl-" var(--id)
scroll-timeline: "tl-" var(--id) inline
If --id
in that example were block
, you’d end up with scroll-timeline: inline "tl-" block
when not using the ident()
function. I don’t think the parser would like that 😅 (nor would I, as an author trying to read the code).
Whichever method we land on here, I would like to call out that we would need the
ident()
function from #9141 when dynamically constructing an ident.It might not be relevant for
view-transition-name
, but would be for other properties, specifically shorthands.Cross-posting this comment from #9141 to clarify:
Parsing purposes. Think of shorthands, such as
scroll-timeline
. Withident()
:
scroll-timeline: inline ident("tl-" var(--id))
scroll-timeline: ident("tl-" var(--id)) inline
Without
ident()
:
scroll-timeline: inline "tl-" var(--id)
scroll-timeline: "tl-" var(--id) inline
If
--id
in that example wereblock
, you’d end up withscroll-timeline: inline "tl-" block
when not using theident()
function. I don’t think the parser would like that 😅 (nor would I, as an author trying to read the code).
I wonder if we can solve this for particular shorthands rather than add avoidable verbosity to view-transition-name
.
I wonder if attr() is usable here? I think if attr() is supported, it would probably address most use cases.
I wonder if attr() is usable here? I think if attr() is supported, it would probably address most use cases.
Yea, something like https://github.com/w3c/csswg-drafts/issues/8320#issuecomment-2044478188
section[id]:has(img.thumbnail) {
--id: attr(id);
}
section[id] img.thumbnail {
view-transition-name: "thumb-" var(--id);
}
If we converge on something like that it would be totally fine.
btw attr()
used to have weird security issues (like having to disallow nonce
), and we'll have to think about this with future security-sensitve attributes, that's why it was never shipped for other feature. If this still comes up, perhaps instead of a general-purpose attr
we could enable data(foo)
and id()
? Seems like id
and custom data-
attributes would be the mainly used attributes for this anyway.
Supporting attr()
with an allow list for which attributes can be referenced sounds neat!
To clarify, I meant having bespoke id()
and data(foo)
functions, which would be instead of attr(id)
and attr(data-foo)
.
I wonder if attr() is usable here? I think if attr() is supported, it would probably address most use cases.
+1 to using attr()
.
Also +1 to Jen's usecase and proposal ( https://github.com/w3c/csswg-drafts/issues/8320#issuecomment-2023077559 ) for auto
.
See discussion above - the problem with this solution is that it doesn't work for cross-document, or if the framework recreates the element. Generating the name from attributes is perhaps more verbose but works for all those use-cases.
An auto
keyword wouldn't address those use cases, but it would address the ones that Jen is trying to solve, which don't have this problem. If you're rebuilding the DOM, then yes, you will need to do something else. But if you're not -- and there are many interesting cases where where you're not -- we can make this a lot simpler for the author.
We could go further to address cross-document cases by relaxing the restriction that a name must be unique and come up with a matching algorithm that can handle multiple elements.
To address things that move or disappear, authors can use unique tags for those items. So let's say you have a list of 10 items and you're moving item 8 to be the 2nd item. You identify that item with a unique name, and the rest of the items with a generic one, and the UA matches them up in DOM order.
We might want to be able to scope the count within a subtree somehow, but I think this would work without requiring counting code in the HTML generator or in JS, and that seems like a usability win imho, particularly for lightweight websites.
(#) An
auto
keyword wouldn't address those use cases, but it would address the ones that Jen is trying to solve, which don't have this problem. If you're rebuilding the DOM, then yes, you will need to do something else. But if you're not -- and there are many interesting cases where where you're not -- we can make this a lot simpler for the author.
Do note that auto
would work in Jen’s case as the code is not using any custom animations. Once an author wants to use a custom animation, auto
itself can’t help you there. When everything is set to auto
, how would you apply custom animations onto them: ::view-transition-group(?whatgoeshere?)
?
It’s only when combined view-transition-class
that view-transition-name: auto
becomes really useful – something worth considering in WebKit’s case, as by the looks of it the current implementation seems to be scoped to L1 which does not include L2’s view-transition-class
.
(#) To address things that move or disappear, authors can use unique tags for those items. So let's say you have a list of 10 items and you're moving item 8 to be the 2nd item. You identify that item with a unique name, and the rest of the items with a generic one, and the UA matches them up in DOM order.
Removing nodes is not covered by this. Take a list of 10 items where you remove item 2 wrapped in a View Transition. All items after the original item 2 get the numbers 3-10 in the old snapshot but 2-9 in the new snapshot – they won’t line up.
(IIRC random()
would be able to correctly cover this, as you can pin a generated value on a node)
This to say that the story for view-transition-name: auto
would be: “Yeah you can use that, but not if you use framework X, Y, or Z; not if you are removing nodes; not if you want to apply custom animations in browser X because they have implemented L1; and not for MPA.” – seems a bit weird to add a feature that only works in a limited number of cases.
The ident()
+ attr()
approach does not have these limitations.
I wonder if attr() is usable here? I think if attr() is supported, it would probably address most use cases.
+1 to using
attr()
.
Great!
Also +1 to Jen's usecase and proposal ( #8320 (comment) ) for
auto
.See discussion above - the problem with this solution is that it doesn't work for cross-document, or if the framework recreates the element. Generating the name from attributes is perhaps more verbose but works for all those use-cases.
An
auto
keyword wouldn't address those use cases, but it would address the ones that Jen is trying to solve, which don't have this problem. If you're rebuilding the DOM, then yes, you will need to do something else. But if you're not -- and there are many interesting cases where where you're not -- we can make this a lot simpler for the author.
It would only address the use case of animating grid positions if the underlying mechanism used for it is an SPA, with a framework that doesn't replace elements. If, for example, this was an MPA where every "current image" had its own page, the author would have to refactor this use case if they used auto
.
In other words, where auto
is enough is not a matter of use case, but a matter of underlying mechanism, or "how your app is built". The problem is that underlying mechanisms can be replaced, and having to refactor view-transition-name: auto
values that are sprinkled around a CSS code base might make people feel that perhaps they should have used attr()
+ ident()
in the first place. This will be even more apparent in use cases that feel less obvious, like an expanding header. auto
would work when the app mechanism is simple, and then when the mechanism changes the author would incur an additional cost because they went with the simpler approach that's less portable by design.
We could go further to address cross-document cases by relaxing the restriction that a name must be unique and come up with a matching algorithm that can handle multiple elements.
Can you give an example for such algorithm? Or you mean that what's below here is that example?
To address things that move or disappear, authors can use unique tags for those items. So let's say you have a list of 10 items and you're moving item 8 to be the 2nd item. You identify that item with a unique name, and the rest of the items with a generic one, and the UA matches them up in DOM order.
This feels simple but actually adding complexity, because it overloads auto
with opinionated behavior that authors have to remember. ident("item-" counter())
or so is much simpler to understand because it's neutral. CSS frameworks can take the role of adding opinionated stuff on top of this for ergonomics, and if we see patterns emerge there we can consider adding something like that into the platform.
To show the net effect the proposed addition of ident()
+attr()
will have on authors I have created comparison demos:
ident()
and attr()
: https://codepen.io/bramus/pen/PogVZwb(The second demo includes a rough polyfill to make it actually work. For best effect use Chrome Canary as these demos also use view-transition-class
)
I was able to remove 100 LOC in the second demo and can now change markup (i.e. add / remove items) without needing to worry about the CSS.
Using view-transition-name: auto
would not have worked here as I am doing the title transition between two different elements (from the h2
to a span
or back, depending on the checked state of the input
).
Agenda+’ing this one as I think it’s ready for discussion at the meeting.
Proposal:
ident()
(from #9141) to dynamically construct idents for use in view-transition-name
and other properties that accept idents.attr()
to be used in ident()
Why not only attr()
?
attr()
an author would need to give each and every element a unique [id]
(or whatever attribute they may use). At that point they’re back to “naming each and every element individually”: card is card-1, title is title-1, photo is photo-1, … rinse and repeat for each and every card+title+photo.[id]
). With ident()
authors then use that value from the card on child elements such as the title, photo, etc.Why ident()
and not allow setting some “strings” as the value?
Why not auto
?
innerHTML
on some parent).<h2>
in one state to the <span>
in the other)
view-transition-class
to target all, set a specific view-transition-name
after all to target just the one.auto
is a solution for View Transitions only. The proposed ident()
not only works with view-transition-name
but also with scroll-timeline-name
, anchor-name
, container-name
, etc.Minor thought on auto
vs element-uuid()
.
Imagine this, which uses the ident()
proposal above:
.card {
--card-id: element-uuid();
view-transition-name: ident(var(--card-id));
img {
view-transition-name: ident(var(--card-id) "-img");
}
}
This works as long as the card elements remain the same elements, but it works even if the img within is a different element.
That doesn't seem possible with auto
.
Do note that auto would work in Jen’s case as the code is not using any custom animations. Once an author wants to use a custom animation, auto itself can’t help you there. When everything is set to auto, how would you apply custom animations onto them: ::view-transition-group(?whatgoeshere?)?
Using view-transition-class
, obviously.
(https://github.com/w3c/csswg-drafts/issues/8320#issuecomment-2060294154) To address things that move or disappear, authors can use unique tags for those items. So let's say you have a list of 10 items and you're moving item 8 to be the 2nd item. You identify that item with a unique name, and the rest of the items with a generic one, and the UA matches them up in DOM order.
Removing nodes is not covered by this. Take a list of 10 items where you remove item 2 wrapped in a View Transition. All items after the original item 2 get the numbers 3-10 in the old snapshot but 2-9 in the new snapshot – they won’t line up.
Not sure what's the problem... You label the item you're removing as "--special-item" and the rest as "--normal-item" and everything should line up just fine?
It would only address the use case of animating grid positions if the underlying mechanism used for it is an SPA, with a framework that doesn't replace elements.
This is fine. Not everyone is building an "SPA". View Transitions can be used to do nice animations for simpler changes within a web page, such as in @jensimmons's demo. Fancy counting functions and string concatenation for building out custom IDs is nice but... if you don't need them, why do we need to require them? We shouldn't require authors to use complicated mechanisms to do simple things.
In general, the CSSWG shouldn't be designing features for large complicated websites only, but also make things easy to use for smaller, simpler websites (and parts of websites) also. Authors can step up to more complicated solutions as they need them, we shouldn't force them into it prematurely.
I +1 the idea of autogenerating names without requiring the author to assign an id to every element. But I prefer an approach which uses node identity to generate names over DOM ordering. The DOM ordering approach is more subtle in that small unrelated changes to the DOM can affect how elements are being matched up.
A node identity based approach is possible with both syntax options: view-transition-name: auto
or view-transition-name: element-uuid()
. I lean towards the latter because of the use-case @jakearchibald pointed to.
@fantasai node identity based generated names should work for the use-case @jensimmons brought up. Would you still want us to use DOM ordering for this..?
Minor thought on
auto
vselement-uuid()
.Imagine this, which uses the
ident()
proposal above:.card { --card-id: element-uuid(); view-transition-name: ident(var(--card-id)); img { view-transition-name: ident(var(--card-id) "-img"); } }
This works as long as the card elements remain the same elements, but it works even if the img within is a different element.
That doesn't seem possible with
auto
.
Practically, I don't see how this is different from:
.card {
view-transition-name: element-uuid();
img {
view-transition-name: element-uuid();
}
}
In which case, auto
would also do the job.
^ view-transition-name: ident(var(--card-id) "-img");
version means even if the img node changes but the ancestor container card
node remains the same, you still get a consistent name.
I personally don't see much point in removing and recreating the img
in this case instead of just mutating it (which is the only thing that would cause the uuid to change), but sure, this is something auto
wouldn't easily do.
Thinking of this example https://codepen.io/jaffathecake/pen/VwBprqL
In this case the developer has to manually give each list item a unique
view-transition-name
. This could be avoided via something like:Where
element-uuid()
returns a value unique and constant for that DOM node.The proposal for CSS
random()
has aper-element
value that could be generalised aselement-uuid()
. https://github.com/w3c/csswg-drafts/issues/2826#issuecomment-1204305712 (cc @tabatkins)The downside to this is it would only work for same-document transitions, since
element-uuid()
s will always be different across documents. It also depends on element usage being consistent, which some frameworks don't always get right.Edit: This would depend on https://github.com/w3c/csswg-drafts/issues/8319, otherwise styling the resulting pseudos is impossible.