Skip to content

Commit 3a5496b

Browse files
authored
[Fiber] Use className on <ViewTransition> to assign view-transition-class (#31999)
Stacked on #31975. This is the primary way we recommend styling your View Transitions since it allows for reusable styling such as a CSS library specializing in View Transitions in a way that's composable and without naming conflicts. E.g. ```js <ViewTransition className="enter-slide-in exit-fade-out update-cross-fade"> ``` This doesn't change the HTML `class` attribute. It's not a CSS class. Instead it assign the `view-transition-class` style prop of the underlying DOM node while it's transitioning. You can also just use `<div style={{viewTransitionClass: ...}}>` on the DOM node but it's convenient to control the Transition completely from the outside and conceptually we're transitioning the whole fragment. You can even make Transition components that just wraps existing components. `<RevealTransition><Component /></RevealTransition>` this way. Since you can also have multiple wrappers for different circumstances it allows React's heuristics to use different classes for different scenarios. We'll likely add more options like configuring different classes for different `types` or scenarios that can't be described by CSS alone. ## CSS Modules ```js import transitions from './transitions.module.css'; <ViewTransition className={transitions.bounceIn}>...</ViewTransition> ``` CSS Modules works well with this strategy because you can have globally unique namespaces and define your transitions in the CSS modules as a library that you can import. [As seen in the fixture here.](8b91b37#diff-b4d9854171ffdac4d2c01be92a5eff4f8e9e761e6af953094f99ca243b054a85R11) I did notice an unfortunate bug in how CSS Modules (at least in Webpack) generates class names. Sometimes the `+` character is used in the hash of the class name which is not valid for `view-transition-class` and so it breaks. I had to rename my class names until the hash yielded something different to work around it. Ideally that bug gets fixed soon. ## className, rly? `className` isn't exactly the most loved property name, however, I'm using `className` here too for consistency. Even though in this case there's no direct equivalent DOM property name. The CSS property is named `viewTransitionClass`, but the "viewTransition" prefix is implied by the Component it is on in this case. For most people the fact that this is actually a different namespace than other CSS classes doesn't matter. You'll most just use a CSS library anyway and conceptually you're just assigning classes the same way as `className` on a DOM node. But if we ever rename the `class` prop then we can do that for this one as well.
1 parent a4d122f commit 3a5496b

13 files changed

Lines changed: 8366 additions & 5471 deletions

File tree

fixtures/view-transition/package.json

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44
"private": true,
55
"devDependencies": {
66
"concurrently": "3.1.0",
7-
"http-proxy-middleware": "0.17.3",
8-
"react-scripts": "0.9.5"
7+
"http-proxy-middleware": "3.0.3",
8+
"react-scripts": "5.0.1",
9+
"@babel/plugin-proposal-private-property-in-object": "7.21.11"
910
},
1011
"dependencies": {
12+
"@babel/register": "^7.25.9",
1113
"express": "^4.14.0",
1214
"ignore-styles": "^5.0.1",
1315
"react": "^19.0.0",
1416
"react-dom": "^19.0.0"
1517
},
18+
"eslintConfig": {
19+
"extends": [
20+
"react-app",
21+
"react-app/jest"
22+
]
23+
},
1624
"scripts": {
1725
"predev": "cp -r ../../build/oss-experimental/* ./node_modules/",
1826
"prestart": "cp -r ../../build/oss-experimental/* ./node_modules/",
@@ -24,5 +32,17 @@
2432
"build": "react-scripts build",
2533
"test": "react-scripts test --env=jsdom",
2634
"eject": "react-scripts eject"
35+
},
36+
"browserslist": {
37+
"production": [
38+
">0.2%",
39+
"not dead",
40+
"not op_mini all"
41+
],
42+
"development": [
43+
"last 1 chrome version",
44+
"last 1 firefox version",
45+
"last 1 safari version"
46+
]
2747
}
2848
}

fixtures/view-transition/server/index.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
require('ignore-styles');
2-
const babelRegister = require('babel-register');
2+
const babelRegister = require('@babel/register');
33
const proxy = require('http-proxy-middleware');
44

55
babelRegister({
6-
ignore: /\/(build|node_modules)\//,
6+
ignore: [/\/(build|node_modules)\//],
77
presets: ['react-app'],
88
});
99

@@ -37,9 +37,10 @@ app.use(express.static(path.resolve(__dirname, '..', 'build')));
3737
if (process.env.NODE_ENV === 'development') {
3838
app.use(
3939
'/',
40-
proxy({
40+
proxy.createProxyMiddleware({
4141
ws: true,
42-
target: 'http://localhost:3001',
42+
changeOrigin: true,
43+
target: 'http://127.0.0.1:3001',
4344
})
4445
);
4546
}

fixtures/view-transition/server/render.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ if (process.env.NODE_ENV === 'development') {
1111
// 'main.css': '',
1212
};
1313
} else {
14-
assets = require('../build/asset-manifest.json');
14+
assets = require('../build/asset-manifest.json').files;
1515
}
1616

1717
export default function render(url, res) {

fixtures/view-transition/src/components/Page.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import React, {
88

99
import './Page.css';
1010

11+
import transitions from './Transitions.module.css';
12+
1113
const a = (
1214
<div key="a">
1315
<ViewTransition>
@@ -24,6 +26,17 @@ const b = (
2426
</div>
2527
);
2628

29+
function Component() {
30+
return (
31+
<ViewTransition
32+
className={
33+
transitions['enter-slide-right'] + ' ' + transitions['exit-slide-left']
34+
}>
35+
<p>Slide In from Left, Slide Out to Right</p>
36+
</ViewTransition>
37+
);
38+
}
39+
2740
export default function Page() {
2841
const [show, setShow] = useState(false);
2942
useEffect(() => {
@@ -72,6 +85,7 @@ export default function Page() {
7285
<div>!!</div>
7386
</ViewTransition>
7487
</Activity>
88+
{show ? <Component /> : <p>&nbsp;</p>}
7589
</div>
7690
</ViewTransition>
7791
</div>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@keyframes enter-slide-right {
2+
0% {
3+
opacity: 0;
4+
translate: -200px 0;
5+
}
6+
100% {
7+
opacity: 1;
8+
translate: 0 0;
9+
}
10+
}
11+
12+
@keyframes exit-slide-left {
13+
0% {
14+
opacity: 1;
15+
translate: 0 0;
16+
}
17+
100% {
18+
opacity: 0;
19+
translate: 200px 0;
20+
}
21+
}
22+
23+
::view-transition-new(.enter-slide-right):only-child {
24+
animation: enter-slide-right ease-in 0.25s;
25+
}
26+
::view-transition-old(.exit-slide-left):only-child {
27+
animation: exit-slide-left ease-in 0.25s;
28+
}

0 commit comments

Comments
 (0)