MD3
Expressive
MATERIAL DESIGN 3 EXPRESSIVE

Shapes

Expressive shape system from Material Design 3. Render organic, smooth-cornered polygons as CSS clip-paths or inline SVG paths with built-in spring-based morphing.

Material Design 3 Expressive shapes utilize a specialized shape engine to draw custom shapes with highly fluid motion. Instead of traditional basic rounded rectangles, M3 introduces 35 unique shape templates (such as flower, sunny, burst, cookie, etc.) which can morph organically when interacted with.

Introduction

At the core of the MD3 Expressive shape engine is a polygon mathematics library designed to draw and animate custom smooth-cornered paths. They are provided as React components:

  • ShapeMedia: Clip any visual element (image, video, gradient container) into an M3 shape and animates its shape on interaction (e.g. hover).
  • ShapeSvg: Renders a shape directly as a scale-independent inline SVG path, great for micro-animations and vector illustrations.
  • useShapeMorph: A custom hook offering the raw animated CSS clipPath value and event handlers for deeper custom components.

The system ships with 35 predefined shapes based on Android Material 3 Design specifications. You can preview them all below. Hover over any shape to see it morph into a clean circle.

Loading demo...

Content Wrapping

Use <ShapeMedia> to wrap any UI child (gradients, pictures, dashboards). It uses CSS clip-path internally to clip the content safely and efficiently without clipping overflow tools or adding heavy DOM wrappers.

Loading demo...

Morph Playground

Pick any start shape and target shape, then slide the morph progress to see the smooth interpolation calculated in real-time by the pure TypeScript math physics engine.

Loading demo...

SVG Rendering

When you need vector graphics, use <ShapeSvg>. It draws high-fidelity, responsive vector shapes that can be filled, stroked, scaled, and morphed dynamically in real-time.

Loading demo...

Usage

ShapeMedia (Content Clipping)

import { ShapeMedia } from "@bug-on/m3-expressive";

{/* Wrap image with custom easing configuration */}

<ShapeMedia
  shape="flower"
  morphTo="circle"
  morphOn="hover"
  morphOptions={{ duration: 0.4, easing: "ease-out" }}
  width={160}
  height={160}
>
  <img 
    src="/avatar.jpg" 
    alt="Profile avatar" 
    className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105" 
  />
</ShapeMedia>

{/* Smooth looping auto-play video wrapper */}
<ShapeMedia
  shape="sunny"
  morphTo="circle"
  morphOn="hover"
  width={200}
  height={200}
>
  <video
    src="/nature-stream.mp4"
    autoPlay
    loop
    muted
    playsInline
    className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
  />
</ShapeMedia>

ShapeSvg (SVG Vector rendering)

import { ShapeSvg } from "@bug-on/m3-expressive";

<ShapeSvg
  shape="sunny"
  width={80}
  height={80}
  fill="#006A60"
/>

useShapeMorph (Custom Canvas/Clip-Path Hook)

import { useShapeMorph } from "@bug-on/m3-expressive";

function CustomComponent() {
  const { clipPath, handlers } = useShapeMorph({
    shape: "clover4Leaf",
    morphTo: "circle",
    morphOn: "hover",
    morphOptions: { duration: 0.4, easing: [0.34, 1.56, 0.64, 1] }, // Springy easing configuration
    width: 100,
    height: 100,
  });

  return (
    <div
      style={{ clipPath }}
      {...handlers}
      className="w-24 h-24 bg-m3-primary"
    />
  );
}

Best Practices & Accessibility

Do

  • Media Display Optimization: Always apply w-full h-full object-cover CSS classes to child <img> or <video> elements to ensure content fits the polygon clip-path cleanly without distortion.
  • Standard Video Setup: Ensure <video> elements wrapped inside ShapeMedia include autoPlay loop muted playsInline attributes for smooth autoplay across devices.
  • Accessibility Optimization: Provide clear visual descriptions via aria-label and declare role="img" for <ShapeMedia> when displaying informative images or videos.
  • Accurate Corner Rounding: Specify exact width and height dimensions on the component for precise clip-path coordinate calculations.

Don't

  • Avoid Distortion: Avoid unconstrained images or videos without object-cover.
  • No Transition Conflicts: Do not apply CSS transition directly to the clip-path property of parent wrappers to prevent physical animation conflicts with Framer Motion's smooth morphing engine.
  • Limit Complex Shapes at Small Sizes: Avoid overly intricate shapes (e.g., 12-clover) for very small elements (below 24dp) as corner details become blurred.

API Reference

ShapeMedia

PropTypeDefaultDescription
shapeMD3ShapeName | RoundedPolygonThe resting shape.
morphToMD3ShapeName | RoundedPolygonThe target shape to morph into.
morphOn"hover" | "click" | "focus" | "scroll" | "none""hover"The trigger condition that starts the shape morphing.
morphOptionsShapeMorphOptionsOptional custom transition properties: duration (seconds) and easing (CSS easing or custom cubic-bezier array).
widthnumberComponent width in pixels. Required for clipPath math calculations.
heightnumberComponent height in pixels. Required for clipPath math calculations.

ShapeSvg

PropTypeDefaultDescription
shapeMD3ShapeName | RoundedPolygonThe SVG shape to render.
morphToMD3ShapeName | RoundedPolygonThe target shape to morph into.
progressnumber0Manual interpolation progress between shape and morphTo [0, 1].
widthnumber100Width of the SVG box.
heightnumber100Height of the SVG box.
fillstring"currentColor"Fill color of the SVG path.

useShapeMorph

OptionTypeDefaultDescription
shapeMD3ShapeName | RoundedPolygonThe resting shape.
morphToMD3ShapeName | RoundedPolygonThe target shape to morph into.
morphOn"hover" | "click" | "focus" | "scroll" | "none""hover"The trigger condition that starts the morphing.
morphOptionsShapeMorphOptionsCustom transition properties: duration (seconds) and easing (CSS easing or custom cubic-bezier array).
widthnumberComponent width in pixels.
heightnumberComponent height in pixels.