diff --git a/src/App.tsx b/src/App.tsx index abcdc21..1f8b0e3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,8 +8,17 @@ import MetricsSimpleCards from '@/components/sections/metrics/MetricsSimpleCards import NavbarCentered from '@/components/ui/NavbarCentered'; import TestimonialRatingCards from '@/components/sections/testimonial/TestimonialRatingCards'; import { Droplets, Flame, Wrench } from "lucide-react"; +import { useEffect } from "react"; +import { applyRippleEffect } from "@/hooks/useButtonClick"; export default function App() { + useEffect(() => { + const buttons = document.querySelectorAll("button"); + buttons.forEach((button) => { + applyRippleEffect(button); + }); + }, []); + return ( <> - {testimonials.map((testimonial) => ( + {testimonials.map((testimonial, index) => (
@@ -73,8 +74,9 @@ const TestimonialRatingCards = ({
-
+
+ {index === 0 && }
{testimonial.name} diff --git a/src/hooks/useButtonClick.ts b/src/hooks/useButtonClick.ts index 5530143..b80d02f 100644 --- a/src/hooks/useButtonClick.ts +++ b/src/hooks/useButtonClick.ts @@ -49,3 +49,30 @@ export const useButtonClick = (href?: string, onClick?: () => void) => { return handleClick; }; + +export const applyRippleEffect = (element: HTMLElement) => { + element.addEventListener("mousedown", (e: MouseEvent) => { + const target = e.currentTarget as HTMLElement; + const ripple = document.createElement("span"); + const rect = target.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const x = e.clientX - rect.left - size / 2; + const y = e.clientY - rect.top - size / 2; + + ripple.style.width = ripple.style.height = `${size}px`; + ripple.style.left = `${x}px`; + ripple.style.top = `${y}px`; + ripple.classList.add("ripple"); + + const existingRipple = target.querySelector(".ripple"); + if (existingRipple) { + existingRipple.remove(); + } + + target.appendChild(ripple); + + ripple.addEventListener("animationend", () => { + ripple.remove(); + }); + }); +}; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 46931a6..40087b1 100644 --- a/src/index.css +++ b/src/index.css @@ -152,6 +152,26 @@ h6 { font-family: '${inter.variable} ${openSans.variable}', sans-serif; } +button { + position: relative; + overflow: hidden; +} + +button::before { + position: absolute; + content: ''; + top: 0; + left: -75%; + width: 50%; + height: 100%; + background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0) 100%); + transform: skewX(-25deg); +} + +button:hover::before { + animation: shine 1s; +} + /* WEBILD_CARD_STYLE */ /* @cards/gradient-radial */ .card { @@ -161,10 +181,16 @@ h6 { /* WEBILD_PRIMARY_BUTTON */ /* @primaryButtons/shadow */ .primary-button { - background: var(--color-primary-cta); + background-image: linear-gradient(to right, var(--color-primary-cta) 0%, var(--color-accent) 50%, var(--color-primary-cta) 100%); + background-size: 200% auto; + transition: background-position 0.5s ease; box-shadow: 2.10837px 3.16256px 9.48767px color-mix(in srgb, var(--color-primary-cta) 40%, transparent); } +.primary-button:hover { + background-position: right center; +} + /* WEBILD_SECONDARY_BUTTON */ /* @secondaryButtons/glass */ .secondary-button { @@ -173,3 +199,8 @@ h6 { box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); border: 1px solid var(--color-secondary-cta); } + +.primary-button:active, +.secondary-button:active { + animation: button-pulse 0.2s ease-in-out; +} diff --git a/src/styles/animations.css b/src/styles/animations.css index d39bb32..15ecdc1 100644 --- a/src/styles/animations.css +++ b/src/styles/animations.css @@ -128,6 +128,18 @@ } } +@keyframes button-pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(0.95); + } + 100% { + transform: scale(1); + } +} + /* Animation classes */ .animate-pulsate { @@ -169,3 +181,25 @@ .animate-progress { animation: progress linear forwards; } + +@keyframes ripple { + to { + transform: scale(4); + opacity: 0; + } +} + +.ripple { + position: absolute; + border-radius: 50%; + background-color: rgba(255, 255, 255, 0.7); + transform: scale(0); + animation: ripple 600ms linear; + pointer-events: none; +} + +@keyframes shine { + 100% { + left: 125%; + } +}