diff --git a/src/admin/theme.test.ts b/src/admin/theme.test.ts index 7b7f6cc..4ea2e8c 100644 --- a/src/admin/theme.test.ts +++ b/src/admin/theme.test.ts @@ -49,4 +49,13 @@ describe("MUI theme", () => { const card = theme.components?.MuiCard?.styleOverrides?.root as any; expect(card?.borderRadius).toBe(16); }); + + it("adds 200-300ms interaction motion (press + focus ring)", () => { + const btn = theme.components?.MuiButton?.styleOverrides?.root as any; + expect(btn?.transition).toContain("transform"); + expect(btn?.["&:active"]?.transform).toBe("scale(0.97)"); + const input = theme.components?.MuiOutlinedInput?.styleOverrides + ?.root as any; + expect(input?.["&.Mui-focused"]?.boxShadow).toContain("rgba"); + }); }); diff --git a/src/admin/theme.ts b/src/admin/theme.ts index afc5dc3..97ece8b 100644 --- a/src/admin/theme.ts +++ b/src/admin/theme.ts @@ -4,6 +4,9 @@ const FONT_BODY = "'Plus Jakarta Sans', system-ui, sans-serif"; const FONT_HEADING = "'Urbanist', sans-serif"; const FONT_MONO = "'DM Mono', Menlo, monospace"; +// Standard Material easing (matches the legacy --transition cubic-bezier). +const EASE = "cubic-bezier(0.4, 0, 0.2, 1)"; + export const theme = createTheme({ cssVariables: { colorSchemeSelector: "[data-theme='%s']", @@ -51,10 +54,31 @@ export const theme = createTheme({ MuiButton: { defaultProps: { disableElevation: true }, styleOverrides: { - root: { borderRadius: 999, paddingInline: "0.95rem" }, + root: { + borderRadius: 999, + paddingInline: "0.95rem", + // Snappy press (150ms) + smooth color/shadow (250ms). + transition: [ + `background-color 250ms ${EASE}`, + `border-color 250ms ${EASE}`, + `color 200ms ${EASE}`, + `box-shadow 250ms ${EASE}`, + `transform 150ms ${EASE}`, + `filter 200ms ${EASE}`, + ].join(", "), + "&:active": { transform: "scale(0.97)" }, + "@media (prefers-reduced-motion: reduce)": { transition: "none" }, + }, containedPrimary: { backgroundImage: "linear-gradient(135deg, #e23a3a, #c01f1f)", boxShadow: "0 5px 14px rgba(214,48,49,0.32)", + // The gradient can't transition, so animate lift + glow instead. + "&:hover": { + transform: "translateY(-1px)", + filter: "brightness(1.04)", + boxShadow: "0 8px 20px rgba(214,48,49,0.42)", + }, + "&:active": { transform: "translateY(0) scale(0.97)" }, }, }, }, @@ -64,11 +88,33 @@ export const theme = createTheme({ borderRadius: 16, boxShadow: "0 6px 20px rgba(20,20,40,0.06), 0 1px 2px rgba(0,0,0,0.03)", + transition: `box-shadow 250ms ${EASE}, transform 250ms ${EASE}`, }, }, }, MuiChip: { - styleOverrides: { root: { borderRadius: 999, fontWeight: 700 } }, + styleOverrides: { + root: { + borderRadius: 999, + fontWeight: 700, + transition: `background-color 200ms ${EASE}, box-shadow 200ms ${EASE}`, + }, + }, + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + // Soft brand focus ring fades in over 200ms. + transition: `box-shadow 200ms ${EASE}`, + "&.Mui-focused": { + boxShadow: "0 0 0 3px rgba(199, 48, 48, 0.12)", + }, + "@media (prefers-reduced-motion: reduce)": { transition: "none" }, + }, + notchedOutline: { + transition: `border-color 200ms ${EASE}`, + }, + }, }, }, });