Skip to content

Commit 7c9b056

Browse files
committed
[Fizz] Add vt- prefix attributes to annotate <ViewTransition> in HTML (facebook#33206)
Stacked on facebook#33194 and facebook#33200. When Suspense boundaries reveal during streaming, the Fizz runtime will be responsible for animating the reveal if necessary (not in this PR). However, for the future runtime to know what to do it needs to know about the `<ViewTransition>` configuration to apply. Ofc, these are virtual nodes that disappear from the HTML. We could model them as comments like we do with other virtual nodes like Suspense and Activity. However, that doesn't let us target them with querySelector and CSS (for no-JS transitions). We also don't have to model every ViewTransition since not every combination can happen using only the server runtime. So instead this collapses `<ViewTransition>` and applies the configuration to the inner DOM nodes. ```js <ViewTransition name="hi"> <div /> <div /> </ViewTransition> ``` Becomes: ```html <div vt-name="hi" vt-update="auto"></div> <div vt-name="hi_1" vt-update="auto"></div> ``` I use `vt-` prefix as opposed to `data-` to keep these virtual attributes away from user specific ones but we're effectively claiming this namespace. There are four triggers `vt-update`, `vt-enter`, `vt-exit` and `vt-share`. The server resolves which ones might apply to this DOM node. The value represents the class name (after resolving view-transition-type mappings) or `"auto"` if no specific class name is needed but this is still a trigger. The value can also be `"none"`. This is different from missing because for example an `vt-update="none"` will block mutations inside it from triggering the boundary where as a missing `vt-update` would bubble up to be handled by a parent. `vt-name` is technically only necessary when `vt-share` is specified to find a pair. However, since an explicit name can also be used to target specific CSS selectors, we include it even for other cases. We want to exclude as many of these annotations as possible. `vt-enter` can only affect the first DOM node inside a Suspense boundary's content since the reveal would cause it to enter but nothing deeper inside. Similarly `vt-exit` can only affect the first DOM node inside a fallback. So for every other case we can exclude them. (For future MPA ViewTransitions of the whole document it might also be something we annotate to children inside the `<body>` as well.) Ideally we'd only include `vt-enter` for Suspense boundaries that actually flushed a fallback but since we prepare all that content earlier it's hard to know. `vt-share` can be anywhere inside an fallback or content. Technically we don't have to include it outside the root most Suspense boundary or for boundaries that are inlined into the root shell. However, this is tricky to detect. It would also not be correct for future MPA ViewTransitions because in that case the shared scenario can affect anything in the two documents so it needs to be in every node everywhere which is effectively what we do. If a `share` class is specified but it has no explicit name, we can exclude it since it can't match anything. `vt-update` is only necessary if something below or a sibling might update like a Suspense boundary. However, since we don't know when rendering a segment if it'll later asynchronously add a Suspense boundary later we have to assume that anywhere might have a child. So these are always included. We collapse to use the inner most one when directly nested though since that's the one that ends up winning. There are some weird edge cases that can't be fully modeled by the lack of virtual nodes. DiffTrain build for [65b5aae](facebook@65b5aae)
1 parent 740458d commit 7c9b056

36 files changed

+1743
-911
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
96eb84e493c4ff2c280990659057164c0f16bbb8
1+
65b5aae010002ef88221cc4998711eaef6068006
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
96eb84e493c4ff2c280990659057164c0f16bbb8
1+
65b5aae010002ef88221cc4998711eaef6068006

compiled/facebook-www/React-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ __DEV__ &&
15371537
exports.useTransition = function () {
15381538
return resolveDispatcher().useTransition();
15391539
};
1540-
exports.version = "19.2.0-www-classic-96eb84e4-20250514";
1540+
exports.version = "19.2.0-www-classic-65b5aae0-20250515";
15411541
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
15421542
"function" ===
15431543
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ __DEV__ &&
15371537
exports.useTransition = function () {
15381538
return resolveDispatcher().useTransition();
15391539
};
1540-
exports.version = "19.2.0-www-modern-96eb84e4-20250514";
1540+
exports.version = "19.2.0-www-modern-65b5aae0-20250515";
15411541
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
15421542
"function" ===
15431543
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,4 +635,4 @@ exports.useSyncExternalStore = function (
635635
exports.useTransition = function () {
636636
return ReactSharedInternals.H.useTransition();
637637
};
638-
exports.version = "19.2.0-www-classic-96eb84e4-20250514";
638+
exports.version = "19.2.0-www-classic-65b5aae0-20250515";

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,4 +635,4 @@ exports.useSyncExternalStore = function (
635635
exports.useTransition = function () {
636636
return ReactSharedInternals.H.useTransition();
637637
};
638-
exports.version = "19.2.0-www-modern-96eb84e4-20250514";
638+
exports.version = "19.2.0-www-modern-65b5aae0-20250515";

compiled/facebook-www/React-profiling.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ exports.useSyncExternalStore = function (
639639
exports.useTransition = function () {
640640
return ReactSharedInternals.H.useTransition();
641641
};
642-
exports.version = "19.2.0-www-classic-96eb84e4-20250514";
642+
exports.version = "19.2.0-www-classic-65b5aae0-20250515";
643643
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
644644
"function" ===
645645
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-profiling.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ exports.useSyncExternalStore = function (
639639
exports.useTransition = function () {
640640
return ReactSharedInternals.H.useTransition();
641641
};
642-
exports.version = "19.2.0-www-modern-96eb84e4-20250514";
642+
exports.version = "19.2.0-www-modern-65b5aae0-20250515";
643643
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
644644
"function" ===
645645
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19063,10 +19063,10 @@ __DEV__ &&
1906319063
(function () {
1906419064
var internals = {
1906519065
bundleType: 1,
19066-
version: "19.2.0-www-classic-96eb84e4-20250514",
19066+
version: "19.2.0-www-classic-65b5aae0-20250515",
1906719067
rendererPackageName: "react-art",
1906819068
currentDispatcherRef: ReactSharedInternals,
19069-
reconcilerVersion: "19.2.0-www-classic-96eb84e4-20250514"
19069+
reconcilerVersion: "19.2.0-www-classic-65b5aae0-20250515"
1907019070
};
1907119071
internals.overrideHookState = overrideHookState;
1907219072
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -19100,7 +19100,7 @@ __DEV__ &&
1910019100
exports.Shape = Shape;
1910119101
exports.Surface = Surface;
1910219102
exports.Text = Text;
19103-
exports.version = "19.2.0-www-classic-96eb84e4-20250514";
19103+
exports.version = "19.2.0-www-classic-65b5aae0-20250515";
1910419104
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
1910519105
"function" ===
1910619106
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18835,10 +18835,10 @@ __DEV__ &&
1883518835
(function () {
1883618836
var internals = {
1883718837
bundleType: 1,
18838-
version: "19.2.0-www-modern-96eb84e4-20250514",
18838+
version: "19.2.0-www-modern-65b5aae0-20250515",
1883918839
rendererPackageName: "react-art",
1884018840
currentDispatcherRef: ReactSharedInternals,
18841-
reconcilerVersion: "19.2.0-www-modern-96eb84e4-20250514"
18841+
reconcilerVersion: "19.2.0-www-modern-65b5aae0-20250515"
1884218842
};
1884318843
internals.overrideHookState = overrideHookState;
1884418844
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -18872,7 +18872,7 @@ __DEV__ &&
1887218872
exports.Shape = Shape;
1887318873
exports.Surface = Surface;
1887418874
exports.Text = Text;
18875-
exports.version = "19.2.0-www-modern-96eb84e4-20250514";
18875+
exports.version = "19.2.0-www-modern-65b5aae0-20250515";
1887618876
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
1887718877
"function" ===
1887818878
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

0 commit comments

Comments
 (0)