Ripple Button
An animated Ripple Button styled with Tailwind CSS.
Example#
import {Button} from 'react-aria-components';
import {useEffect, useRef, useState} from 'react';
import Airplane from '@spectrum-icons/workflow/Airplane';
function RippleButton(props) {
const [coords, setCoords] = useState({ x: -1, y: -1 });
const [isRippling, setIsRippling] = useState(false);
let timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
let onPress = (e) => {
setCoords({ x: e.x, y: e.y });
if (e.x !== -1 && e.y !== -1) {
setIsRippling(true);
timeout.current = setTimeout(() => setIsRippling(false), 600);
}
};
useEffect(() => {
return () => {
clearTimeout(timeout.current);
};
}, []);
return (
<div className="bg-gradient-to-r from-teal-300 to-cyan-500 p-12 rounded-lg flex justify-center">
<Button
onPress={onPress}
className={`
relative overflow-hidden min-h-16
inline-flex items-center justify-center rounded-md bg-black bg-opacity-50 bg-clip-padding border border-white/20 px-3.5 py-2 font-medium text-white
hover:bg-opacity-60 pressed:bg-opacity-70 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
>
{isRippling
? (
<div
className="ripple"
style={{
left: coords.x - 15,
top: coords.y - 15
}}
/>
)
: (
''
)}
<span className="flex items-center gap-2">{props.children}</span>
</Button>
</div>
);
}
<RippleButton>
<Airplane size="S" /> Book flight
</RippleButton>
import {Button} from 'react-aria-components';
import {useEffect, useRef, useState} from 'react';
import Airplane from '@spectrum-icons/workflow/Airplane';
function RippleButton(props) {
const [coords, setCoords] = useState({ x: -1, y: -1 });
const [isRippling, setIsRippling] = useState(false);
let timeout = useRef<
ReturnType<typeof setTimeout> | null
>(null);
let onPress = (e) => {
setCoords({ x: e.x, y: e.y });
if (e.x !== -1 && e.y !== -1) {
setIsRippling(true);
timeout.current = setTimeout(
() => setIsRippling(false),
600
);
}
};
useEffect(() => {
return () => {
clearTimeout(timeout.current);
};
}, []);
return (
<div className="bg-gradient-to-r from-teal-300 to-cyan-500 p-12 rounded-lg flex justify-center">
<Button
onPress={onPress}
className={`
relative overflow-hidden min-h-16
inline-flex items-center justify-center rounded-md bg-black bg-opacity-50 bg-clip-padding border border-white/20 px-3.5 py-2 font-medium text-white
hover:bg-opacity-60 pressed:bg-opacity-70 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
>
{isRippling
? (
<div
className="ripple"
style={{
left: coords.x - 15,
top: coords.y - 15
}}
/>
)
: (
''
)}
<span className="flex items-center gap-2">
{props.children}
</span>
</Button>
</div>
);
}
<RippleButton>
<Airplane size="S" /> Book flight
</RippleButton>
import {Button} from 'react-aria-components';
import {
useEffect,
useRef,
useState
} from 'react';
import Airplane from '@spectrum-icons/workflow/Airplane';
function RippleButton(
props
) {
const [
coords,
setCoords
] = useState({
x: -1,
y: -1
});
const [
isRippling,
setIsRippling
] = useState(false);
let timeout = useRef<
ReturnType<
typeof setTimeout
> | null
>(null);
let onPress = (e) => {
setCoords({
x: e.x,
y: e.y
});
if (
e.x !== -1 &&
e.y !== -1
) {
setIsRippling(
true
);
timeout.current =
setTimeout(
() =>
setIsRippling(
false
),
600
);
}
};
useEffect(() => {
return () => {
clearTimeout(
timeout.current
);
};
}, []);
return (
<div className="bg-gradient-to-r from-teal-300 to-cyan-500 p-12 rounded-lg flex justify-center">
<Button
onPress={onPress}
className={`
relative overflow-hidden min-h-16
inline-flex items-center justify-center rounded-md bg-black bg-opacity-50 bg-clip-padding border border-white/20 px-3.5 py-2 font-medium text-white
hover:bg-opacity-60 pressed:bg-opacity-70 transition-colors cursor-default outline-none focus-visible:ring-2 focus-visible:ring-white/75`}
>
{isRippling
? (
<div
className="ripple"
style={{
left:
coords
.x -
15,
top:
coords
.y -
15
}}
/>
)
: (
''
)}
<span className="flex items-center gap-2">
{props
.children}
</span>
</Button>
</div>
);
}
<RippleButton>
<Airplane size="S" />
{' '}
Book flight
</RippleButton>
div.ripple {
position: absolute;
height: 30px;
width: 30px;
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: ripple 600ms linear;
background-color: rgba(255, 255, 255, .6);
}
@keyframes ripple {
from {
transform: scale(0);
opacity: 1;
}
to {
transform: scale(6);
opacity: 0;
}
}
div.ripple {
position: absolute;
height: 30px;
width: 30px;
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: ripple 600ms linear;
background-color: rgba(255, 255, 255, .6);
}
@keyframes ripple {
from {
transform: scale(0);
opacity: 1;
}
to {
transform: scale(6);
opacity: 0;
}
}
div.ripple {
position: absolute;
height: 30px;
width: 30px;
border-radius: 50%;
transform: scale(0);
opacity: 1;
animation: ripple 600ms linear;
background-color: rgba(255, 255, 255, .6);
}
@keyframes ripple {
from {
transform: scale(0);
opacity: 1;
}
to {
transform: scale(6);
opacity: 0;
}
}
Tailwind config#
This example uses the tailwindcss-react-aria-components plugin. Add it to your tailwind.config.js
:
module.exports = {
// ...
plugins: [
require('tailwindcss-react-aria-components')
]
};
module.exports = {
// ...
plugins: [
require('tailwindcss-react-aria-components')
]
};
module.exports = {
// ...
plugins: [
require(
'tailwindcss-react-aria-components'
)
]
};
Components#
Button
A ripple helps indicate where an interaction has taken place.