feat(motion): add interactive micro-feedback

This commit is contained in:
2026-03-20 17:44:20 +08:00
parent 142f4a399a
commit 36822f05e0
17 changed files with 144 additions and 62 deletions
+8 -8
View File
@@ -18,19 +18,19 @@ export const themeDetails = {
}
} as const satisfies Record<ThemeName, { label: string; note: string }>;
export const motionModeNames = ["default", "reduced"] as const;
export const motionModeNames = ["interactive", "static"] as const;
export type MotionModeName = (typeof motionModeNames)[number];
export const defaultMotionMode: MotionModeName = "default";
export const defaultMotionMode: MotionModeName = "interactive";
export const motionModeDetails = {
default: {
label: "Default",
note: "Standard Cadence UI motion for hover, press, overlays, and hierarchy"
interactive: {
label: "Interactive",
note: "Micro-interactions with hover lift, press feedback, focus transitions, and animated state changes"
},
reduced: {
label: "Reduced",
note: "Collapse durations, distances, and animated feedback"
static: {
label: "Static",
note: "Keep visual states readable while removing motion-heavy feedback and animation"
}
} as const satisfies Record<MotionModeName, { label: string; note: string }>;
+36 -21
View File
@@ -1,15 +1,15 @@
:root,
:root[data-motion="default"],
[data-motion="default"] {
:root[data-motion="interactive"],
[data-motion="interactive"] {
--dur-instant: 1ms;
--dur-fast: 120ms;
--dur-fast: 140ms;
--dur-base: 200ms;
--dur-slow: 320ms;
--dur-deliberate: 460ms;
--dur-slow: 280ms;
--dur-deliberate: 300ms;
--ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
--ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
--ease-exit: cubic-bezier(0.4, 0, 1, 1);
--ease-standard: cubic-bezier(0.25, 1, 0.5, 1);
--ease-emphasized: cubic-bezier(0.22, 1, 0.36, 1);
--ease-exit: cubic-bezier(0.3, 1, 0.5, 1);
--distance-xs: 4px;
--distance-sm: 8px;
@@ -21,8 +21,8 @@
--scale-pop: 1.02;
}
:root[data-motion="reduced"],
[data-motion="reduced"] {
:root[data-motion="static"],
[data-motion="static"] {
--dur-instant: 1ms;
--dur-fast: 1ms;
--dur-base: 1ms;
@@ -38,12 +38,12 @@
scroll-behavior: auto;
}
:root[data-motion="reduced"] *,
:root[data-motion="reduced"] *::before,
:root[data-motion="reduced"] *::after,
[data-motion="reduced"] *,
[data-motion="reduced"] *::before,
[data-motion="reduced"] *::after {
:root[data-motion="static"] *,
:root[data-motion="static"] *::before,
:root[data-motion="static"] *::after,
[data-motion="static"] *,
[data-motion="static"] *::before,
[data-motion="static"] *::after {
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
@@ -113,17 +113,32 @@
}
.motion-pressable {
transition-duration: var(--dur-fast);
transition-duration: var(--dur-base);
transition-property: color, background-color, border-color, box-shadow, transform;
transition-timing-function: var(--ease-standard);
transition-timing-function: var(--ease-emphasized);
will-change: transform, box-shadow;
}
.motion-pressable:hover {
transform: translateY(calc(var(--distance-xs) * -0.25)) scale(var(--scale-hover));
@media (hover: hover) {
.motion-pressable:hover {
transform: translateY(var(--ui-button-hover-translate, -1px))
scale(var(--ui-button-hover-scale, 1.02));
box-shadow: var(--ui-button-hover-shadow, var(--shadow-sm));
}
}
:root[data-motion="static"] .motion-pressable:hover,
[data-motion="static"] .motion-pressable:hover,
:root[data-motion="static"] .motion-pressable:active,
[data-motion="static"] .motion-pressable:active {
box-shadow: inherit;
transform: none;
}
.motion-pressable:active {
transform: scale(var(--scale-press));
transform: translateY(0) scale(var(--ui-button-press-scale, var(--scale-press)));
box-shadow: var(--ui-button-active-shadow, var(--shadow-xs));
transition-duration: var(--dur-fast);
}
.motion-enter-fade {