Forms✨ Premium

Radio Ripple

CSS-only radio buttons: the chosen option's dot pops in with a bounce and a light ripple spreads and fades around the circle, on real native radios.

CSSAnimationRadio

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Forms has 20 effects, including 2 free. Explore the category →

3 usage examples

In-store pick-up time slot during an order — Radio Ripple example 1

① In-store pick-up time slot during an order

WhenAt the "Pick-up" step, the customer picks when they will collect their order: a click on "5 pm – 7 pm" pops the green dot in, the ripple leaves the circle and the slot's card takes a tint.
WhyOne slot, and only one: the textbook case for a radio button. The bounce confirms the choice at a step where the customer must not get the time wrong.
SettingsThree options as clickable cards, green #1f5f4a for the border, the dot (::before) and the ripple (::after at 22%). The chosen card's background comes from the scene's CSS.
Quick poll at the end of a local news article — Radio Ripple example 2

② Quick poll at the end of a local news article

WhenThe reader reaches the end of the article and answers "Should the market square be car-free on Saturdays?" with a tap: the dot pops in and the results show next to the three answers.
WhyThree short answers, one tap: the ripple makes the answer feel alive without slowing anyone down, and the reader sees at once that it counted.
SettingsBurgundy #7f1d1d, 14 px options. The result bars and the "Thanks" are shown by the scene when a radio changes; the percentages are hard-coded for the demo.
Listening speed in an audiobook app's player — Radio Ripple example 3

③ Listening speed in an audiobook app's player

WhenBefore resuming the book, the listener picks 1×, 1.25× or 1.5×; each option shows the time left in the chapter at that speed. From the keyboard, the arrows move from one speed to the next, replaying the bounce and the ripple, then Enter on "Resume listening".
WhyThree speeds, only one active: a radio button is the right control, and the large cards keep each choice's duration readable. The bounce confirms the change without pulling attention away from the book.
SettingsOptions as large cards on a purple background, yellow #fbbf24 for the circle, the dot and the ripple, fieldset legend left visible ("Listening speed"), name="vitesse". The book cover is drawn in CSS by the scene.

How it works

The group is a <fieldset class="rrp-group"> whose <legend> names the question (hidden from sight, read by screen readers). Each option is a <label class="rrp-option"> holding the real <input type="radio">, the drawn circle .rrp-circle (22 px, 2 px #6366f1 border) and its text. The radio is transparent and sits under the circle: it keeps the click, the focus, the arrow keys and form submission.

Two pseudo-elements make the animation. ::before is the centre dot: at scale(0) at rest, it goes to scale(1) when the radio is checked, in 0.3 s on a cubic-bezier(.68,-.55,.265,1.55) curve that makes it overshoot then settle. ::after is a 20% indigo disc reaching 8 px beyond the circle; on selection, the rrp-ripple animation (0.4 s) grows it from 0 to 1.5 times while fading it out: that is the ripple.

No JavaScript. The radios of one group share a name (here option): it is what allows a single choice, and what your server receives. The option checked in the HTML (checked) also plays its ripple on load.

Accessibility

  • Real radios: Tab enters the group on the checked option, up / down arrows change the choice, a screen reader announces "Choose an option, group, Option 1, radio button, checked, 1 of 3".
  • Group name: the fieldset's legend is read before the first option; write the real question in it ("Pick-up time slot").
  • Visible focus: from the keyboard, an #a5b4fc ring surrounds the active option's circle (input:focus-visible + .rrp-circle).
  • Reduced motion: under prefers-reduced-motion: reduce, the dot appears with no bounce and the ripple is not played.
  • Hit area: the whole row (circle and text) is clickable.

Browser compatibility

CSS only: :checked, pseudo-elements, @keyframes, inset (Chrome 87, Firefox 66, Safari 14.1). Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): click, Tab then arrow down, touch at 390 px, reduced motion. The focus ring needs :focus-visible (Chrome 86, Firefox 85, Safari 15.4).

Chrome 87+✓ Full (measured)
Firefox 85+✓ Full (measured)
Safari 15.4+✓ Full (measured in WebKit)
Edge 87+✓ Full (Chromium engine)
Mobile iOS 15.4+✓ WebKit engine, not checked on a device
Android Chrome 87+✓ Full (touch measured at 390 px)

Without inset (browsers from 2020 and earlier), the dot and ripple are not drawn but the radios remain usable. Without :focus-visible, no ring from the keyboard.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0296-root">
  <fieldset class="rrp-group">
    <legend class="rrp-legend">Choose an option</legend>
    <div class="rrp-list">
      <label class="rrp-option">
        <input type="radio" name="option" value="1" checked>
        <span class="rrp-circle" aria-hidden="true"></span>
        <span>Option 1</span>
      </label>
      <!-- Option 2, Option 3: same structure -->
    </div>
  </fieldset>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Colour (CSS — border-color of .rrp-circle, background of ::before and ::after) #6366f1, ripple rgba(99,102,241,.2) Change all three together; keep the ripple very transparent (20 to 25%).
Dot bounce (CSS — transition of .rrp-circle::before) .3s cubic-bezier(.68,-.55,.265,1.55) ease-out for a dot that appears without overshooting.
Ripple (CSS — @keyframes rrp-ripple and inset of ::after) 0.4 s, from 0 to 1.5 times, 8 px overflow Set inset to -14px and the duration to .6s for a wider, slower ripple.
Circle size (CSS — .rrp-circle and the input) 22px Give the invisible input the same size to keep the hit area under the circle.
Group name (HTML — name and <legend>) option · "Choose an option" A different name per question; the legend can stay visible by removing the rrp-legend class.
Demo frame (CSS — .fx-0296-root) min-height 300px, #0a0a0f background Remove it in a real form: the text then takes the page's colour.

FAQ

Both copies have the same name, so the browser treats them as one group: only one radio can be checked across the page. Give each question its own name ("slot", "delivery"…); it is also what tells the answers apart on the server.
Yes, remove checked. Then add required to one of the radios if an answer is mandatory: the browser will block the send.
Yes: set .rrp-list to flex-direction: row with a gap; the left / right arrow keys work too.