← каталог
Ripple Button
Кнопка с Material-рябью, расходящейся из точки клика. CSS + пара строк JS.
cssclickbasic~2 КБreduced-motion ✓
Пропсы
Разобрано из кода записи: обязательные — без «?», у остальных показано значение по умолчанию.
| Проп | Тип | По умолчанию | Что это |
|---|---|---|---|
| children | ReactNode | — |
Установка
npx shadcn@latest add https://motiva.pages.dev/r/ripple-button.jsonЧто нужно в проекте до установки
components.jsonв корне — с алиасами в форме"components": "@/components". Нет файла — CLI не поймёт, куда класть.- Алиас
@объявлен дважды:"paths": {"@/*": ["./src/*"]}в корневомtsconfig.json(в Vite он часто содержит толькоreferences— тогда CLI создаёт папку@) иresolve.aliasв конфиге сборщика. - Токены подключены один раз на приложение:
import "@/styles/motiva-tokens.css"— они приезжают вместе с записью. Без них секция потеряет цвета, ритм и тему. - Своя типографика не должна спорить: если у вас на обёртке
text-align: center, он протечёт внутрь блоков. - Проект без TypeScript? Всё равно нужен минимальный
tsconfig.jsonсpathsи"tsx": trueвcomponents.json: CLI грузит tsconfig безусловно, а с"tsx": falseидёт заjsconfig.jsonи падает так же. Приехавший рядом.tsxможно не трогать — вам нужен.vanilla.js.
Экспорт
Код
.rb {
position: relative;
overflow: hidden;
padding: 0.9rem 2.2rem;
border: none;
border-radius: 12px;
background: linear-gradient(135deg, oklch(0.62 0.19 280), oklch(0.72 0.15 220));
color: #fff;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
isolation: isolate;
}
.rb-ripple {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.45);
transform: scale(0);
animation: rb-ripple 0.6s ease-out;
pointer-events: none;
}
@keyframes rb-ripple {
to {
transform: scale(2.4);
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.rb-ripple {
animation-duration: 0.01s;
}
}
"use client";
import { type MouseEvent, type ReactNode } from "react";
import "./ripple-button.css";
/** Кнопка с Material-рябью из точки клика (CSS + добавление span на клик). */
export function RippleButton({ children, ...props }: { children: ReactNode } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
function onClick(e: MouseEvent<HTMLButtonElement>) {
const btn = e.currentTarget;
const rect = btn.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const span = document.createElement("span");
span.className = "rb-ripple";
span.style.width = span.style.height = `${size}px`;
span.style.left = `${e.clientX - rect.left - size / 2}px`;
span.style.top = `${e.clientY - rect.top - size / 2}px`;
btn.appendChild(span);
span.addEventListener("animationend", () => span.remove());
props.onClick?.(e);
}
return (
<button {...props} className="rb" onClick={onClick}>
{children}
</button>
);
}
Похожие эффекты
лицензия: MITавтор: Motivaисточник: оригинал (Motiva)v1.0.0