Skip to main content

Material Expressive: Google's Bold New Design System for 2026

March 24, 2026

</>

Google has shipped the biggest update to Material Design since Material You in 2021. Material Expressive is not an incremental revision — it is a philosophical shift. Where Material You was about personalization through color, Material Expressive is about feeling. Components now respond with physics-based motion, surfaces have tactile depth, and the entire system is built around what Google calls "sensory design."

Let me break down what changed, what it means for your projects, and how to actually use it.

What Changed from Material 3

Material 3 (Material You) gave us dynamic color theming, updated typography scales, and refreshed components. Material Expressive keeps all of that and adds three major layers:

1. Dynamic Motion System

Every component now has a defined motion personality. A button does not just change color on press — it physically responds. A card does not just appear — it enters the viewport with momentum and settles with spring physics.

Material 3 had motion guidelines, but they were suggestions. Material Expressive makes motion a first-class design token:

TokenMaterial 3Material Expressive
--md-sys-motion-duration-short100msContext-aware (50-150ms)
--md-sys-motion-duration-medium300msContext-aware (200-400ms)
--md-sys-motion-easing-standardcubic-bezier(0.2, 0, 0, 1)Spring-based with damping ratio
--md-sys-motion-easing-emphasizedcubic-bezier(0.2, 0, 0, 1)Spring with overshoot
--md-sys-motion-pathN/AArc motion paths for spatial transitions

The spring-based easing is the biggest shift. Instead of fixed cubic-bezier curves, components use spring physics defined by stiffness, damping, and mass:

/* Material Expressive spring tokens */
:root {
  /* Responsive spring — snappy, no overshoot */
  --md-sys-motion-spring-responsive: linear(
    0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%,
    0.938 16.7%, 1.017 19.1%, 1.067, 1.099 24%, 1.114 25.7%,
    1.115 27.5%, 1.097 30.2%, 1.033 38.3%, 1.011 43.1%,
    0.998 53.5%, 1 63.8%, 1.002
  );

  /* Expressive spring — bouncy, slight overshoot */
  --md-sys-motion-spring-expressive: linear(
    0, 0.006, 0.025 2.8%, 0.101 6%, 0.413 14%,
    0.64 18.8%, 0.955 28.2%, 1.05 33.2%, 1.088 36%,
    1.103 38.7%, 1.102 41.6%, 1.084 45.3%, 1.015 56.8%,
    0.997 63.5%, 0.994 72%, 1.001 85.5%, 1
  );

  /* Spatial spring — for elements that move across the screen */
  --md-sys-motion-spring-spatial: linear(
    0, 0.005, 0.02 3.2%, 0.082 6.8%, 0.337 15.6%,
    0.521 20.8%, 0.816 30.2%, 0.944 36.6%, 1.008 42.2%,
    1.041 47%, 1.05 52.2%, 1.038 58.4%, 1.007 70.2%,
    0.999 78%, 1
  );
}

The linear() function approximates spring physics in pure CSS — no JavaScript needed. These tokens replace the old cubic-bezier curves across the entire component library.

2. Tactile Surfaces

Material Design always had elevation (shadows). Material Expressive replaces flat shadows with what Google calls "tactile surfaces" — components that feel like they have physical depth and material properties.

The key change: surfaces now respond to interaction with dimensional feedback.

/* Material Expressive button — tactile press response */
.md-button {
  /* Rest state */
  transform: translateZ(0);
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.1),
    0 1px 3px rgba(0, 0, 0, 0.08);
  transition: transform 0.1s var(--md-sys-motion-spring-responsive),
              box-shadow 0.1s var(--md-sys-motion-spring-responsive);
}

.md-button:hover {
  /* Lift on hover */
  transform: translateY(-1px) translateZ(0);
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.12),
    0 4px 8px rgba(0, 0, 0, 0.08);
}

.md-button:active {
  /* Press into surface */
  transform: translateY(1px) translateZ(0);
  box-shadow:
    0 0px 1px rgba(0, 0, 0, 0.12),
    0 1px 2px rgba(0, 0, 0, 0.06);
  transition-duration: 0.05s;
}

This is a departure from Material 3 where buttons used ripple effects and state layers. Material Expressive keeps ripples but adds physical displacement — the button moves toward you on hover and into the surface on press.

3. Sensory Feedback Layer

Material Expressive introduces a new concept: components can request haptic feedback, sound, and visual pulse effects coordinated through a single API.

// Material Expressive feedback API
import { FeedbackController } from '@material/web/feedback';

const feedback = new FeedbackController({
  haptic: true,       // Request device haptics
  sound: false,       // Optional UI sounds
  visual: true,       // Ripple + pulse effects
});

// Attach to a component
const button = document.querySelector('md-filled-button');
feedback.attach(button, {
  onPress: {
    haptic: 'light',              // Light haptic tap
    visual: 'ripple',             // Classic Material ripple
    duration: 'responsive',       // Auto-calculated duration
  },
  onLongPress: {
    haptic: 'medium',
    visual: 'pulse',              // New: radial pulse effect
    duration: 'expressive',
  },
});

On devices with haptic engines (phones, some laptops), buttons produce a physical sensation. On devices without haptics, the visual feedback compensates with more pronounced motion.

Using Material Expressive in React

Google released @material/web v3 with Material Expressive support. Here is how to integrate it in a React project.

Installation

npm install @material/web@latest

Basic Component Usage

Material Web components are web components, so they work in React with standard element syntax:

import '@material/web/button/filled-button.js';
import '@material/web/button/outlined-button.js';
import '@material/web/textfield/filled-text-field.js';
import '@material/web/card/card.js';

function SignUpForm() {
  return (
    <form className="max-w-md mx-auto space-y-6 p-8">
      <md-card appearance="filled">
        <div className="p-6 space-y-4">
          <h2 className="text-2xl font-semibold">Create Account</h2>

          <md-filled-text-field
            label="Email"
            type="email"
            required
            style={{ width: '100%' }}
          />

          <md-filled-text-field
            label="Password"
            type="password"
            required
            style={{ width: '100%' }}
          />

          <div className="flex gap-3 pt-2">
            <md-filled-button type="submit">
              Sign Up
            </md-filled-button>
            <md-outlined-button type="button">
              Cancel
            </md-outlined-button>
          </div>
        </div>
      </md-card>
    </form>
  );
}

Customizing the Theme

Material Expressive tokens are CSS custom properties. Override them at the root:

:root {
  /* Color */
  --md-sys-color-primary: #E21B1B;
  --md-sys-color-on-primary: #ffffff;
  --md-sys-color-primary-container: #ffdad6;
  --md-sys-color-surface: #fffbff;
  --md-sys-color-on-surface: #201a19;

  /* Shape — Material Expressive defaults to more rounded corners */
  --md-sys-shape-corner-small: 8px;
  --md-sys-shape-corner-medium: 16px;
  --md-sys-shape-corner-large: 24px;
  --md-sys-shape-corner-extra-large: 32px;

  /* Typography */
  --md-sys-typescale-body-large-font: 'Ubuntu', sans-serif;
  --md-sys-typescale-body-large-size: 16px;
  --md-sys-typescale-body-large-line-height: 24px;

  /* Motion — override spring parameters */
  --md-sys-motion-spring-default: var(--md-sys-motion-spring-responsive);
}

The New Card Component

Cards are where Material Expressive shines most. The new card component supports three appearances with distinct interaction patterns:

import '@material/web/card/card.js';

function FeatureCards() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
      {/* Elevated card — lifts on hover, presses on click */}
      <md-card appearance="elevated" interactive>
        <div className="p-6">
          <h3>Analytics</h3>
          <p>Real-time dashboards for your data.</p>
        </div>
      </md-card>

      {/* Filled card — subtle background shift on interaction */}
      <md-card appearance="filled" interactive>
        <div className="p-6">
          <h3>Automation</h3>
          <p>Build workflows without code.</p>
        </div>
      </md-card>

      {/* Outlined card — border emphasis on interaction */}
      <md-card appearance="outlined" interactive>
        <div className="p-6">
          <h3>Security</h3>
          <p>Enterprise-grade protection.</p>
        </div>
      </md-card>
    </div>
  );
}

The interactive attribute enables the full Material Expressive motion treatment — spring-based hover lift, tactile press, and ripple feedback.

Implementing Spring Animations Manually

If you are not using Material Web components but want the Material Expressive motion feel, implement springs with Framer Motion:

import { motion, useSpring, useTransform } from 'framer-motion';

function ExpressiveButton({ children, onClick }) {
  const scale = useSpring(1, {
    stiffness: 400,
    damping: 17,
    mass: 0.8,
  });

  const y = useSpring(0, {
    stiffness: 500,
    damping: 20,
  });

  const shadow = useTransform(
    y,
    [-2, 0, 1],
    [
      '0 4px 8px rgba(0,0,0,0.12), 0 8px 16px rgba(0,0,0,0.08)',
      '0 1px 2px rgba(0,0,0,0.1), 0 1px 3px rgba(0,0,0,0.08)',
      '0 0px 1px rgba(0,0,0,0.12)',
    ],
  );

  return (
    <motion.button
      style={{ scale, y, boxShadow: shadow }}
      onHoverStart={()=> { scale.set(1.02); y.set(-2); }}
      onHoverEnd={()=> { scale.set(1); y.set(0); }}
      onTapStart={()=> { scale.set(0.97); y.set(1); }}
      onTap={()=> { scale.set(1); y.set(0); onClick?.(); }}
      className="px-6 py-3 bg-red-600 text-white font-medium"
    >
      {children}
    </motion.button>
  );
}

The spring parameters (stiffness, damping, mass) map to Material Expressive's motion tokens:

Material Expressive TokenFramer Motion Equivalent
spring-responsivestiffness: 500, damping: 25, mass: 0.6
spring-expressivestiffness: 350, damping: 15, mass: 0.8
spring-spatialstiffness: 200, damping: 20, mass: 1.0

Apple Liquid Glass vs Material Expressive

Apple announced Liquid Glass at WWDC 2025, and the design community has been comparing the two approaches ever since. They are solving different problems.

Philosophy

Apple Liquid Glass is about transparency and spatial context. UI elements are translucent, revealing content behind them. The metaphor is glass — you can see through it, it refracts light, and it responds to what is behind it.

Material Expressive is about tactile physicality. UI elements feel solid and responsive. The metaphor is physical material — it has weight, it moves with spring physics, and it responds to your touch.

Visual Comparison

Apple Liquid Glass          Material Expressive
─────────────────           ───────────────────
Translucent surfaces        Opaque surfaces with depth
Blur + vibrancy             Shadow + elevation
Refraction effects          Spring-based motion
Passive (reacts to BG)      Active (reacts to input)
Minimal chrome              Expressive chrome

When to Use Which

ContextBetter ChoiceWhy
Media-heavy apps (photos, video)Liquid GlassTransparency showcases content
Data-heavy apps (dashboards, tools)Material ExpressiveOpaque surfaces improve readability
Mobile-first productsMaterial ExpressiveTactile feedback rewards touch input
Desktop-first productsEitherBoth work well with pointer input
Cross-platform productsMaterial ExpressiveBetter web support, web components
Apple ecosystem onlyLiquid GlassNative SwiftUI integration

They are not mutually exclusive. You can use Material Expressive's spring motion with glass-morphism surfaces:

/* Hybrid: Material Expressive motion + glass surface */
.glass-card {
  /* Glass surface */
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(20px) saturate(1.8);
  border: 1px solid rgba(255, 255, 255, 0.3);

  /* Material Expressive motion */
  transition: transform 0.3s var(--md-sys-motion-spring-expressive),
              box-shadow 0.3s var(--md-sys-motion-spring-expressive);
}

.glass-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}

.glass-card:active {
  transform: translateY(1px);
  transition-duration: 0.08s;
}

Migrating from Material 3

If you are already using Material 3 / Material You, the migration path is incremental. Material Expressive is additive — nothing was removed.

Step 1: Update Motion Tokens

Replace cubic-bezier easings with spring-based linear() curves:

/* Before (Material 3) */
.component {
  transition: transform 300ms cubic-bezier(0.2, 0, 0, 1);
}

/* After (Material Expressive) */
.component {
  transition: transform 350ms var(--md-sys-motion-spring-responsive);
}

Step 2: Add Tactile Feedback to Interactive Elements

Add hover lift and press response to cards, buttons, and list items:

/* Add to any interactive surface */
.interactive-surface {
  transition:
    transform 0.2s var(--md-sys-motion-spring-responsive),
    box-shadow 0.2s var(--md-sys-motion-spring-responsive);
}

.interactive-surface:hover {
  transform: translateY(-2px);
}

.interactive-surface:active {
  transform: translateY(1px);
  transition-duration: 0.05s;
}

Step 3: Adopt Arc Motion for Navigation Transitions

Material Expressive introduces arc motion paths — elements move in curves instead of straight lines during page transitions:

// Arc motion for shared element transitions
// Uses the View Transitions API
document.startViewTransition(() => {
  // Update DOM
  navigateToPage(nextPage);
});

// CSS for arc motion path
::view-transition-old(hero-image),
::view-transition-new(hero-image) {
  animation-duration: 500ms;
  animation-timing-function: var(--md-sys-motion-spring-spatial);
}

::view-transition-new(hero-image) {
  /* Arc motion: element follows a curved path */
  offset-path: path('M 0 0 Q 50 -80 100 0');
  animation-name: arc-enter;
}

@keyframes arc-enter {
  from {
    offset-distance: 0%;
    opacity: 0;
  }
  to {
    offset-distance: 100%;
    opacity: 1;
  }
}

Arc motion feels more natural than linear movement because real objects rarely move in perfectly straight lines.

Practical Takeaways for Your Design System

Whether you adopt Material Expressive fully or just borrow ideas, here are the principles worth integrating:

1. Replace Static Easing with Springs

Springs feel more natural than cubic-bezier curves. A spring has momentum — it overshoots slightly and settles. This micro-overshoot is what makes interfaces feel alive rather than mechanical.

You do not need a physics library. The linear() CSS function can approximate any spring curve, and tools like the Spring Easing Generator will generate the values for you.

2. Add Dimensional Response to Interactive Elements

The hover-lift / press-sink pattern takes 4 lines of CSS and dramatically improves perceived quality:

.interactive:hover  { transform: translateY(-2px); }
.interactive:active { transform: translateY(1px); transition-duration: 0.05s; }

The short transition-duration on :active is critical — press response must feel instant. Hover can be slower (200ms), but press should complete in under 80ms.

3. Use Motion to Communicate Hierarchy

Material Expressive assigns different spring parameters to different levels of the component hierarchy:

  • Primary actions (FAB, primary buttons): Expressive spring — bouncy, attention-grabbing.
  • Secondary actions (cards, list items): Responsive spring — snappy, no overshoot.
  • Background elements (navigation, surfaces): Spatial spring — slow, smooth, unobtrusive.

This creates a motion hierarchy where the most important elements have the most expressive motion, and background elements move subtly so they do not compete for attention.

4. Coordinate Feedback Channels

Tactile feedback is not just visual. Consider:

  • Haptics on mobile for primary actions (button presses, toggles, destructive actions).
  • Sound for confirmations and errors (opt-in, always mutable).
  • Visual ripples and pulses for all interactive elements.

When these channels are coordinated, the interaction feels cohesive. When they are mismatched (e.g., a heavy haptic buzz for a subtle toggle), it feels wrong.

5. Respect Reduced Motion Preferences

All of this motion must be disabled for users who prefer reduced motion:

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

Material Expressive handles this automatically in its component library. If you are implementing the patterns manually, this media query is non-negotiable.

The Bigger Picture

Material Expressive reflects a broader industry shift. After years of flat, static interfaces, design systems are reclaiming motion and physicality as core interaction principles. Apple is doing it with transparency and light. Google is doing it with springs and tactile depth.

The takeaway is not to adopt one system or the other. It is to recognize that motion is no longer decorative — it is functional. A spring-based button press communicates "your input was received" faster than a color change alone. A hover lift tells the user "this is interactive" without relying on cursor changes.

Build your design system's motion language with the same rigor you apply to color and typography. Define tokens, document behavior, and enforce consistency. Material Expressive gives you an excellent blueprint to start from.

Recommended Posts