<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title></title>
    <link rel="self" type="application/atom+xml" href="https://next.siphalor.de/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://next.siphalor.de"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-05-16T00:00:00+00:00</updated>
    <id>https://next.siphalor.de/atom.xml</id>
    <entry xml:lang="en">
        <title>Custom Ripple Exercise</title>
        <published>2026-05-16T00:00:00+00:00</published>
        <updated>2026-05-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Siphalor
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://next.siphalor.de/blog/ripple/"/>
        <id>https://next.siphalor.de/blog/ripple/</id>
        
        <content type="html" xml:base="https://next.siphalor.de/blog/ripple/">&lt;div class=&quot;heading-links&quot;&gt;
    &lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;codepen.io&#x2F;Siphalor&#x2F;pen&#x2F;ogYYrqE&quot;&gt;Codepen&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
        &lt;&#x2F;ul&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;I&#x27;m not a huge fan of Material Design, but I recently noticed that I quite enjoy the little ripple effect you get on
their buttons.&lt;br &#x2F;&gt;
You can see them in the &lt;a rel=&quot;nofollow external&quot; href=&quot;https:&#x2F;&#x2F;material.angular.dev&#x2F;components&#x2F;categories&quot;&gt;Angular Material documentation&lt;&#x2F;a&gt;
when clicking on the cards.&lt;&#x2F;p&gt;
&lt;p&gt;Since I thought it would be a fun little challenge, I tried to replicate it with a bunch of more or less modern CSS
(and a bit of JS).&lt;&#x2F;p&gt;
&lt;p&gt;The resulting code is provided with the codepen link above.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;p&gt;I started out with a simple HTML button and some basic styling.
The goal is not to touch this code at all.&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Button&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;primary ripple&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Primary&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;black ripple&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Dark&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;&#x2F;div&gt;
    
&lt;&#x2F;figure&gt;&lt;h2 id=&quot;layering-approaches&quot;&gt;Layering approaches&lt;&#x2F;h2&gt;
&lt;p&gt;From my understanding, there are two main ways to add in the ripple circles, you can either&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;use a &lt;code&gt;::before&lt;&#x2F;code&gt; or &lt;code&gt;::after&lt;&#x2F;code&gt; pseudo-element or&lt;&#x2F;li&gt;
&lt;li&gt;use a layered background on the element&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Both have their ups and their downs:&lt;&#x2F;p&gt;
&lt;p&gt;Pseudo-elements are a kind of limited resource, since there is only a maximum of one &lt;code&gt;::before&lt;&#x2F;code&gt; and &lt;code&gt;::after&lt;&#x2F;code&gt; for every
element on the page. A lot of CSS hacks tend to use them, so it is basically unavoidable
that you&#x27;ll get conflicts at some points.&lt;&#x2F;p&gt;
&lt;p&gt;Layered backgrounds, however, require that the background on the affected elements has to be set up in a certain way, so
that the ripple can &quot;inject&quot; into it.&lt;br &#x2F;&gt;
While this may be a good option if you&#x27;re only targeting a limited set of components, it&#x27;s pretty much impossible to
just slap on a simple class with this approach.&lt;&#x2F;p&gt;
&lt;p&gt;I chose to use pseudo-elements for my exercise.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;foundations&quot;&gt;Foundations&lt;&#x2F;h2&gt;
&lt;p&gt;First off, let&#x27;s create an &lt;code&gt;::after&lt;&#x2F;code&gt; pseudo-element (since that naturally layers on top of the element) and make it
overlay the targeted element:&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;.ripple&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;	position&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; relative&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;::after {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;        content&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        position&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; absolute&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        inset&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        border-radius&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; inherit&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        background-color&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; rgba&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;255 0 0&lt;&#x2F;span&gt;&lt;span&gt; &#x2F; &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.6&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        pointer-events&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; none&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;:active::after&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.3&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
.demo-button {
	position: relative;
}
.demo-button::after {
	content: &quot;&quot;;
	position: absolute;
	inset: 0;
	opacity: 0;
	border-radius: inherit;
	background-color: rgba(255 0 0 &#x2F; 0.6);
	pointer-events: none;
}
.demo-button:active::after {
	opacity: 0.3;
}
}
&lt;&#x2F;style&gt;
&lt;&#x2F;div&gt;
    
    &lt;figcaption&gt;&lt;p&gt;The buttons now get a red overlay when clicked&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Since the &lt;code&gt;::after&lt;&#x2F;code&gt; pseudo-elements are on top of the buttons, it is important to set &lt;code&gt;pointer-events: none&lt;&#x2F;code&gt;,
so that clicking the buttons still goes through to the actual button elements underneath.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the ripple effect should be circular. I decided to use a &lt;code&gt;radial-gradient&lt;&#x2F;code&gt; for this instead of a &lt;code&gt;border-radius&lt;&#x2F;code&gt;
since this offers some nice options for further customizing this later.&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;.ripple&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;	&#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;::after {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-invalid z-deprecated&quot;&gt;        background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;radial-gradient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;circle farthest-corner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; at&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; center&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant z-constant&quot;&gt; currentColor 30&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant z-constant&quot;&gt; transparent 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
.demo-button {
	position: relative;
}
.demo-button::after {
	content: &quot;&quot;;
	position: absolute;
	inset: 0;
	opacity: 0;
	border-radius: inherit;
	pointer-events: none;
	background: radial-gradient(circle farthest-corner at center, currentColor 30%, transparent 30%, transparent 100%);
}
.demo-button:active::after {
	opacity: 0.3;
}
}
&lt;&#x2F;style&gt;
&lt;&#x2F;div&gt;
    
&lt;&#x2F;figure&gt;
&lt;p&gt;To have some automatically adapting contrast, one can use &lt;code&gt;currentColor&lt;&#x2F;code&gt; to use the text color inside this background.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;animating&quot;&gt;Animating&lt;&#x2F;h2&gt;
&lt;p&gt;The circle shouldn&#x27;t just statically rest where it is, but should grow out when clicked and
slowly fade out when let go.&lt;&#x2F;p&gt;
&lt;p&gt;To transition the gradient, we can use a custom property. For transitioning them, it is required to declare them with
an &lt;code&gt;@property&lt;&#x2F;code&gt; at-rule.&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;@property&lt;&#x2F;span&gt;&lt;span&gt; --ripple-size {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	syntax: &amp;quot;&amp;lt;percentage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	inherits: false;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;	initial-value&lt;&#x2F;span&gt;&lt;span&gt;: 0%;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;.ripple&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;	&#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;::after {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-invalid z-deprecated&quot;&gt;        background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;radial-gradient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;                circle farthest-corner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; at&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; center&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;                currentColor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant z-support&quot;&gt;                currentColor var&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--ripple-size&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant z-support&quot;&gt;                transparent var&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--ripple-size&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant z-constant&quot;&gt;                transparent 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        transition&lt;&#x2F;span&gt;&lt;span&gt;: opacity &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; ease-out&lt;&#x2F;span&gt;&lt;span&gt;, --ripple-size &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; ease-out&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;:active::after&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.15&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        --ripple-size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
@property --ripple-size-100 {
	syntax: &quot;&lt;percentage&gt;&quot;;
	inherits: false;
	initial-value: 0%;
}
.demo-button {
	position: relative;
}
.demo-button::after {
	content: &quot;&quot;;
	position: absolute;
	inset: 0;
	border-radius: inherit;
	pointer-events: none;
	opacity: 0;
	background: radial-gradient(
		circle farthest-corner at center,
		currentColor,
		currentColor var(--ripple-size-100),
		transparent var(--ripple-size-100),
		transparent 100%
	);
	transition: opacity 0.5s ease-out, --ripple-size-100 0.5s ease-out;
}
.demo-button:active::after {
	opacity: 0.15;
	--ripple-size-100: 100%;
}
}
&lt;&#x2F;style&gt;
&lt;&#x2F;div&gt;
    
&lt;&#x2F;figure&gt;
&lt;p&gt;Well, this is the point where the pure-CSS solution ends, and I had to get a little help from JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;Currently, when you release the button, the ripple declines again before vanishing.
This is not what Material does and not what I want though.&lt;br &#x2F;&gt;
I&#x27;d like it to continue expanding while slowly fading out.&lt;&#x2F;p&gt;
&lt;p&gt;I played around a bit with CSS animations and combinations with transitions, but nothing really worked like I wanted to,
especially since animations and transitions don&#x27;t pair nicely.
Transitions strictly transition between actually declared values
and will just ignore that a property was in the middle of an animation:&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;@property&lt;&#x2F;span&gt;&lt;span&gt; --ripple-size {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	syntax: &amp;quot;&amp;lt;percentage&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	inherits: false;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;	initial-value&lt;&#x2F;span&gt;&lt;span&gt;: 0%;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;button&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;	&#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;::after {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		--ripple-size: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        transition&lt;&#x2F;span&gt;&lt;span&gt;: opacity &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; ease-out&lt;&#x2F;span&gt;&lt;span&gt;, --ripple-size-100 &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; ease-out&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;:active::after&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.5&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;		animation&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-support z-constant&quot;&gt; ease-out forwards&lt;&#x2F;span&gt;&lt;span&gt; --ripple-expand;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;@keyframes&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; --ripple-expand&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;	from&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; --ripple-size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;    to&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; --ripple-size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
@property --ripple-size-200 {
	syntax: &quot;&lt;percentage&gt;&quot;;
	inherits: false;
	initial-value: 0%;
}
.demo-button {
	position: relative;
}
.demo-button::after {
	content: &quot;&quot;;
	position: absolute;
	inset: 0;
	border-radius: inherit;
	pointer-events: none;
	opacity: 0;
	--ripple-size-200: 100%;
	background: radial-gradient(
		circle farthest-corner at center,
		currentColor,
		currentColor var(--ripple-size-200),
		transparent var(--ripple-size-200),
		transparent 100%
	);
	transition: opacity 3s ease-out, --ripple-size-200 3s ease-out;
}
.demo-button:active::after {
	opacity: 0.5;
	animation: 3s ease-out forwards --ripple-expand;
}
@keyframes --ripple-expand {
from { --ripple-size-200: 0%; }
to { --ripple-size-200: 100%; }
}
}
&lt;&#x2F;style&gt;
&lt;&#x2F;div&gt;
    
    &lt;figcaption&gt;&lt;p&gt;An attempt with a combination of transition and animation.
I increased the opacity and transition&#x2F;animation duration so the snapping when releasing early is more noticeable.&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;So, JavaScript to the rescue.&lt;br &#x2F;&gt;
With a little event handler that adds and removes a class that resets the sizes, this looks pretty nice already.&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;.ripple&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;	&#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;::after {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		--ripple-size: &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;:active::after&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        opacity&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.15&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        --ripple-size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;.ripple-start::after&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;		--ripple-size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;document.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;pointerdown&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (event.target.classList.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        event.target.classList.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple-start&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(()&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event.target.classList.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple-start&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div id=&quot;demo-300&quot;&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
@property --ripple-size-300 {
	syntax: &quot;&lt;percentage&gt;&quot;;
	inherits: false;
	initial-value: 0%;
}
.demo-button {
	position: relative;
}
.demo-button::after {
	content: &quot;&quot;;
	position: absolute;
	inset: 0;
	border-radius: inherit;
	pointer-events: none;
	opacity: 0;
	--ripple-size-300: 100%;
	background: radial-gradient(
		circle farthest-corner at center,
		currentColor,
		currentColor var(--ripple-size-300),
		transparent var(--ripple-size-300),
		transparent 100%
	);
	transition: opacity 0.5s ease-out, --ripple-size-300 0.5s ease-out;
}
.demo-button:active::after {
	opacity: 0.15;
	--ripple-size-300: 100%;
}
.demo-button.ripple-start::after {
transition: none;
--ripple-size-300: 0%;
}
}
&lt;&#x2F;style&gt;
&lt;script&gt;
document.getElementById(&#x27;demo-300&#x27;).addEventListener(&#x27;pointerdown&#x27;, (event) =&gt; {
if (event.target.classList.contains(&quot;demo-button&quot;)) {
event.target.classList.add(&quot;ripple-start&quot;);
setTimeout(() =&gt; event.target.classList.remove(&quot;ripple-start&quot;), 10);
}
})
&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
    
    &lt;figcaption&gt;&lt;p&gt;Even on quick releases, the ripple now expands outwards.&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;&lt;h2 id=&quot;positioning-the-ripple&quot;&gt;Positioning the ripple&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;re still missing one of the arguably nicest visual cues. The ripple should expand from where the user has clicked.&lt;&#x2F;p&gt;
&lt;p&gt;For this a little JavaScript is needed anyway, so I&#x27;m not feeling too bad for the little &lt;code&gt;.ripple-start&lt;&#x2F;code&gt; hack above.&lt;&#x2F;p&gt;
&lt;p&gt;All that is needed is a few more custom properties as a way for JavaScript to pass the click position to CSS.&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity&quot;&gt;.ripple&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;	&#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	&amp;amp;::after {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;        &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-invalid z-deprecated&quot;&gt;        background&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;radial-gradient&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;                circle farthest-corner&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; at&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; var&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--ripple-x&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; var&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--ripple-y&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;                currentColor&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant z-support&quot;&gt;                currentColor var&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--ripple-size&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant z-support&quot;&gt;                transparent var&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;--ripple-size&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant z-constant&quot;&gt;                transparent 100&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;%&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;    &#x2F;* ... *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;document.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;addEventListener&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;pointerdown&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, (&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; (event.target.classList.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        event.target.style.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;--ripple-x&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; `${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;offsetX&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}px`&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        event.target.style.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;setProperty&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;--ripple-y&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; `${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;event&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;offsetY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}px`&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;        event.target.classList.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple-start&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        setTimeout&lt;&#x2F;span&gt;&lt;span&gt;(()&lt;&#x2F;span&gt;&lt;span class=&quot;z-storage z-type&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; event.target.classList.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;remove&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;ripple-start&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;div id=&quot;demo-400&quot;&gt;
&lt;button class=&quot;demo-button&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
@property --ripple-size-400 {
	syntax: &quot;&lt;percentage&gt;&quot;;
	inherits: false;
	initial-value: 0%;
}
.demo-button {
	position: relative;
}
.demo-button::after {
	content: &quot;&quot;;
	position: absolute;
	inset: 0;
	border-radius: inherit;
	pointer-events: none;
	opacity: 0;
	--ripple-size-400: 100%;
	background: radial-gradient(
		circle farthest-corner at var(--ripple-x, 0) var(--ripple-y, 0),
		currentColor,
		currentColor var(--ripple-size-400),
		transparent var(--ripple-size-400),
		transparent 100%
	);
	transition: opacity 0.5s ease-out, --ripple-size-400 0.5s ease-out;
}
.demo-button:active::after {
	opacity: 0.15;
	--ripple-size-400: 100%;
}
.demo-button.ripple-start::after {
transition: none;
--ripple-size-400: 0%;
}
}
&lt;&#x2F;style&gt;
&lt;script&gt;
document.getElementById(&#x27;demo-400&#x27;).addEventListener(&#x27;pointerdown&#x27;, (event) =&gt; {
if (event.target.classList.contains(&quot;demo-button&quot;)) {
event.target.style.setProperty(&quot;--ripple-x&quot;, `${event.offsetX}px`);
event.target.style.setProperty(&quot;--ripple-y&quot;, `${event.offsetY}px`);
event.target.classList.add(&quot;ripple-start&quot;);
setTimeout(() =&gt; event.target.classList.remove(&quot;ripple-start&quot;), 10);
}
})
&lt;&#x2F;script&gt;
&lt;&#x2F;div&gt;
    
&lt;&#x2F;figure&gt;&lt;h2 id=&quot;final-result&quot;&gt;Final Result&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow external&quot; href=&quot;https:&#x2F;&#x2F;codepen.io&#x2F;Siphalor&#x2F;pen&#x2F;ogYYrqE&quot;&gt;codepen&lt;&#x2F;a&gt; includes some further minor adjustments such as a bouncy easing
for the opacity (&lt;a rel=&quot;nofollow external&quot; href=&quot;https:&#x2F;&#x2F;fwdtools.com&#x2F;css-easing-generator&quot;&gt;this is a nice tool for creating easings&lt;&#x2F;a&gt;) and a subtle
gradient inside the ripple.&lt;&#x2F;p&gt;
&lt;p&gt;I think the result is actually far better than the Material implementation, especially on large surfaces.&lt;&#x2F;p&gt;
&lt;figure class=&quot;padded&quot;&gt;
    &lt;div&gt;
&lt;button class=&quot;demo-button final-ripple&quot;&gt;Button&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button final-ripple primary&quot;&gt;Primary&lt;&#x2F;button&gt;
&lt;button class=&quot;demo-button final-ripple black&quot;&gt;Dark&lt;&#x2F;button&gt;
&lt;style&gt;
@scope {
:scope {
	display: flex;
	flex-wrap: wrap;
	gap: 1em;
	&amp; &gt; * {
		flex: 200px 1 1;
		aspect-ratio: 1;
	}
}
}
&lt;&#x2F;style&gt;
&lt;&#x2F;div&gt;
    
    &lt;figcaption&gt;&lt;p&gt;Some extra-large buttons as a treat :)&lt;&#x2F;p&gt;
&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;&lt;h2 id=&quot;ps&quot;&gt;PS&lt;&#x2F;h2&gt;
&lt;p&gt;Hi, and thanks for reading.&lt;&#x2F;p&gt;
&lt;p&gt;This is the first blog post I&#x27;ve ever written, so this was also an exercise in writing and how to embed the buttons and
code blocks etc.&lt;&#x2F;p&gt;
&lt;p&gt;I hope you liked it :)&lt;&#x2F;p&gt;
&lt;style&gt;
@property --ripple-size-final {
	syntax: &quot;&lt;percentage&gt;&quot;;
	inherits: false;
	initial-value: 100%;
}
@scope {
.demo-button {
	padding: 0.5em 1em;
	border: none;
	border-radius: 0.5em;
	--demo-button-bg: white;
	--demo-button-color: black;
	cursor: pointer;
	background-color: var(--demo-button-bg);
	color: var(--demo-button-color);
	transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
	&amp;:hover {
		background-color: color-mix(in srgb, var(--demo-button-color) 10%, var(--demo-button-bg));
	}
	&amp;:active {
		background-color: color-mix(in srgb, var(--demo-button-color) 20%, var(--demo-button-bg));
	}
	&amp;.primary {
		--demo-button-bg: var(--primary);
	}
	&amp;.black {
		--demo-button-bg: black;
		--demo-button-color: white;
	}
	
	&amp;.final-ripple {
		position: relative;

		&amp;::after {
			content: &quot;&quot;;
			position: absolute;
			inset: 0;
			border-radius: inherit;
			pointer-events: none;

			opacity: 0;
			background: radial-gradient(
				circle farthest-corner at var(--ripple-x, 0) var(--ripple-y, 0),
				color-mix(currentColor, transparent 60%),
				currentColor var(--ripple-size-final),
				transparent var(--ripple-size-final),
				transparent 100%
			);

			transition: opacity 0.6s linear, --ripple-size-final 0.5s ease-out;
		}

		&amp;:active {
			&amp;::after {
				transition: opacity 1s cubic-bezier(0.18, 3, 0.45, 0.82),
					--ripple-size-final 0.5s ease-out;
				opacity: 0.15;
				--ripple-size-final: 100%;
			}
		}
		&amp;.ripple-start {
			&amp;::after {
				transition: none;
				--ripple-size-final: 0%;
			}
		}
	}
}
}
&lt;&#x2F;style&gt;
&lt;script&gt;
document.addEventListener(&#x27;pointerdown&#x27;, (event) =&gt; {
if (event.target.classList.contains(&quot;final-ripple&quot;)) {
event.target.style.setProperty(&quot;--ripple-x&quot;, `${event.offsetX}px`);
event.target.style.setProperty(&quot;--ripple-y&quot;, `${event.offsetY}px`);
event.target.classList.add(&quot;ripple-start&quot;);
setTimeout(() =&gt; event.target.classList.remove(&quot;ripple-start&quot;), 10);
}
})
&lt;&#x2F;script&gt;
</content>
        
    </entry>
</feed>
