Ripple
The ripple is Material’s press feedback: a circle that grows from the pointer. 24 components use it. It is on by default and can be switched off at any level.
Turn it off globally
Section titled “Turn it off globally”<html data-ripple="off">Every ripple in the app stops spawning. Hover, focus and pressed state layers still work — only the animated circle is suppressed.
Show code for each technology
<!-- index.html <head> — the icon font the components draw from -->
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap">
<!-- index.html — register the AWC UI elements once -->
<script type="module">
import '@awc-ui/core/define';
</script>
<div style="display:flex;gap:12px;align-items:center;margin-block-end:16px;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">on</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>
<div data-ripple="off" style="display:flex;gap:12px;align-items:center;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">off</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>// Icons need the Material Symbols stylesheet in index.html — see Installation.
import { MdButton, MdIconButton } from '@awc-ui/react';
export function Demo() {
return (
<>
<div style={{ display: 'flex', gap: '12px', alignItems: 'center', marginBlockEnd: '16px' }}>
<span style={{ width: '5rem', opacity: '.7', fontSize: '.8rem' }}>on</span>
<MdButton variant="filled">Press me</MdButton>
<MdIconButton icon="favorite" aria-label="Favourite"></MdIconButton>
</div>
<div data-ripple="off" style={{ display: 'flex', gap: '12px', alignItems: 'center' }}>
<span style={{ width: '5rem', opacity: '.7', fontSize: '.8rem' }}>off</span>
<MdButton variant="filled">Press me</MdButton>
<MdIconButton icon="favorite" aria-label="Favourite"></MdIconButton>
</div>
</>
);
}// Icons need the Material Symbols stylesheet in index.html — see Installation.
// app.module.ts — register the AWC UI elements once
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AwcUiModule } from '@awc-ui/angular';
@NgModule({
imports: [AwcUiModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- app.component.html -->
<div style="display:flex;gap:12px;align-items:center;margin-block-end:16px;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">on</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>
<div data-ripple="off" style="display:flex;gap:12px;align-items:center;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">off</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div><script setup>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<template>
<div style="display:flex;gap:12px;align-items:center;margin-block-end:16px;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">on</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>
<div data-ripple="off" style="display:flex;gap:12px;align-items:center;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">off</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>
</template><script>
// Icons need the Material Symbols stylesheet in index.html — see Installation.
import '@awc-ui/core/define';
</script>
<div style="display:flex;gap:12px;align-items:center;margin-block-end:16px;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">on</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>
<div data-ripple="off" style="display:flex;gap:12px;align-items:center;">
<span style="width:5rem;opacity:.7;font-size:.8rem;">off</span>
<md-button variant="filled">Press me</md-button>
<md-icon-button icon="favorite" aria-label="Favourite"></md-icon-button>
</div>Three levels, and opting back in
Section titled “Three levels, and opting back in”The switch resolves to one custom property, --md-sys-ripple-enabled
(1 on, 0 off). It travels both ways, so a subtree or a single
component can opt back in inside a disabled region:
<html data-ripple="off"> <!-- no ripples anywhere -->
<section data-ripple="on"> <!-- ...except in here --> <md-button variant="filled">Ripples</md-button> </section>
<md-button variant="filled" ripple="on">Also ripples</md-button></html>| Scope | Attribute | Where |
|---|---|---|
| Global | data-ripple="off" / "on" | <html> or any ancestor |
| Region | data-ripple="off" / "on" | Any wrapper element |
| Component | ripple="off" / "on" | The component host |
Both spellings resolve to the same property, so a component never needs to know which one was used.
Why local wins
Section titled “Why local wins”A local rule applies the property directly to the host; the global one
arrives by inheritance from an ancestor. A directly-applied custom
property always beats an inherited one, at any specificity — no !important
and no specificity tricks.
Why not :host-context()
Section titled “Why not :host-context()”The obvious implementation — reading an ancestor’s state from inside the
shadow root — is not available: Firefox does not implement
:host-context(). An earlier version of this library used it and was
silently inert in Firefox. The inherited-custom-property approach works in
every browser and needs no JavaScript.
Runtime toggle
Section titled “Runtime toggle”// offdocument.documentElement.dataset.ripple = 'off';// back ondocument.documentElement.dataset.ripple = 'on';// or remove the attribute entirely for the defaultdelete document.documentElement.dataset.ripple;Because it is pure CSS, the change applies instantly with no re-render.
Reduced motion
Section titled “Reduced motion”The ripple is animation. Respect the OS preference:
@media (prefers-reduced-motion: reduce) { :root { --md-sys-ripple-enabled: 0; }}When to switch it off
Section titled “When to switch it off”| Situation | Recommendation |
|---|---|
| Utilitarian internal tool, flat visual language | Global off |
| Dense data grid where every cell click ripples | off on the table region |
User has prefers-reduced-motion | off via the media query above |
| Consumer product | Leave it on — it is a core Material affordance |
Custom controls
Section titled “Custom controls”If you build your own interactive surface and want matching feedback, use
the md-ripple primitive rather than a hand-rolled animation — it
participates in this same global switch:
<div class="my-tile" style="position: relative; overflow: hidden;"> <md-ripple></md-ripple> …content…</div>Note that pointer input ripples automatically but keyboard activation does
not — call trigger() from your Enter/Space handler, which is what the
library components do.
Related
Section titled “Related”- Shape Morph — the other expressive toggle, same mechanism
md-ripple— the primitive itself