[Fiber] Use className on <ViewTransition> to assign view-transition-class - #31999
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Comparing: a4d122f2d192fe0b6480e669cca43c8f953aaf85...648c923a82a2c374219ff2f1d5b96cc3e082205f Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: Expand to show
|
eps1lon
approved these changes
Jan 7, 2025
sebmarkbage
force-pushed
the
viewtransitionclass
branch
from
January 8, 2025 17:16
8b91b37 to
a99e5d7
Compare
I did have to rename my classes because there's a bug where generated CSS Module class names can have the `+` character in them which is invalid for View Transition Class Names.
sebmarkbage
force-pushed
the
viewtransitionclass
branch
from
January 8, 2025 18:19
a99e5d7 to
c3c8c34
Compare
github-actions Bot
pushed a commit
that referenced
this pull request
Jan 8, 2025
…lass (#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. DiffTrain build for [3a5496b](3a5496b)
github-actions Bot
pushed a commit
that referenced
this pull request
Jan 8, 2025
…lass (#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. DiffTrain build for [3a5496b](3a5496b)
leochiu-a
added a commit
to leochiu-a/open-resume
that referenced
this pull request
Aug 21, 2026
The rules lived at the bottom of globals.css, four hundred lines from the only component they describe. React intends them to be a module — react/react#31999 introduced the `enter`/`exit` props with `import transitions from './transitions.module.css'` as the motivating usage — and the module earns more than proximity: the two class names are hashed, so `page.tsx` has to reference them through this file's exports instead of spelling them, and `@keyframes` are hashed with them, which is what lets the keyframe be called `fade`. The root rules need `:global(…)`. `root` there names the browser's own snapshot, but CSS Modules treats it as an identifier in a place identifiers get hashed, so written plainly they compile to `::view-transition-old(…__root)` — a group that does not exist. Nothing errors. The rules stop matching, root falls back to the browser's 250ms default, and the flicker they exist to prevent comes back. That form is also what vercel/next.js#77321 landed on, because LightningCSS and PostCSS disagree about whether to hash that selector. The pragma from the same PR is the wrong tool here: it turns localisation off for the whole file, and the two classes depend on it. Reduced motion stays in globals.css. It is not about this transition — it is the document's answer for any of them, including ones added later that would otherwise ship without it. The new spec measures the animation the browser actually ran, because every way this breaks is silent: a dropped `startTransition`, a hash containing `+` (which is not a legal view-transition-class, per that React PR), or a missing `:global(…)` all leave a column that still swaps and a panel that still works. Removing either `:global(…)` fails both tests — root reports 250ms on the way in and is drawn again on the way out.
3 tasks
leochiu-a
added a commit
to leochiu-a/open-resume
that referenced
this pull request
Aug 21, 2026
…cked Code review found the comment in `appearance-transition.module.css` crediting react/react#31999 with the `enter`/`exit` props. That PR introduced `className`; the props came earlier, in #31975. Everything else it is cited for — CSS Modules as the intended home for these classes, the `+`-in-hash hazard — holds. The commit that introduced the error keeps it in history; the file no longer repeats it. Two other comments claimed more than the code did. The type declaration said it catches a misspelled `enter`, which is true of prop names and not of their values: `"auto" | "none" | string` reads as a constraint and collapses to `string`, so the union is gone and the two special values are documented instead. The spec said a skipped transition "fails with the empty array", which was a TypeError on `ran.types` — it now waits for a transition to be recorded and asserts the count, which is also what closes a race the review spotted: both helpers return when the DOM commits, and the DOM commits before `ready` resolves. `changeMode(true)`/`changeMode(false)` said nothing at the call sites, so the two directions are named functions. The CSS module's import was bound to `modeSwap`, naming something other than the file. The duration was written four times across the module and the spec. It is now `--appearance-swap-duration`, declared once, and the spec reads it rather than carrying a copy — which found something: the production build minifies `180ms` to `.18s`, so a test that assumed the unit compares 0.18 against animations measured in milliseconds. `::view-transition-new(root) { animation: none }` was missing. The recipe pairs it with the `display: none` on the old frame, and Chrome happens to create no such animation, so the result rested on that rather than on the rule. Reduced motion is app-wide and shipped with nothing checking it, which for an accessibility rule is the wrong way round — there is a test now. `docs/interface.md` documented the theme wipe as the app's only View Transition and is where AGENTS.md says the editor's layout belongs. It now covers the appearance swap and the reduced-motion policy, and the theme-wipe paragraph no longer implies it is alone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
This doesn't change the HTML
classattribute. It's not a CSS class. Instead it assign theview-transition-classstyle 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
typesor scenarios that can't be described by CSS alone.CSS Modules
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.
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 forview-transition-classand 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?
classNameisn't exactly the most loved property name, however, I'm usingclassNamehere too for consistency. Even though in this case there's no direct equivalent DOM property name. The CSS property is namedviewTransitionClass, 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 asclassNameon a DOM node.But if we ever rename the
classprop then we can do that for this one as well.