Add motion design tokens (duration and easing) to @wordpress/theme#76097
Add motion design tokens (duration and easing) to @wordpress/theme#76097jameskoster wants to merge 14 commits intotrunkfrom
Conversation
|
Size Change: +192 B (0%) Total Size: 7.74 MB
ℹ️ View Unchanged
|
mirka
left a comment
There was a problem hiding this comment.
Here's some context on why I'm hesitant to add motion tokens in this manner.
- If motion is not going to be themed dynamically through
ThemeProvider, it isn't necessary to manage as a official design token. (Which is good for avoiding token bloat in the stylesheet. But @aduth has suggested that we can manage tokens in the theme package as a central "data store", while still omitting them from the tokens stylesheet, so that could be a mitigation option.) - Most motion is already encapsulated at relatively higher levels (e.g. Tooltip/Dialog components, dropdown motion module, focus module). Which is both sufficient and easier for motion sharing across similar components.
- At the moment, there are Framer Motion usages that can't use values from CSS variables (not a blocker per se, but needs some planning on what to do).
So motion is one of those categories where I think we need to weight the cost–benefit. Maybe my intuition of the cost is overblown, and benefit underestimated.
These are just my hesitations — open to everyone's thoughts!
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @nick-a8c. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
I defer to y'all on the implementation :) What's not clear to me is how we'd ensure consistent profiles (easing, duration) across components without tokens. Is that something we'd have to maintain manually?
Could there be a world where it is theme-able? For instance would it make sense for |
I think this strikes at an interesting question about what values make sense to be a design token. This interpretation reads to me as being based on the ability to customize, though I've personally approached it more as encoding design decisions that apply uniformly across components, that we can update in one place and cascade (single source of truth), and that can be used in the design process (e.g. Figma variables) for better code/design parity. In particular, the way these are being proposed feel to me like they aren't tied to any one specific component but apply generally across the system. I'm not as sure about design tooling, but it feels like a value we'd want to expose and use in motion design. Or maybe reframing: What is it about what's said about how we can manage motion through higher-level abstractions that is untrue about how we think about typography or color, for example? |
I guess a large part of my skepticism is about the validity of encoding the motion design decisions at the individual CSS property level.
|
|
Another fun thought that came to mind in support of higher-level encapsulation! Let's say we had an "intensity" setting for motion, where high intensity is very fun and low intensity is more calm. Could this be achieved globally by changing token values at a CSS property level? I'd say no. Each motion module (tooltips, dialogs, etc) needs to tweak itself appropriately for each intensity level, which would probably be switched by entire declaration blocks via a data attribute. |
Doesn't it make sense to handle this via ThemeProvider though, like all other theming aspects? With higher-level encapsulation could motion still be themeable if we introduced and utilised a wider range of motion primitives in the theme package, maybe with modes for different styles? |
|
I see both sides of the argument. Personally, I like the idea that shared variables all come as DS tokens. If we keep the stylesheet around, we don't really have to include these tokens in |
It will be handled by rather than: So while tokens could be beneficial if it's important to "quantize" durations and easings across the ecosystem to a certain set of values, it's not a requirement for overall motion consistency or motion theming. Consistency and theming will need to be addressed at higher levels anyway. |
|
Would it not be useful to have a set of tokens to utilise like so? This would give third parties building custom components a set of tokens to maintain coherent animation profiles with the system (and inherit any updates we make automatically). Or is the point that tokens like Sorry if I'm missing something obvious. |
Yes, if the point is to quantize values across the system, I'm not against that or anything. Let's try it out. |
|
I think the duration tokens are probably okay for a start, but how do you feel about the easing tokens? I'm wondering if there should be more of a scale, e.g. |
I have no idea actually. I think it's something that needs to be factored out of the actual animations that are going to be used? In other words, if we aren't sure yet, it could be fine to leave it out for now and add the tokens when we have a better idea of what is needed. |
|
Heh, I (softly) take the opposite position where the system provides some guardrails that inform what's possible rather than trying to extract tokens from patterns. I worry the latter could result in a lack of cohesion. Because the tokens would be semantic, introducing something lightweight now shouldn’t lock us in. We could start consuming them in a few prominent examples (Modal/Dialog, Menu, etc.) – maybe even in this PR to test – then iterate (and expand the tokens) later as needed. |
|
Sure, I'm fine with that too 👍 |
|
I updated |
d234848 to
93122b3
Compare
|
Flaky tests detected in 84b2bb7. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/23904759173
|
There was a problem hiding this comment.
Probably makes sense to move this file to the root storybook folder so we can just use @wordpress/ui components and avoid a lot of custom CSS?
There was a problem hiding this comment.
Do you mean just this file or the whole story?
There was a problem hiding this comment.
I think I worked out what you meant, and updated accordingly. We'll need to consolidate our separate token folders at some point, but that doesn't need to happen here(?).
If I got it wrong let me know 😅
There was a problem hiding this comment.
Yeah my wording was off. What you did is correct, thanks 😄
| const EASING_TOKENS = [ | ||
| { | ||
| name: 'standard', | ||
| variable: 'var(--wpds-motion-easing-standard)', | ||
| description: 'State changes like hover, color, and toggle transitions.', | ||
| }, | ||
| { | ||
| name: 'decelerate', | ||
| variable: 'var(--wpds-motion-easing-decelerate)', | ||
| description: | ||
| 'Elements entering the screen, such as menus and popovers.', | ||
| }, |
There was a problem hiding this comment.
It isn't great that we need to define these manually when the data is all already available in the token store, but just not fully exposed in @wordpress/theme/design-tokens.js. Not a blocker, but I will see what we can do to make this easier.
There was a problem hiding this comment.
Coincidentally, this ☝️ can also help us with the token usage in all the JS files! We can expose the actual token data, not just the metadata. Win.
There was a problem hiding this comment.
Yeah my wording was off. What you did is correct, thanks 😄
| const EASING_TOKENS = [ | ||
| { | ||
| name: 'standard', | ||
| variable: 'var(--wpds-motion-easing-standard)', | ||
| description: 'State changes like hover, color, and toggle transitions.', | ||
| }, | ||
| { | ||
| name: 'decelerate', | ||
| variable: 'var(--wpds-motion-easing-decelerate)', | ||
| description: | ||
| 'Elements entering the screen, such as menus and popovers.', | ||
| }, |
There was a problem hiding this comment.
Coincidentally, this ☝️ can also help us with the token usage in all the JS files! We can expose the actual token data, not just the metadata. Win.
2f2b871 to
ffcefc6
Compare
|
@aduth do you think we should explore adopting these tokens in |
|
I'd love to hear @nebojsajurcic-a8c's thoughts on the easing curve scale. The values can be tweaked over time, so don't necessarily need to be perfect right now, but they're largely based on existing profiles so should be in a good spot. |
13925e0 to
a3eec5a
Compare
|
Partially related, but one thing that @aduth and I recently discussed is to consider adding a |
I don't have a strong preference. I raise it mostly in case it throws a wrench in how we're thinking about the tokens as presented in this pull request. If we're either reasonably okay with the idea of CollapsibleCard animations becoming longer to align to the proposed token timing, or vice-versa "lg" animation durations being shortened, then it'd be okay to fine-tune those details separately. |
a3eec5a to
48297cf
Compare
|
I reckon this is worth merging. I'm still open to feedback and iterating, but I think the pieces are in place for that. The duration scale feels comprehensive and the values are easily adjusted. Regarding easing, there are really only three fundamental motion scenarios; entering, exiting, and changing state in place, and we have a token for each here. The emphasized pair adds an intensity for the enter/exit cases where prominent elements (dialogs, drawers) need more expressive movement. If a genuine need emerges for a |
|
Not necessarily blocking, but I'd be curious to understand how we picked the easing curves. I recently took an animation course where the author mostly recommends using From the related
This is not to say that we should blindly follow that advice, but I was curious to see that our curves adopt a different approach. |
|
The values are based on the current animation effects used in the Modal and Menu components, both of which have been refined fairly recently. I like the simplicity of applying deceleration consistently to entrance and exit animations, though there’s a potential downside: in directional animations, elements might feel like they’re abruptly stopping rather than moving away naturally. Though in practice this likely isn’t an issue at the moment because I don't think we have any cases where elements remain visible after the animation ends, so any “hard stop” is effectively hidden once the element leaves the viewport or fades out. I suppose we could try using deceleration for Modal and Menu. If we like it then we can remove the acceleration tokens for now and add them back later if we need them. |
|
I gave it a try and while I have a very slight preference for what's on trunk, I think using deceleration for exit animations does feel snappier. Given the difference is almost impossible to notice unless you're really looking for it, and this approach results in fewer tokens overall, it seems worth trying as a start. That said, I also added a |
I’d add one point if it’s still useful: when comparing easing approaches (ease-in, ease-in-out, ease-out), the most effective way is to test them side by side in a single motion study—typically in Rive or After Effects—so differences are clearly visible. Based on the recent comments, I’d encourage this approach rather than relying on predefined curves. This is a nuanced part of the design and benefits from direct comparison. I can run a test next week once I’m back. |
|
I think the most interesting point here is not which exact easing curve to use (which is still valuable and to be investigated), but the fact that we should come up with a set of semantic DS tokens that represent the right abstraction, one that can hold true even if we later tweak the curves. For example, using the words |
|
I noticed in #76912 that Although in general this feels prone to fall out of sync and we likely want a better way to maintain it 🤷 |
Very good question. No I don't think we'd want that, we want names that convey intent. Maybe something like:
Some other options:
Obviously descriptions would help guide consumers about when to use each, but the main point is that we're not tied into any specific mechanism. It's an interesting framework because you might argue that something like |
Introduces standardized duration and easing tokens for consistent animation timing across components. Includes a Storybook story with interactive demos and usage guidance for each token. Made-with: Cursor
Replace hardcoded duration and easing values with --wpds-motion-* design tokens in UI/Dialog, Components/Modal, and UI dropdown motion. Components/Menu retains computed strings due to Emotion compatibility constraints (no-ds-tokens ESLint rule). Made-with: Cursor
Co-authored-by: Lena Morita <lena@jaguchi.com>
Move the motion tokens story out of `packages/theme/src/stories/` and into `storybook/stories/design-system/tokens/` so it can use existing components (`Stack` from `@wordpress/ui`, `Button`/`SelectControl`/ `TextControl` from `@wordpress/components`) instead of hand-rolled CSS. This reduces the CSS module from 124 lines to 42, keeping only the animation-specific styles (track, dot, keyframes, typography). Made-with: Cursor
Co-authored-by: Lena Morita <lena@jaguchi.com>
Use semantic t-shirt size names only and resolve actual values from CSS custom properties at runtime, so labels stay accurate if token values change. Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Renames `emphasized-decelerate` → `decelerate-emphasized` and `emphasized-accelerate` → `accelerate-emphasized` so the direction is the primary axis, making it easier to add further intensity variants (e.g. `decelerate-subtle`) in the future. Made-with: Cursor
…nter/exit - Remove `accelerate` and `accelerate-emphasized` tokens (unused) - Add `gentle` easing token for subtle hover/color/background transitions - Use `decelerate-emphasized` for modal and dialog exit animations (overlay, backdrop, and frame) instead of accelerate variants - Update descriptions to reflect enter/exit usage of decelerate tokens - Refine `standard` description to focus on on-screen movement Made-with: Cursor
The overlay/backdrop is a background opacity transition, not a prominent element entering or exiting, so gentle is a better fit than decelerate-emphasized. Made-with: Cursor
Made-with: Cursor
d66429a to
84b2bb7
Compare
Screen.Recording.2026-03-03.at.16.33.21.mov
What
Adds motion design tokens to
@wordpress/theme— a set of duration and easing curve tokens for standardizing animation timing across components — and adopts them in Dialog, Modal, and Menu/DropdownMenu.Duration tokens
--wpds-motion-duration-xs50ms--wpds-motion-duration-sm100ms--wpds-motion-duration-md200ms--wpds-motion-duration-lg300ms--wpds-motion-duration-xl400msEasing tokens
--wpds-motion-easing-gentlecubic-bezier(0.25, 0.1, 0.25, 1)--wpds-motion-easing-standardcubic-bezier(0.4, 0, 0.2, 1)--wpds-motion-easing-deceleratecubic-bezier(0, 0, 0, 1)--wpds-motion-easing-decelerate-emphasizedcubic-bezier(0.29, 0, 0, 1)Why
Animation timing is currently hardcoded across components with magic numbers. The
Dialogcomponent, for example, uses0.2s cubic-bezier(0.29, 0, 0, 1)for its entrance and0.2s cubic-bezier(1, 0, 0.2, 1)for its exit — values that were chosen thoughtfully but aren't reusable by other components.Centralizing these values as tokens:
Design decisions
Easing model — decelerate for both enter and exit: Rather than providing separate accelerate/decelerate pairs, the system uses decelerating easing for both entrances and exits. This follows the logic that all enter/exit animations should start fast and feel responsive. Accelerating easing (slow start, fast end) can feel jarring for exits like fades and scale-downs, where the element lingers visibly before vanishing abruptly. Decelerating easing front-loads the visual change and finishes gently, which feels smoother for the opacity and scale-based animations used by dialogs, modals, and menus.
Four easing tokens, four roles: The easing tokens map to a simple decision tree:
decelerate(ordecelerate-emphasizedfor prominent elements)standardgentlelinearkeyword (no token needed)Direction-first naming: Easing tokens use a
direction-modifierpattern (e.g.decelerate-emphasizedrather thanemphasized-decelerate) so that the direction is the primary axis. This groups related tokens alphabetically and leaves room for future intensity variants (e.g.decelerate-subtle) without ad hoc naming.Duration scale: The five-step
xs–xlscale provides enough granularity without being overwhelming. The descriptions intentionally avoid prescribing specific components to specific durations, since the right choice depends on the element's size, prominence, and context.How
Token definition
packages/theme/tokens/motion.json(DTCG format, withdurationandcubicBeziertypes)terrazzo.config.tsto include the new file and generateDurationSizeandEasingTypeScript typesmotion.story.tsx) with interactive animation demosToken adoption
UI/Dialog (
packages/ui/src/dialog/style.module.css): Replaced all hardcoded duration+easing pairs. The backdrop uses--wpds-motion-easing-gentle(background opacity transition), while the popup uses--wpds-motion-easing-decelerate-emphasizedfor both enter and exit (prominent element).Components/Modal (
packages/components/src/modal/):--wpds-motion-easing-gentle(background opacity transition).--wpds-motion-easing-decelerate-emphasized(prominent element).--modal-frame-animation-durationcustom property with--wpds-motion-duration-mdapplied directly in CSS, removingframeStylefrom the exit animation hook and the component.@wordpress/base-stylesfade mixins with local keyframes using--wpds-motion-duration-smfor the backdrop fade.Components/Menu and DropdownMenu (
packages/components/src/utils/dropdown-motion.ts,packages/ui/src/utils/css/dropdown-motion.module.css):dropdown-motion.module.css) now uses--wpds-motion-duration-md,--wpds-motion-easing-decelerate, and--wpds-motion-duration-smtokens directly.dropdown-motion.ts) retain hardcoded values because theno-ds-tokensESLint rule prevents--wpds-*usage inpackages/components/src/(incompatible with Emotion's build-time fallback injection). A comment documents the constraint and notes that values should stay in sync with the tokens.Testing
npm run --workspace @wordpress/theme buildto verify the token build succeedsdesign-tokens.css