feat: enhance styling and animations for search

- Added rainbow animation to global styles.
- Introduced new color variables for consistent theming.
- Updated AddToSearchBarButton to use the new RainbowButton component.
- Adjusted dialog content size for better layout.
- Removed deprecated RainbowButton component from magicui registry.
This commit is contained in:
Thomas Camlong
2026-01-08 11:29:56 +01:00
parent 9ec2047967
commit a69eedc638
6 changed files with 109 additions and 65 deletions

View File

@@ -152,6 +152,26 @@
--shadow-opacity: var(--shadow-opacity);
--color-shadow-color: var(--shadow-color);
--animate-rainbow: rainbow var(--speed, 2s) infinite linear;
--color-color-5: var(--color-5);
--color-color-4: var(--color-4);
--color-color-3: var(--color-3);
--color-color-2: var(--color-2);
--color-color-1: var(--color-1);
@keyframes rainbow {
0% {
background-position: 0%;
}
100% {
background-position: 200%;
}
}
}
:root {
@@ -223,6 +243,16 @@
--spacing: 0.25rem;
--tracking-normal: 0em;
--color-1: oklch(66.2% 0.225 25.9);
--color-2: oklch(60.4% 0.26 302);
--color-3: oklch(69.6% 0.165 251);
--color-4: oklch(80.2% 0.134 225);
--color-5: oklch(90.7% 0.231 133);
}
.dark {
@@ -284,6 +314,16 @@
--shadow-offset-x: 0px;
--shadow-offset-y: 2px;
--color-1: oklch(66.2% 0.225 25.9);
--color-2: oklch(60.4% 0.26 302);
--color-3: oklch(69.6% 0.165 251);
--color-4: oklch(80.2% 0.134 225);
--color-5: oklch(90.7% 0.231 133);
}
@layer base {
@@ -340,4 +380,4 @@
input.error:focus {
@apply ring-2 ring-destructive ring-offset-2 ring-offset-background;
}
}
}

View File

@@ -2,7 +2,7 @@
import { useState } from "react"
import { Check, Copy, Globe2, Search, Sparkles } from "lucide-react"
import { RainbowButton } from "@/registry/magicui/rainbow-button"
import { RainbowButton } from "@/components/ui/rainbow-button"
import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
@@ -80,18 +80,17 @@ export function AddToSearchBarButton({
variant="outline"
size={size}
className={cn(
"shadow-md px-5 hover:shadow-primary/30 active:scale-[0.99]",
"shadow-sm",
className,
)}
>
<Search className="h-4 w-4" />
<span className="hidden sm:inline">Add to browser search</span>
<span className="hidden sm:inline text-foreground group-hover:scale-105 transition-all duration-300">Add to browser search</span>
<span className="sm:hidden">Add to search</span>
<Sparkles className="h-4 w-4 text-primary" />
</RainbowButton>
</DialogTrigger>
<DialogContent className="sm:max-w-xl">
<DialogContent className="sm:max-w-5xl">
<DialogHeader className="space-y-1">
<DialogTitle className="flex items-center gap-2 text-lg sm:text-xl">
<Globe2 className="h-5 w-5 text-primary" />

View File

@@ -325,7 +325,7 @@ export function IconSearch({ icons }: IconSearchProps) {
</DropdownMenuContent>
</DropdownMenu>
<AddToSearchBarButton size="sm" className="flex-1 sm:flex-none" />
<AddToSearchBarButton className="flex-1 sm:flex-none rounded-sm" />
{/* Clear all button */}
{(searchQuery || selectedCategories.length > 0 || sortOption !== "relevance") && (

View File

@@ -164,3 +164,5 @@ export {
ModalTrigger,
}

View File

@@ -0,0 +1,61 @@
import React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const rainbowButtonVariants = cva(
cn(
"relative cursor-pointer group transition-all animate-rainbow",
"inline-flex items-center justify-center gap-2 shrink-0",
"rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive",
"text-sm font-medium whitespace-nowrap",
"disabled:pointer-events-none disabled:opacity-50",
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0"
),
{
variants: {
variant: {
default:
"border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]",
outline:
"border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-xl px-3 text-xs",
lg: "h-11 rounded-xl px-8",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
interface RainbowButtonProps
extends
React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof rainbowButtonVariants> {
asChild?: boolean
}
const RainbowButton = React.forwardRef<HTMLButtonElement, RainbowButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(rainbowButtonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
RainbowButton.displayName = "RainbowButton"
export { RainbowButton, rainbowButtonVariants, type RainbowButtonProps }

View File

@@ -1,58 +0,0 @@
"use client";
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const rainbowButtonVariants = cva(
"relative inline-flex items-center justify-center overflow-hidden rounded-full font-semibold transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
{
variants: {
variant: {
default: "text-white shadow-lg",
outline: "text-foreground border border-border bg-background/80 backdrop-blur-sm",
},
size: {
sm: "h-9 px-4 text-sm",
default: "h-10 px-6 text-sm",
lg: "h-12 px-8 text-base",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface RainbowButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof rainbowButtonVariants> {
asChild?: boolean
}
const RainbowButton = React.forwardRef<HTMLButtonElement, RainbowButtonProps>(
({ className, variant, size, asChild = false, children, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
ref={ref}
className={cn("group isolate", rainbowButtonVariants({ variant, size }), className)}
{...props}
>
<span
aria-hidden
className="absolute inset-0 -z-10 animate-[spin_6s_linear_infinite] bg-[conic-gradient(#f97316,#f43f5e,#a855f7,#06b6d4,#22c55e,#f59e0b,#f97316)] opacity-80"
/>
<span aria-hidden className="absolute inset-[1px] -z-0 rounded-full bg-background/80" />
<span className="relative z-10 flex items-center gap-2">{children}</span>
</Comp>
)
},
)
RainbowButton.displayName = "RainbowButton"
export { RainbowButton, rainbowButtonVariants }