엘리먼트
리플 버튼
누른 위치에서 파동이 퍼지는 마이크로 인터랙션 버튼
- #button
- #micro-interaction
- #ripple
1280px
설치
이 항목은 아직 설치할 수 없어요. 소스를 참고해 주세요. 설치할 수 없는 항목 알아보기
AI 프롬프트
Build a button that spawns a ripple span at the pointer-down position and scales it to 28x while fading out over 700ms using var(--ds-ease-out).
demo.module.cssdemo.module.cssdemo.module.csscss
.stage {
min-height: 100vh;
display: grid;
place-items: center;
}
.button {
position: relative;
overflow: hidden;
padding: 14px 28px;
border: 0;
border-radius: var(--ds-radius-md);
background: var(--ds-color-brand-blue);
color: var(--ds-color-on-blue);
font-weight: var(--label-weight, 700);
cursor: pointer;
transition: transform 160ms var(--ds-ease-out);
}
.button:active {
transform: scale(0.97);
}
.ripple {
position: absolute;
width: 12px;
height: 12px;
margin: -6px 0 0 -6px;
border-radius: 50%;
background: currentColor;
opacity: 0.35;
animation: ripple 700ms var(--ds-ease-out) forwards;
pointer-events: none;
}
@keyframes ripple {
to {
transform: scale(28);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.ripple {
animation: none;
opacity: 0;
}
}
demo.tsxdemo.tsxdemo.tsxtsx
"use client";
import { useState, type PointerEvent } from "react";
import { playUiSound } from "@/components/arc/sound/play";
import styles from "./demo.module.css";
type Ripple = { id: number; x: number; y: number };
export default function Demo() {
const [ripples, setRipples] = useState<Ripple[]>([]);
const onPointerDown = (event: PointerEvent<HTMLButtonElement>) => {
playUiSound("click");
const rect = event.currentTarget.getBoundingClientRect();
const id = Date.now() + Math.random();
setRipples((current) => [...current, { id, x: event.clientX - rect.left, y: event.clientY - rect.top }]);
window.setTimeout(() => setRipples((current) => current.filter((r) => r.id !== id)), 700);
};
return (
<div className={styles.stage}>
<button type="button" className={styles.button} onPointerDown={onPointerDown}>
눌러 보기
{ripples.map((ripple) => (
<span key={ripple.id} className={styles.ripple} style={{ left: ripple.x, top: ripple.y }} />
))}
</button>
</div>
);
}