Ripple is a script that adds a ripple effect to most buttons and allows users to add ripples to any element too. Original code from Polymer Project's paper-ripple component.
Installation
About
As described by Material Design, ripples are a visual form of feedback for touch events providing users a clear signal that an element is being touched.
There are three types of ripple motions:
- Default: The ripple emanates from the point of contact and stays there. Found in most ripple surfaces/elements.
- Recentered: The ripple emanates from the point of contact, but it will move to the center of the element during the animation. Usually can be found on small buttons such as those from the classic editor's top toolbar.
- Unbounded: The ripple emanates from the center of the element and stays there. Usually used for circle-shaped elements such as Floating Action Buttons (FABs).
Ripple's duration, color, duration, decay duration, and size are some of the characteristics you can customize to fit your needs and brand/stylistic choices.
Usage
You don't have to do anything besides importing the script to make it work. But you can configurate the script to add more elements to be able to have the ripple effect and you can also change how the ripple itself behaves on certain elements.
Adding elements
You can allow more elements to have the ripple effect by just creating an object named window.ripplesConfig with any combination of the following properties: normalRipples, recenteredRipples, and/or unboundedRipples. The value of any of these properties should be a NodeList (a list of elements given by the document.querySelectorAll() method).
Each property grants ripples to the matching elements, but their behavior will depend on which category they are set to (see the about section to learn how each category behaves).
TL;DR: An example of a configuration script that uses all three ripple categories looks like this:
window.ripplesConfig = {
'normalRipples': document.querySelectorAll('.elements-1, .elements-2'),
'recenteredRipples': document.querySelectorAll('.foo .bar'),
'unboundedRipples': document.querySelectorAll('.lorem .ipsum')
};
This basically means that:
- Elements with the classes
elements-1andelements-2will have the default ripple behavior on touch. - Elements with the classes
fooandbarwill have a ripple with recentered behavior. - Elements with the classes
loremandipsumwill have a ripple with unbounded behavior.
Another way is to add any of the following data-attributes to any element to give it a ripple effect on touch without editing any JavaScript:
[data-ripple]will grant the element a normal ripple.[data-recentered]will grant the element a recentered ripple.[data-unbounded]will grant the element an unbounded ripple.
An example of each case would be:
| Code | Result |
|---|---|
<div class="wds-button has-ripple">Touch me!</div>
|
|
<div class="wds-button recentered-ripple">I am re-centered!</div>
|
|
<div class="wds-button unbounded-ripple">And I am unbounded!</div>
|
Another thing you can do is to add a subtle effect when the user is hovering over a ripple surface which may not look like it can trigger such effect. This can be done by using pseudo-classes such as ::before or ::after on a relative-positioned element:
| Code | Result |
|---|---|
|
<div class="card has-ripple">Hover and touch me!</div>
.card {
align-items: center;
background-color: var(--theme-page-background-color--secondary);
border-radius: 8px;
display: flex;
height: 14em;
justify-content: center;
margin: 1em;
overflow: hidden;
padding: 24px;
position: relative;
text-align: center;
width: 10em;
}
.card:hover {
cursor: pointer;
}
.card::before {
background-color: currentColor;
content: '';
height: 100%;
left: 0;
opacity: 0;
pointer-events: none;
position: absolute;
top: 0;
transition: opacity 15ms linear, background-color 15ms linear;
width: 100%;
z-index: 1;
}
.card:hover::before {
opacity: .04;
}
.card:active::before,
.card:focus-within::before {
opacity: .12;
transition-duration: 75ms;
}
|
Hover and touch me!
|
Modifying behavior
You can change some properties of the ripples on a per-element basis or to groups of elements by using CSS custom properties (variables). To learn what each variable does, go to the API section.
TL;DR: An example of a configuration stylesheet that uses all available variables looks like this:
.nice-button {
--ripple-config__duration--ms: 1000;
--ripple-config__end-duration--ms: 500;
--ripple-config__max-radius--px: 200;
--ripple-config__min-duration--ms: 600;
--ripple-config__timing-function: ease;
}
Which means that:
- The element with the
nice-buttonclass will have a specific ripple behavior consisting of:- It's duration will be 1 second (
1000ms). - It's fade-out duration will be half a second (
500ms). - It's maximum radius will be 400px (
200* 2). - It's minimum duration will be 0.6 seconds (
600ms). - It's timing function will be
ease.
- It's duration will be 1 second (
Additionally, you can change some visual aspects of it as well:
.nice-button {
--ripple-color: lime;
--ripple-border-radius: 0;
--ripple-opacity: 0.5;
}
Which also means that:
- The element with the
nice-buttonclass will have a ripple whose appearance consists of:- It's color will be
lime. - It will be a square rather than a circle (due to it's border radius being
0). - It will have an opacity of 50% (
0.5).
- It's color will be
Examples
A new morning Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in nisl sed quam convallis laoreet a mollis arcu. Sed molestie consequat libero vitae dictum.
A new morning Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in nisl sed quam convallis laoreet a mollis arcu. Sed molestie consequat libero vitae dictum.
#var-card-1 {
--ripple-config__duration--ms: 6000;
--ripple-config__max-radius--px: 300;
--ripple-color: #3ea6ff;
}
#var-card-2 {
--ripple-config__duration--ms: 600;
--ripple-config__max-radius--px: 150;
--ripple-config__min-duration--ms: 500;
--ripple-color: #e91e63;
--ripple-border-radius: 30%;
--ripple-opacity: 40%;
}
#var-card-3 {
--ripple-config__end-duration--ms: 6000;
--ripple-config__max-radius--px: 1000;
--ripple-config__timing-function: ease;
--ripple-color: #64dd17;
--ripple-border-radius: 0;
--ripple-opacity: 0.3;
}
#var-card-4 {
--ripple-config__duration--ms: 1500;
--ripple-config__end-duration--ms: 1600;
--ripple-config__max-radius--px: 50;
--ripple-config__min-duration--ms: 1000;
--ripple-config__timing-function: ease-out;
--ripple-color: #d500f9;
--ripple-border-radius: 50%;
--ripple-opacity: .35;
}
| Material Design Ripple | |
|---|---|
| Code | Result |
|
<div class="mdc-ripple has-ripple" id="mdc-card-1">Normal speed</div>
<div class="mdc-ripple has-ripple" id="mdc-card-2">1000% Slower</div>
/* (Cards' CSS not included). */
.mdc-ripple::before {
background-color: currentColor;
content: '';
height: 200%;
left: -50%;
opacity: 0;
pointer-events: none;
position: absolute;
top: -50%;
transition: opacity 75ms linear;
width: 200%;
z-index: 1;
}
.mdc-ripple:hover::before {
opacity: .04;
}
.mdc-ripple:is(:focus, :focus-within, :active)::before {
opacity: .12;
}
/* Normal speed. */
.mdc-ripple {
--ripple-config__duration--ms: 225;
--ripple-config__end-delay--ms: 3000;
--ripple-config__end-duration--ms: 150;
--ripple-config__max-radius--px: 2000;
--ripple-config__min-duration--ms: 75;
--ripple-config__timing-function: cubic-bezier(.4, 0, .2, 1);
--ripple-opacity: .12;
}
/* x10 (1000%) slower. */
#mdc-card-2.mdc-ripple {
--ripple-config__duration--ms: 2250;
--ripple-config__end-duration--ms: 1500;
--ripple-config__min-duration--ms: 750;
}
|
Normal speed
1000% Slower
|
API
HTML elements
Note that you don't have to write any HTML in order to make this script work, but you may want to know the purpose of each generated element.
| HTML element | Description |
|---|---|
<div class="ripple-container"></div>
|
Mandatory. Wraps and limits ripple's location. |
<div class="ripple"></div>
|
Mandatory. It's the ripple itself. |
CSS selectors
| CSS selector | Description |
|---|---|
[ripple]
[data-ripple]
|
Selects any element with the ripple attribute. These elements will have a ripple upon script execution.
|
[recentered]
[data-recentered]
|
Selects any element with the recentered attribute. These elements will have a recentered ripple upon script execution.
|
[unbounded]
[data-unbounded]
|
Selects any element with the unbounded attribute. These elements will have a unbounded ripple upon script execution.
|
CSS variables
| CSS variable | Description |
|---|---|
--ripple-config__duration--ms
|
Duration (in miliseconds) of the ripple expansion animation. Defaults to, at least, 800ms.
|
--ripple-config__end-duration--ms
|
Duration (in miliseconds) of the ripple fade-out animation. Defaults to 150ms (on touch) or 400ms (on key press).
|
--ripple-config__max-radius--px
|
Maximum radius of the ripple (in pixels). Defaults to 300px.
|
--ripple-config__min-duration--ms
|
Minimum duration (in miliseconds) of the riple expansion animation. Defaults to 800ms.
|
--ripple-config__timing-function
|
CSS easing function that determines the expansion animation's speed rate. Defaults to cubic-bezier(.2, .9, .1, .9).
|
--ripple-color
|
Determines the ripple's color. Defaults to currentText (the element's current text color).
|
--ripple-border-radius
|
Determines the ripple's border-radius. Defaults to 50% (full circle).
|
--ripple-opacity
|
Determines the ripple's opacity. Defaults to 25%. |
--ripple-checked-color
|
Determines the ripple's color on checked elements such as checkboxes, radio buttons, and toggles. Defaults to var(--theme-link-color).
|
--ripple-unchecked-color
|
Determines the ripple's color on un-checked elements such as checkboxes, radio buttons, and toggles. Defaults to var(--theme-body-text-color).
|
--ripple-checked-opacity
|
Determines the ripple's opacity on checked elements such as checkboxes, radio buttons, and toggles. Defaults to 20%. |
--ripple-unchecked-opacity
|
Determines the ripple's opacity on un-checked elements such as checkboxes, radio buttons, and toggles. Defaults to 15%. |