Sample the springs over M3's web durations, and add the Standard scheme
bin/springs.mjs is the sampler behind motion.css: damping ratio and stiffness in, the 49-point linear() and the settle time out. Run with --settle it reproduces the six curves the file carried byte for byte, so only the window changed. Each spring is now sampled over the duration M3 publishes for the web (spatial 350/500/650 ms, effects 150/200/300 ms) instead of stopping at its own settle time, so the *-duration tokens read Google's numbers. Sampling stays in real time — point i is the spring at duration × i / 48 — so the bounce lands at the same millisecond as before; a longer duration just holds the tail flat at 1, and a shorter one pins the last point, at most 0.3% of the travel in one frame. Each token's comment records damping, stiffness, settle time and sampled duration. M3's second motion scheme is here too: [data-motion="standard"] swaps the three spatial springs (damping 0.9, 0.2% overshoot instead of 9%) and their durations; the effects springs sample identically in both schemes, so they are shared. The reduced-motion block names both selectors, so it still zeroes every duration. Plan: docs/plans/material-3-alignment.md step 5, findings core C8, C9. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
2feb623ec0
commit
5b0ca26857
@@ -84,7 +84,7 @@ Tailwind's default palette is cleared: every colour class names an M3 role. `tex
|
||||
- Type: `type-{display|headline|title|body|label}-{lg|md|sm}` and `type-emphasized-…` — M3's 15 styles and their emphasized twins, one weight step heavier, rounder, with their own tracking. Never assemble `text-*`, `leading-*` and `tracking-*` by hand; a utility carries size, line height and tracking together. Emphasis is deliberate and one element at a time (M3 asks for it on badges, primary buttons, selected rows, headlines), so a regular utility inside an emphasized one goes back to plain. The font is Google Sans Flex (`font-sans`).
|
||||
- Shape: `rounded-corner-{none|xs|sm|md|lg|lg-increased|xl|xl-increased|xxl|full}`.
|
||||
- Elevation: `shadow-elevation-{1…5}` — for what floats over content, not for panels (a panel separates by its container tone).
|
||||
- Motion: `ease-spatial-{fast|default|slow}` (position, size, shape; springs that overshoot) and `ease-effects-{fast|default|slow}` (colour, opacity). Always pair an easing with its duration: `duration-(--md-sys-motion-spatial-fast-duration) ease-spatial-fast`. Reduced motion zeroes the durations.
|
||||
- Motion: `ease-spatial-{fast|default|slow}` (position, size, shape; springs that overshoot) and `ease-effects-{fast|default|slow}` (colour, opacity, which never overshoot). Always pair an easing with its duration: `duration-(--md-sys-motion-spatial-fast-duration) ease-spatial-fast` — M3's published web durations, spatial 350/500/650 ms and effects 150/200/300 ms. `motion.scheme` in the config picks `expressive` (the default, with the bounce) or `standard` (minimal bounce), which the head script writes to `<html data-motion>` and which swaps the three spatial springs; a component names a spring, never a scheme. Reduced motion zeroes every duration in both schemes.
|
||||
- States: `state-layer` (M3's hover/focus/press overlay; makes the element `relative` and `isolate`), `focus-ring` (keyboard focus indicator), `link` (a link in running text).
|
||||
- `dark:` follows the page's theme (`data-theme`), not the operating system.
|
||||
- `x-figure` on an element holding one number counts it up on first appearance and on change.
|
||||
|
||||
@@ -1,53 +1,69 @@
|
||||
/*
|
||||
* Motion, as M3 Expressive's springs.
|
||||
* Motion, as M3's springs.
|
||||
*
|
||||
* M3 Expressive moves on physics, not on a duration and a curve: every transition is a
|
||||
* spring with a damping ratio and a stiffness. CSS cannot run a spring, so each one is
|
||||
* sampled into a `linear()` easing over the time it takes to settle (to within 0.1% of its
|
||||
* travel) — 49 points, past the resolution anyone can see at 60 Hz. The constants are
|
||||
* androidx Compose Material 3's own (tokens/ExpressiveMotionTokens.kt, Apache-2.0):
|
||||
* sampled into a `linear()` easing — 49 points, past the resolution anyone can see at 60 Hz —
|
||||
* by bin/springs.mjs (`npm run build:springs`), which carries the maths and the derivation.
|
||||
* The constants are androidx Compose Material 3's own ({Expressive,Standard}MotionTokens.kt,
|
||||
* Apache-2.0, © Google LLC, androidx-main @ 1608250):
|
||||
*
|
||||
* spatial — position, size, shape. Underdamped, so the fast one overshoots by 9% and
|
||||
* settles back: that small bounce is what reads as Expressive.
|
||||
* spatial — position, size, shape. Underdamped, so the Expressive fast one overshoots by 9%
|
||||
* and settles back: that small bounce is what reads as Expressive.
|
||||
* effects — colour and opacity. Critically damped; a colour must never overshoot.
|
||||
*
|
||||
* Each spring is sampled over the duration M3 publishes for the web
|
||||
* (docs/reference/m3/styles.md § Motion, "Web curve equivalents"), so the `*-duration` tokens
|
||||
* read Google's numbers: spatial 350/500/650 ms, effects 150/200/300 ms. Sampling is in real
|
||||
* time — point i is the spring's value at `duration × i / 48` — so the bounce lands at the same
|
||||
* millisecond whatever the duration is. Where the duration outlasts the spring the tail is flat
|
||||
* at 1; where it is shorter the final point is pinned to 1, which closes at most 0.3% of the
|
||||
* travel in the last frame. Each token's comment records damping, stiffness, the settle time
|
||||
* (when the spring stays within 0.1% of its travel) and the duration it was sampled over.
|
||||
*
|
||||
* Use a pair together, easing and duration, or the curve is stretched over the wrong time
|
||||
* and the bounce lands late:
|
||||
*
|
||||
* transition: transform var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast);
|
||||
* class="transition-transform duration-(--md-sys-motion-spatial-fast-duration) ease-spatial-fast"
|
||||
*
|
||||
* M3 has two motion schemes. Expressive is the default here and the values above; Standard
|
||||
* ("minimal bounce, for utilitarian products") swaps the three spatial springs when the theme
|
||||
* script writes `data-motion="standard"` on <html>. Its effects springs are the same ones, so
|
||||
* only spatial is overridden. A component never names a scheme: it names a spring.
|
||||
*
|
||||
* The cubic-bezier easings below are M3's non-spring set (@material/web token values,
|
||||
* Apache-2.0, © Google LLC), for the few places a duration is fixed by something outside
|
||||
* the page — a view transition, an animated scroll.
|
||||
*
|
||||
* Reduced motion zeroes every duration here, so anything that animates through these tokens
|
||||
* turns instant with no per-component check; anything that animates without them is a bug.
|
||||
* Reduced motion zeroes every duration here, both schemes, so anything that animates through
|
||||
* these tokens turns instant with no per-component check; anything that animates without them
|
||||
* is a bug.
|
||||
*/
|
||||
:root {
|
||||
/* damping 0.6, stiffness 800; settles in 360ms, peaks at 1.094 */
|
||||
--md-sys-motion-spatial-fast: linear(0, 0.0206, 0.0754, 0.1544, 0.2492, 0.3524, 0.458, 0.5613, 0.6588, 0.7479, 0.8269, 0.895, 0.952, 0.9981, 1.0338, 1.0603, 1.0783, 1.0893, 1.0942, 1.0943, 1.0906, 1.0842, 1.0759, 1.0665, 1.0565, 1.0465, 1.037, 1.0281, 1.0201, 1.0131, 1.0072, 1.0023, 0.9984, 0.9955, 0.9934, 0.992, 0.9912, 0.991, 0.9912, 0.9917, 0.9924, 0.9932, 0.9942, 0.9951, 0.996, 0.9969, 0.9977, 0.9984, 1);
|
||||
--md-sys-motion-spatial-fast-duration: 360ms;
|
||||
/* damping 0.6, stiffness 800; settles in 360ms; peaks at 1.095 at 139ms; sampled over 350ms */
|
||||
--md-sys-motion-spatial-fast: linear(0, 0.0195, 0.0716, 0.1471, 0.2381, 0.3378, 0.4404, 0.5416, 0.6378, 0.7265, 0.806, 0.8754, 0.9342, 0.9826, 1.0211, 1.0503, 1.0713, 1.0849, 1.0924, 1.0948, 1.0931, 1.0882, 1.0812, 1.0726, 1.0632, 1.0534, 1.0438, 1.0347, 1.0263, 1.0187, 1.0121, 1.0064, 1.0018, 0.9981, 0.9953, 0.9933, 0.992, 0.9913, 0.991, 0.9912, 0.9916, 0.9923, 0.9931, 0.994, 0.9949, 0.9958, 0.9967, 0.9975, 1);
|
||||
--md-sys-motion-spatial-fast-duration: 350ms;
|
||||
|
||||
/* damping 0.8, stiffness 380; settles in 440ms, peaks at 1.015 */
|
||||
--md-sys-motion-spatial-default: linear(0, 0.0145, 0.0527, 0.1076, 0.1736, 0.2461, 0.3214, 0.3968, 0.4702, 0.54, 0.6051, 0.665, 0.7193, 0.7679, 0.8108, 0.8483, 0.8807, 0.9083, 0.9316, 0.951, 0.9669, 0.9798, 0.99, 0.998, 1.004, 1.0085, 1.0116, 1.0136, 1.0147, 1.0151, 1.0151, 1.0146, 1.0138, 1.0128, 1.0118, 1.0106, 1.0095, 1.0084, 1.0073, 1.0063, 1.0053, 1.0045, 1.0037, 1.0031, 1.0025, 1.002, 1.0015, 1.0011, 1);
|
||||
--md-sys-motion-spatial-default-duration: 440ms;
|
||||
/* damping 0.8, stiffness 380; settles in 440ms; peaks at 1.015 at 269ms; sampled over 500ms */
|
||||
--md-sys-motion-spatial-default: linear(0, 0.0185, 0.0663, 0.1336, 0.2126, 0.2973, 0.3832, 0.4669, 0.5461, 0.6192, 0.6854, 0.7443, 0.7958, 0.8402, 0.8779, 0.9094, 0.9354, 0.9564, 0.9731, 0.9861, 0.996, 1.0033, 1.0085, 1.0119, 1.014, 1.015, 1.0152, 1.0148, 1.014, 1.0129, 1.0117, 1.0104, 1.0091, 1.0078, 1.0066, 1.0055, 1.0046, 1.0037, 1.0029, 1.0023, 1.0017, 1.0013, 1.0009, 1.0006, 1.0004, 1.0002, 1, 0.9999, 1);
|
||||
--md-sys-motion-spatial-default-duration: 500ms;
|
||||
|
||||
/* damping 0.8, stiffness 200; settles in 600ms, peaks at 1.015 */
|
||||
--md-sys-motion-spatial-slow: linear(0, 0.0142, 0.0517, 0.1057, 0.1706, 0.2421, 0.3166, 0.3912, 0.464, 0.5334, 0.5984, 0.6583, 0.7127, 0.7615, 0.8047, 0.8426, 0.8755, 0.9036, 0.9274, 0.9473, 0.9638, 0.9771, 0.9878, 0.9962, 1.0027, 1.0074, 1.0108, 1.0131, 1.0144, 1.0151, 1.0151, 1.0148, 1.0141, 1.0132, 1.0122, 1.0111, 1.0099, 1.0088, 1.0077, 1.0067, 1.0057, 1.0049, 1.0041, 1.0034, 1.0027, 1.0022, 1.0017, 1.0013, 1);
|
||||
--md-sys-motion-spatial-slow-duration: 600ms;
|
||||
/* damping 0.8, stiffness 200; settles in 600ms; peaks at 1.015 at 370ms; sampled over 650ms */
|
||||
--md-sys-motion-spatial-slow: linear(0, 0.0166, 0.0597, 0.1211, 0.1939, 0.273, 0.354, 0.434, 0.5107, 0.5826, 0.6487, 0.7084, 0.7615, 0.8081, 0.8484, 0.8829, 0.912, 0.9361, 0.9559, 0.9719, 0.9845, 0.9943, 1.0017, 1.0071, 1.0108, 1.0132, 1.0146, 1.0151, 1.0151, 1.0145, 1.0137, 1.0126, 1.0114, 1.0102, 1.009, 1.0078, 1.0067, 1.0057, 1.0047, 1.0039, 1.0031, 1.0025, 1.0019, 1.0015, 1.0011, 1.0008, 1.0005, 1.0003, 1);
|
||||
--md-sys-motion-spatial-slow-duration: 650ms;
|
||||
|
||||
/* damping 1.0, stiffness 3800; settles in 150ms */
|
||||
/* damping 1.0, stiffness 3800; settles in 150ms; sampled over 150ms */
|
||||
--md-sys-motion-effects-fast: linear(0, 0.0163, 0.0576, 0.1147, 0.1807, 0.2507, 0.3214, 0.3902, 0.4558, 0.5172, 0.5737, 0.6253, 0.6718, 0.7136, 0.7508, 0.7837, 0.8128, 0.8383, 0.8606, 0.8801, 0.897, 0.9117, 0.9244, 0.9353, 0.9448, 0.9529, 0.9599, 0.9658, 0.9709, 0.9753, 0.979, 0.9822, 0.9849, 0.9872, 0.9892, 0.9909, 0.9923, 0.9935, 0.9945, 0.9954, 0.9961, 0.9967, 0.9972, 0.9977, 0.998, 0.9983, 0.9986, 0.9988, 1);
|
||||
--md-sys-motion-effects-fast-duration: 150ms;
|
||||
|
||||
/* damping 1.0, stiffness 1600; settles in 240ms */
|
||||
--md-sys-motion-effects-default: linear(0, 0.0175, 0.0616, 0.1219, 0.1912, 0.2642, 0.3374, 0.4082, 0.4751, 0.5372, 0.594, 0.6454, 0.6916, 0.7326, 0.7689, 0.8009, 0.8288, 0.8532, 0.8743, 0.8926, 0.9084, 0.922, 0.9337, 0.9437, 0.9523, 0.9596, 0.9658, 0.9711, 0.9756, 0.9794, 0.9826, 0.9854, 0.9877, 0.9897, 0.9913, 0.9927, 0.9939, 0.9949, 0.9957, 0.9964, 0.997, 0.9975, 0.9979, 0.9982, 0.9985, 0.9988, 0.999, 0.9991, 1);
|
||||
--md-sys-motion-effects-default-duration: 240ms;
|
||||
/* damping 1.0, stiffness 1600; settles in 240ms; sampled over 200ms */
|
||||
--md-sys-motion-effects-default: linear(0, 0.0124, 0.0446, 0.0902, 0.1443, 0.2032, 0.2642, 0.3253, 0.3849, 0.4422, 0.4963, 0.547, 0.594, 0.6372, 0.6768, 0.7127, 0.7452, 0.7745, 0.8009, 0.8244, 0.8454, 0.8641, 0.8807, 0.8954, 0.9084, 0.9199, 0.93, 0.9389, 0.9467, 0.9536, 0.9596, 0.9648, 0.9694, 0.9734, 0.9769, 0.98, 0.9826, 0.985, 0.987, 0.9887, 0.9902, 0.9916, 0.9927, 0.9937, 0.9946, 0.9953, 0.9959, 0.9965, 1);
|
||||
--md-sys-motion-effects-default-duration: 200ms;
|
||||
|
||||
/* damping 1.0, stiffness 800; settles in 330ms */
|
||||
--md-sys-motion-effects-slow: linear(0, 0.0166, 0.0586, 0.1165, 0.1833, 0.254, 0.3253, 0.3947, 0.4606, 0.5221, 0.5788, 0.6303, 0.6768, 0.7184, 0.7554, 0.7881, 0.8169, 0.8421, 0.8641, 0.8833, 0.8999, 0.9144, 0.9268, 0.9375, 0.9467, 0.9546, 0.9614, 0.9672, 0.9722, 0.9764, 0.98, 0.9831, 0.9857, 0.9879, 0.9898, 0.9914, 0.9927, 0.9939, 0.9948, 0.9956, 0.9963, 0.9969, 0.9974, 0.9978, 0.9982, 0.9985, 0.9987, 0.9989, 1);
|
||||
--md-sys-motion-effects-slow-duration: 330ms;
|
||||
/* damping 1.0, stiffness 800; settles in 330ms; sampled over 300ms */
|
||||
--md-sys-motion-effects-slow: linear(0, 0.0139, 0.0496, 0.0995, 0.1583, 0.2216, 0.2865, 0.3509, 0.4131, 0.4722, 0.5275, 0.5788, 0.6258, 0.6687, 0.7075, 0.7424, 0.7737, 0.8016, 0.8264, 0.8484, 0.8678, 0.8849, 0.8999, 0.9131, 0.9247, 0.9347, 0.9435, 0.9512, 0.9578, 0.9636, 0.9686, 0.973, 0.9767, 0.98, 0.9828, 0.9852, 0.9873, 0.9891, 0.9907, 0.992, 0.9931, 0.9941, 0.995, 0.9957, 0.9963, 0.9969, 0.9973, 0.9977, 1);
|
||||
--md-sys-motion-effects-slow-duration: 300ms;
|
||||
|
||||
--md-sys-motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
--md-sys-motion-easing-standard-accelerate: cubic-bezier(0.3, 0, 1, 1);
|
||||
@@ -60,8 +76,29 @@
|
||||
--md-sys-motion-duration-long: 500ms;
|
||||
}
|
||||
|
||||
/*
|
||||
* The Standard scheme: the same three spatial springs at a damping ratio of 0.9, which leaves
|
||||
* a 0.2% overshoot instead of Expressive's 9% — motion that eases into place rather than
|
||||
* bouncing. Google's web durations for them are 350/500/750 ms; the effects springs, the
|
||||
* legacy easings and every other token are the ones above.
|
||||
*/
|
||||
[data-motion="standard"] {
|
||||
/* damping 0.9, stiffness 1400; settles in 230ms; peaks at 1.002 at 193ms; sampled over 350ms */
|
||||
--md-sys-motion-spatial-fast: linear(0, 0.0316, 0.1076, 0.2062, 0.313, 0.4185, 0.517, 0.6056, 0.6828, 0.7486, 0.8036, 0.8487, 0.8852, 0.9142, 0.937, 0.9546, 0.968, 0.9781, 0.9856, 0.991, 0.9948, 0.9975, 0.9992, 1.0004, 1.001, 1.0014, 1.0015, 1.0015, 1.0014, 1.0013, 1.0011, 1.001, 1.0008, 1.0007, 1.0005, 1.0004, 1.0003, 1.0003, 1.0002, 1.0001, 1.0001, 1.0001, 1.0001, 1, 1, 1, 1, 1, 1);
|
||||
--md-sys-motion-spatial-fast-duration: 350ms;
|
||||
|
||||
/* damping 0.9, stiffness 700; settles in 320ms; peaks at 1.002 at 272ms; sampled over 500ms */
|
||||
--md-sys-motion-spatial-default: linear(0, 0.0322, 0.1094, 0.2094, 0.3173, 0.4237, 0.5227, 0.6114, 0.6886, 0.7541, 0.8086, 0.8532, 0.8891, 0.9175, 0.9397, 0.9569, 0.9699, 0.9796, 0.9867, 0.9918, 0.9954, 0.9979, 0.9995, 1.0006, 1.0011, 1.0014, 1.0015, 1.0015, 1.0014, 1.0012, 1.0011, 1.0009, 1.0007, 1.0006, 1.0005, 1.0004, 1.0003, 1.0002, 1.0002, 1.0001, 1.0001, 1.0001, 1, 1, 1, 1, 1, 1, 1);
|
||||
--md-sys-motion-spatial-default-duration: 500ms;
|
||||
|
||||
/* damping 0.9, stiffness 300; settles in 490ms; peaks at 1.002 at 416ms; sampled over 750ms */
|
||||
--md-sys-motion-spatial-slow: linear(0, 0.0311, 0.1061, 0.2037, 0.3095, 0.4143, 0.5125, 0.6009, 0.6782, 0.7442, 0.7995, 0.8451, 0.882, 0.9115, 0.9347, 0.9527, 0.9665, 0.9769, 0.9846, 0.9903, 0.9943, 0.9971, 0.999, 1.0002, 1.0009, 1.0013, 1.0015, 1.0015, 1.0014, 1.0013, 1.0012, 1.001, 1.0008, 1.0007, 1.0006, 1.0005, 1.0004, 1.0003, 1.0002, 1.0002, 1.0001, 1.0001, 1.0001, 1, 1, 1, 1, 1, 1);
|
||||
--md-sys-motion-spatial-slow-duration: 750ms;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:root {
|
||||
:root,
|
||||
[data-motion="standard"] {
|
||||
--md-sys-motion-spatial-fast-duration: 0ms;
|
||||
--md-sys-motion-spatial-default-duration: 0ms;
|
||||
--md-sys-motion-spatial-slow-duration: 0ms;
|
||||
|
||||
Reference in New Issue
Block a user