Material Design ink ripple effect component for web element interactions with JavaScript and CSS-only implementations
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The Sass API provides comprehensive mixin and function systems for styling ripple effects with CSS-only fallbacks. It supports both basic and advanced configuration patterns for different design requirements.
These mixins are required for any ripple implementation.
/**
* Base ripple surface styles - MANDATORY
* Adds fundamental styles required for ripple effects
*/
@mixin surface();
/**
* Bounded ripple radius styles
* For ripples clipped by element boundaries (most common)
* @param $radius - Border radius for the ripple bounds (default: 100%)
*/
@mixin radius-bounded($radius: 100%);
/**
* Unbounded ripple radius styles
* For ripples extending beyond element boundaries (checkboxes, radio buttons)
* @param $radius - Border radius for the ripple effect (default: 100%)
*/
@mixin radius-unbounded($radius: 100%);Usage Example:
@use "@material/ripple";
.my-button {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states;
}
.my-checkbox-ripple {
@include ripple.surface;
@include ripple.radius-unbounded;
@include ripple.states;
}Simple approach using automatic opacity calculations based on color lightness.
/**
* Basic state styles with automatic opacity - MANDATORY when using basic approach
* Adds hover, focus, and press state styles using calculated opacities
* @param $color - Base color for the ripple effect
* @param $has-nested-focusable-element - Whether element contains focusable children (default: false)
*/
@mixin states($color, $has-nested-focusable-element: false);
/**
* Activated state styles for toggleable elements
* @param $color - Base color for activated states
* @param $has-nested-focusable-element - Whether element contains focusable children (default: false)
*/
@mixin states-activated($color, $has-nested-focusable-element: false);
/**
* Selected state styles for selectable elements
* @param $color - Base color for selected states
* @param $has-nested-focusable-element - Whether element contains focusable children (default: false)
*/
@mixin states-selected($color, $has-nested-focusable-element: false);Usage Examples:
@use "@material/ripple";
@use "@material/theme";
// Basic button with primary theme color
.my-button {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states(theme.prop-value(primary));
}
// Toggleable element with activated states
.my-toggle {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states(theme.prop-value(on-surface));
&--activated {
@include ripple.states-activated(theme.prop-value(primary));
}
}
// Form field with nested input
.my-form-field {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states(theme.prop-value(on-surface), $has-nested-focusable-element: true);
}Granular control over individual state opacities.
/**
* Base color setup for advanced states - MANDATORY when using advanced approach
* @param $color - Base color for all state calculations
*/
@mixin states-base-color($color);
/**
* Custom opacity values for specific interaction states
* @param $opacity-map - Map of state names to opacity values (hover, focus, press)
* @param $has-nested-focusable-element - Whether element contains focusable children (default: false)
*/
@mixin states-opacities($opacity-map, $has-nested-focusable-element: false);Usage Example:
@use "@material/ripple";
.my-custom-surface {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states-base-color(#1976d2);
@include ripple.states-opacities((
hover: 0.04,
focus: 0.12,
press: 0.10
));
}
// Different opacities for activated state
.my-custom-surface--activated {
@include ripple.states-base-color(#ffffff);
@include ripple.states-opacities((
hover: 0.08,
focus: 0.24,
press: 0.20
));
}These mixins are deprecated but still supported for backward compatibility.
/**
* DEPRECATED: Use states-opacities instead
* Set hover state opacity
* @param $opacity - Hover opacity value
*/
@mixin states-hover-opacity($opacity);
/**
* DEPRECATED: Use states-opacities instead
* Set focus state opacity
* @param $opacity - Focus opacity value
* @param $has-nested-focusable-element - Whether element contains focusable children (default: false)
*/
@mixin states-focus-opacity($opacity, $has-nested-focusable-element: false);
/**
* DEPRECATED: Use states-opacities instead
* Set press state opacity
* @param $opacity - Press opacity value
*/
@mixin states-press-opacity($opacity);Utility functions for opacity calculations and color manipulation.
/**
* Calculate appropriate opacity for a color in a given state
* @param $color - Base color value
* @param $state - State name (hover, focus, press, selected, activated)
* @returns Calculated opacity value for the color and state combination
*/
@function states-opacity($color, $state);Usage Example:
@use "@material/ripple";
$primary-color: #1976d2;
.my-element {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states-base-color($primary-color);
@include ripple.states-opacities((
hover: ripple.states-opacity($primary-color, hover),
focus: ripple.states-opacity($primary-color, focus),
press: ripple.states-opacity($primary-color, press)
));
}When JavaScript is not available, these CSS classes provide basic ripple styling.
/**
* Basic ripple surface class
* Apply to elements that should have ripple effects
*/
.mdc-ripple-surface {}
/**
* Primary theme color ripple
* Uses the theme's primary color for ripple effects
*/
.mdc-ripple-surface--primary {}
/**
* Secondary theme color ripple
* Uses the theme's secondary (accent) color for ripple effects
*/
.mdc-ripple-surface--accent {}/**
* Applied when ripple is upgraded with JavaScript
* Enables CSS variable-based ripple animations
*/
.mdc-ripple-upgraded {}
/**
* Applied to unbounded ripples
* Allows ripple to extend beyond element boundaries
*/
.mdc-ripple-upgraded--unbounded {}
/**
* Applied during focus state
* Shows background highlighting for focused elements
*/
.mdc-ripple-upgraded--background-focused {}
/**
* Applied during ripple activation animation
* Triggers the expanding ripple effect
*/
.mdc-ripple-upgraded--foreground-activation {}
/**
* Applied during ripple deactivation animation
* Triggers the fade-out ripple effect
*/
.mdc-ripple-upgraded--foreground-deactivation {}@use "@material/ripple";
@use "@material/theme";
.custom-button {
// Base button styles
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
// Ripple integration
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states(theme.prop-value(primary));
// Ensure proper stacking context
position: relative;
overflow: hidden;
}@use "@material/ripple";
@use "@material/theme";
.icon-button {
width: 40px;
height: 40px;
border-radius: 50%;
border: none;
background: transparent;
cursor: pointer;
// Unbounded ripple for circular button
@include ripple.surface;
@include ripple.radius-unbounded;
@include ripple.states(theme.prop-value(on-surface));
// Ensure ripple visibility
position: relative;
overflow: visible;
}@use "@material/ripple";
@use "@material/theme";
.form-field {
padding: 16px;
border: 1px solid;
border-radius: 4px;
cursor: pointer;
// Ripple with nested focusable element
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states(
theme.prop-value(on-surface),
$has-nested-focusable-element: true
);
position: relative;
overflow: hidden;
input {
// Input styling
border: none;
background: transparent;
outline: none;
}
}@use "@material/ripple";
.premium-surface {
@include ripple.surface;
@include ripple.radius-bounded(8px);
// Custom color and opacity configuration
@include ripple.states-base-color(#9c27b0);
@include ripple.states-opacities((
hover: 0.06,
focus: 0.18,
press: 0.14
));
// Premium activated state
&--premium {
@include ripple.states-base-color(#ffd700);
@include ripple.states-opacities((
hover: 0.08,
focus: 0.20,
press: 0.16
));
}
}@use "@material/ripple";
@use "@material/theme";
.fallback-button {
@include ripple.surface;
@include ripple.radius-bounded;
@include ripple.states(theme.prop-value(primary));
// CSS-only states using pseudo-classes
// These work when JavaScript is disabled
&:hover {
// Hover styles applied via CSS
}
&:focus {
// Focus styles applied via CSS
}
&:active {
// Active/press styles applied via CSS
}
}The Sass system generates and manages these CSS custom properties:
--mdc-ripple-fg-size: /* Foreground ripple size */
--mdc-ripple-left: /* Left position for unbounded ripples */
--mdc-ripple-top: /* Top position for unbounded ripples */--mdc-ripple-fg-scale: /* Scale factor for ripple animation */
--mdc-ripple-fg-translate-start: /* Animation start position */
--mdc-ripple-fg-translate-end: /* Animation end position */These variables are automatically managed by the JavaScript foundation and should not be set manually in most cases.