Merge version_2_1776677260726 into main #1

Merged
bender merged 3 commits from version_2_1776677260726 into main 2026-04-20 09:31:23 +00:00
6 changed files with 120 additions and 3 deletions

View File

@@ -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 (
<>
<div id="nav" data-section="nav">

View File

@@ -0,0 +1,14 @@
type GlassCardProps = {
className?: string;
};
const GlassCard = ({ className }: GlassCardProps) => {
return (
<div className={`glass-card ${className || ''}`}>
Active Now
</div>
);
};
export default GlassCard;

View File

@@ -4,6 +4,7 @@ import TextAnimation from "@/components/ui/TextAnimation";
import ImageOrVideo from "@/components/ui/ImageOrVideo";
import GridOrCarousel from "@/components/ui/GridOrCarousel";
import { cls } from "@/lib/utils";
import GlassCard from "@/components/cards/GlassCard";
type Testimonial = {
name: string;
@@ -56,7 +57,7 @@ const TestimonialRatingCards = ({
</div>
<GridOrCarousel carouselThreshold={3}>
{testimonials.map((testimonial) => (
{testimonials.map((testimonial, index) => (
<div key={testimonial.name} className="flex flex-col justify-between gap-5 h-full p-5 rounded card">
<div className="flex flex-col items-start gap-5">
<div className="flex gap-1">
@@ -73,8 +74,9 @@ const TestimonialRatingCards = ({
</div>
<div className="flex items-center gap-3">
<div className="size-10 overflow-hidden rounded-full">
<div className="relative size-10 overflow-hidden rounded-full">
<ImageOrVideo imageSrc={testimonial.imageSrc} videoSrc={testimonial.videoSrc} />
{index === 0 && <GlassCard className="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/4" />}
</div>
<div className="flex flex-col">
<span className="text-base font-medium leading-tight">{testimonial.name}</span>

View File

@@ -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();
});
});
};

View File

@@ -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;
}

View File

@@ -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%;
}
}