From e95db23573987dcf1abff63a9fae0b3db3686764 Mon Sep 17 00:00:00 2001 From: Fringg Date: Wed, 25 Feb 2026 08:44:42 +0300 Subject: [PATCH] fix: rewrite HoverBorderGradient with CSS rotate instead of framer-motion Replace framer-motion gradient interpolation (buggy in TG WebView) with pure CSS conic-gradient + transform:rotate() keyframe animation. Transform animations are GPU-accelerated and reliable everywhere. --- src/components/ui/hover-border-gradient.tsx | 75 ++++----------------- tailwind.config.js | 5 ++ 2 files changed, 17 insertions(+), 63 deletions(-) diff --git a/src/components/ui/hover-border-gradient.tsx b/src/components/ui/hover-border-gradient.tsx index 61b2cc8..65f5ff4 100644 --- a/src/components/ui/hover-border-gradient.tsx +++ b/src/components/ui/hover-border-gradient.tsx @@ -1,95 +1,44 @@ -'use client'; -import React, { useState, useEffect } from 'react'; -import { motion } from 'framer-motion'; +import React from 'react'; import { cn } from '@/lib/utils'; -type Direction = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT'; - export function HoverBorderGradient({ children, containerClassName, className, as: Tag = 'button', - duration = 1, - clockwise = true, ...props }: React.PropsWithChildren< { as?: React.ElementType; containerClassName?: string; className?: string; - duration?: number; - clockwise?: boolean; } & React.HTMLAttributes >) { - const [hovered, setHovered] = useState(false); - const [direction, setDirection] = useState('TOP'); - - const rotateDirection = (currentDirection: Direction): Direction => { - const directions: Direction[] = ['TOP', 'LEFT', 'BOTTOM', 'RIGHT']; - const currentIndex = directions.indexOf(currentDirection); - const nextIndex = clockwise - ? (currentIndex - 1 + directions.length) % directions.length - : (currentIndex + 1) % directions.length; - return directions[nextIndex]; - }; - - // Bright accent-colored moving gradient spot - const movingMap: Record = { - TOP: 'radial-gradient(30% 60% at 50% 0%, rgb(var(--color-accent-400)) 0%, transparent 100%)', - LEFT: 'radial-gradient(25% 55% at 0% 50%, rgb(var(--color-accent-400)) 0%, transparent 100%)', - BOTTOM: - 'radial-gradient(30% 60% at 50% 100%, rgb(var(--color-accent-400)) 0%, transparent 100%)', - RIGHT: - 'radial-gradient(25% 55% at 100% 50%, rgb(var(--color-accent-400)) 0%, transparent 100%)', - }; - - // On hover — full bright glow - const highlight = - 'radial-gradient(80% 200% at 50% 50%, rgb(var(--color-accent-500)) 0%, rgb(var(--color-accent-300) / 0.3) 50%, transparent 100%)'; - - useEffect(() => { - if (!hovered) { - const interval = setInterval(() => { - setDirection((prevState) => rotateDirection(prevState)); - }, duration * 1000); - return () => clearInterval(interval); - } - }, [hovered, duration]); - return ( setHovered(true)} - onMouseLeave={() => setHovered(false)} className={cn( - 'relative flex content-center items-center justify-center overflow-visible rounded-xl border-none p-[1.5px] transition duration-500', + 'group relative flex items-center justify-center overflow-hidden rounded-xl p-[1.5px]', containerClassName, )} {...props} > + {/* Rotating conic gradient — pure CSS transform, always smooth */} +
+ {/* Inner content */}
{children}
- -
); } diff --git a/tailwind.config.js b/tailwind.config.js index d107268..82a280c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -173,8 +173,13 @@ export default { 'move-horizontal': 'moveHorizontal 40s ease infinite', 'move-in-circle-fast': 'moveInCircle 20s ease infinite', 'spotlight-ace': 'spotlightAce 2s ease 0.75s 1 forwards', + 'border-spin': 'borderSpin 3s linear infinite', }, keyframes: { + borderSpin: { + '0%': { transform: 'rotate(0deg)' }, + '100%': { transform: 'rotate(360deg)' }, + }, fadeIn: { '0%': { opacity: '0' }, '100%': { opacity: '1' },