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
clipPathvalue and event handlers for deeper custom components.
Gallery
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.
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.
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.
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.
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-coverCSS 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 insideShapeMediaincludeautoPlay loop muted playsInlineattributes for smooth autoplay across devices. - Accessibility Optimization: Provide clear visual descriptions via
aria-labeland declarerole="img"for<ShapeMedia>when displaying informative images or videos. - Accurate Corner Rounding: Specify exact
widthandheightdimensions 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
transitiondirectly to theclip-pathproperty 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
| Prop | Type | Default | Description |
|---|---|---|---|
shape | MD3ShapeName | RoundedPolygon | — | The resting shape. |
morphTo | MD3ShapeName | RoundedPolygon | — | The target shape to morph into. |
morphOn | "hover" | "click" | "focus" | "scroll" | "none" | "hover" | The trigger condition that starts the shape morphing. |
morphOptions | ShapeMorphOptions | — | Optional custom transition properties: duration (seconds) and easing (CSS easing or custom cubic-bezier array). |
width | number | — | Component width in pixels. Required for clipPath math calculations. |
height | number | — | Component height in pixels. Required for clipPath math calculations. |
ShapeSvg
| Prop | Type | Default | Description |
|---|---|---|---|
shape | MD3ShapeName | RoundedPolygon | — | The SVG shape to render. |
morphTo | MD3ShapeName | RoundedPolygon | — | The target shape to morph into. |
progress | number | 0 | Manual interpolation progress between shape and morphTo [0, 1]. |
width | number | 100 | Width of the SVG box. |
height | number | 100 | Height of the SVG box. |
fill | string | "currentColor" | Fill color of the SVG path. |
useShapeMorph
| Option | Type | Default | Description |
|---|---|---|---|
shape | MD3ShapeName | RoundedPolygon | — | The resting shape. |
morphTo | MD3ShapeName | RoundedPolygon | — | The target shape to morph into. |
morphOn | "hover" | "click" | "focus" | "scroll" | "none" | "hover" | The trigger condition that starts the morphing. |
morphOptions | ShapeMorphOptions | — | Custom transition properties: duration (seconds) and easing (CSS easing or custom cubic-bezier array). |
width | number | — | Component width in pixels. |
height | number | — | Component height in pixels. |