Customizable Material Design 3 Ripple Effects for React Apps – M3-Ripple

Description:

M3-Ripple is a React component library that implements Google’s Material Design 3 ripple effect specification.

You can integrate it into buttons, cards, list items, or any interactive element that requires visual confirmation of user interaction.

Features

  • 🎯 Perfect Material Design 3 compliance with authentic ripple animations
  • ⚡ Zero-configuration setup that works out of the box with any parent element
  • 🎨 Automatic color inheritance from parent elements using currentColor
  • 🔧 Customizable opacity levels for both hover and pressed states
  • ⏱️ Configurable animation duration and timing parameters
  • 📱 Touch and mouse event support for cross-platform compatibility
  • 🎛️ CSS variable support for global theming control
  • 🚫 Built-in disabled state management
  • 🎪 Custom easing functions for fine-tuned animation control
  • 📦 Lightweight implementation with minimal bundle impact

Preview

material-design-ripple

Use Cases

  • Interactive buttons in web applications requiring Material Design feedback patterns
  • Card components in data dashboards where users need visual confirmation of selection
  • Navigation menu items that benefit from tactile feedback during user interaction
  • Form controls and input fields that require modern interaction patterns
  • E-commerce product listings where visual feedback improves user engagement

How to Use It

1. Install the package through your preferred package manager:

npm install m3-ripple

2. Import both the component and its required CSS file into your React application:

import { Ripple } from 'm3-ripple'
import 'm3-ripple/ripple.css'

3. Add the Ripple component inside any interactive element and apply position: relative to the parent container:

function BasicButton() {
    return (
        <button
            style={{
                position: 'relative',
                padding: '12px 24px',
                backgroundColor: '#1976d2',
                color: 'white',
                border: 'none',
                borderRadius: '4px',
                cursor: 'pointer'
            }}
        >
            <Ripple />
            Click Me
        </button>
    )
}

4. The ripple effect automatically inherits the color from its parent element through CSS’s currentColor property. You can control the ripple color by setting the color style on the parent element:

function ColoredRippleButton() {
    return (
        <div
            style={{
                position: 'relative',
                padding: '16px 32px',
                backgroundColor: '#f5f5f5',
                color: '#e91e63', // This controls the ripple color
                border: '2px solid #e91e63',
                borderRadius: '8px',
                cursor: 'pointer'
            }}
        >
            <Ripple />
            Pink Ripple Button
        </div>
    )
}

5. Configure ripple opacity globally using CSS custom properties:

:root {
  --ripple-hover-opacity: 0.08;
  --ripple-pressed-opacity: 0.12;
}

6. Override global settings with component-specific props:

function CustomOpacityRipple() {
    return (
        <button style={{ position: 'relative', padding: '10px 20px' }}>
            <Ripple 
                hoverOpacity={0.15} 
                pressedOpacity={0.25} 
            />
            High Opacity Ripple
        </button>
    )
}

7. Customize animation timing and behavior with detailed props:

function AdvancedRipple() {
    return (
        <div style={{ position: 'relative', padding: '20px' }}>
            <Ripple 
                duration={200}
                minimumPressDuration={300}
                touchDelay={100}
                easing="cubic-bezier(0.2, 0, 0, 1)"
                className="custom-ripple-class"
                style={{ zIndex: 1 }}
            />
            Advanced Ripple Configuration
        </div>
    )
}

8. Disable ripple effects conditionally:

function ConditionalRipple({ isDisabled }) {
    return (
        <button 
            style={{ 
                position: 'relative', 
                padding: '12px 24px',
                opacity: isDisabled ? 0.6 : 1
            }}
            disabled={isDisabled}
        >
            <Ripple disabled={isDisabled} />
            {isDisabled ? 'Disabled Button' : 'Active Button'}
        </button>
    )
}

9. All available component props.

export interface RippleProps {
/**
* Disables the ripple.
*/
disabled?: boolean

/**
* hoverOpacity: The opacity of the ripple when hovered.
*
* @default 0.08
*/
hoverOpacity?: number

/**
* pressedOpacity: The opacity of the ripple when pressed.
*
* @default 0.12
*/
pressedOpacity?: number

/**
* Additional CSS classes to apply to the ripple container.
*/
className?: string

/**
* Additional styles to apply to the ripple container.
*/
style?: React.CSSProperties

/**
* Easing function for the ripple animation.
*/
easing?: 'cubic-bezier(0.2, 0, 0, 1)'

/**
* The duration in milliseconds for the ripple to grow when pressed.
*
* @default 150
*/
duration?: number

/**
* The minimum duration in milliseconds for the ripple to be considered a
* valid press.
*
* @default 225
*/
minimumPressDuration?: number

/**
* * The duration in milliseconds for the ripple to wait before starting the
*
* @default 150
*/
touchDelay?: number
}

FAQs

Q: Why isn’t the ripple effect appearing on my element?
A: The parent element must have position: relative applied to it. The ripple component uses absolute positioning and requires this CSS property to position itself correctly within the parent boundaries.

Q: Can I use M3-Ripple with existing UI libraries like Material-UI or Ant Design?
A: Yes, M3-Ripple works with any React component or HTML element.

Q: How do I customize the ripple color to match my design system?
A: Set the color CSS property on the parent element. The ripple automatically inherits this color through currentColor. You can also use CSS variables for global theming control.

Q: Does M3-Ripple support server-side rendering?
A: Yes, the component works with server-side rendering frameworks like Next.js. The CSS animations are handled client-side after hydration.

Add Comment