Button
Thu Sep 11 2025
Customizable React button component built on shadcn/ui with 8 color variants, multiple sizes, and TypeScript support for modern web applications
Install - Shadcn UI
我们默认使用 shadcn 作为我们的基础组件库,并且根据 shadcn 组件库的组件增改我们的样式。
另外,我们会使用的是 shadcn ui button 组件的副本,所以可能与您的版本有所不同,可以关注我们是“如何在原有基础上修改”的。
Default Button
更改 shadcn ui 的默认 button。
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 defaultButtonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", { variants: { variant: { default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline", // Primary 颜色变体 "primary-blue": "bg-blue-600 text-white shadow-xs hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600", "primary-green": "bg-green-600 text-white shadow-xs hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600", "primary-purple": "bg-purple-600 text-white shadow-xs hover:bg-purple-700 dark:bg-purple-500 dark:hover:bg-purple-600", "primary-red": "bg-red-600 text-white shadow-xs hover:bg-red-700 dark:bg-red-500 dark:hover:bg-red-600", // Secondary 颜色变体 - 与 secondary 变体相似的设计哲学(颜色深度上调一阶) "secondary-blue": "bg-blue-100 text-blue-900 shadow-xs hover:bg-blue-200 dark:bg-blue-900 dark:text-blue-100 dark:hover:bg-blue-800", "secondary-green": "bg-green-100 text-green-900 shadow-xs hover:bg-green-200 dark:bg-green-900 dark:text-green-100 dark:hover:bg-green-800", "secondary-purple": "bg-purple-100 text-purple-900 shadow-xs hover:bg-purple-200 dark:bg-purple-900 dark:text-purple-100 dark:hover:bg-purple-800", "secondary-red": "bg-red-100 text-red-900 shadow-xs hover:bg-red-200 dark:bg-red-900 dark:text-red-100 dark:hover:bg-red-800", }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", lg: "h-10 rounded-md px-6 has-[>svg]:px-4", icon: "size-9", // customize size huge: "h-12 rounded-md has-[>svg]:px-6 px-8 text-lg", badge: "h-8 text-sm px-4 gap-1 rounded-full", }, }, defaultVariants: { variant: "default", size: "default", }, },);function DefaultButton({ className, variant, size, asChild = false, ...props}: React.ComponentProps<"button"> & VariantProps<typeof defaultButtonVariants> & { asChild?: boolean; }) { const Comp = asChild ? Slot : "button"; return ( <Comp data-slot="button" className={cn(defaultButtonVariants({ variant, size, className }))} {...props} /> );}export { DefaultButton, defaultButtonVariants };大小
颜色
Primary color
Secondary color
Rainbow Button
"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( "rainbow-border relative isolate inline-flex items-center justify-center gap-2 rounded-xl font-black bg-black text-white border-none transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none ", { variants: { size: { default: "py-2 px-4 text-sm", sm: "h-8 py-2 px-3 text-xs", lg: "h-12 px-6 py-3 text-base", icon: "size-10", huge: "h-12 text-lg rounded-md has-[>svg]:px-6 px-8", }, }, defaultVariants: { size: "default", }, },);type RainbowButtonProps = React.ComponentProps<"button"> & VariantProps<typeof rainbowButtonVariants> & { asChild?: boolean; };function RainbowBordersButton({ className, size, asChild = false, children, ...props}: RainbowButtonProps) { const Comp = asChild ? Slot : "button"; return ( <Comp className={cn(rainbowButtonVariants({ size, className }))} {...props}> <span className="relative z-10">{children}</span> {/* 按钮颜色 */} <span className="absolute inset-0 z-[1] rounded-xl bg-black" /> {/* 彩色 border 和背景 */} <style jsx>{` .rainbow-border::before, .rainbow-border::after { content: ""; position: absolute; left: -2px; top: -2px; border-radius: 12px; background: linear-gradient( 45deg, #fb0094, #0000ff, #00ff00, #ffff00, #ff0000, #fb0094, #0000ff, #00ff00, #ffff00, #ff0000 ); background-size: 400%; width: calc(100% + 4px); height: calc(100% + 4px); z-index: -1; animation: rainbow 20s linear infinite; } .rainbow-border::after { filter: blur(50px); } @keyframes rainbow { 0% { background-position: 0 0; } 50% { background-position: 400% 0; } 100% { background-position: 0 0; } } `}</style> </Comp> );}export { RainbowBordersButton, rainbowButtonVariants };Shining Button
Based on Animata
import * as React from "react";import { Slot } from "@radix-ui/react-slot";import { cva, type VariantProps } from "class-variance-authority";import { ArrowLeft, ArrowRight } from "lucide-react";import { cn } from "@/lib/utils";// Shiny button with a hover sweep "shine" implemented via a pseudo-element.// Button-first API with optional `asChild` Slot (shadcn style).const shiningButtonVariants = cva( [ // Layout and interaction container "group relative inline-flex items-center justify-center gap-4 overflow-hidden rounded-lg font-bold", // Focus/ring/a11y "transition-colors focus-visible:outline-none", // Ensure icons inside don't steal pointer events "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-5 [&_svg]:shrink-0", // Shine sweep overlay via ::before so it works with `asChild` "before:pointer-events-none before:absolute before:inset-y-0 before:-left-16 before:h-full before:w-12 before:rotate-[30deg]", "before:bg-gradient-to-r before:from-transparent before:via-white/50 before:to-transparent", "before:transition-[left] before:duration-700 before:ease-out before:will-change-[left] hover:before:left-[calc(100%+1rem)]", "before:scale-y-150 before:z-10", ].join(" "), { variants: { emphasis: { none: "", border: "border-2 border-transparent", ring: "ring-2 ring-transparent", }, variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/85", }, size: { sm: "px-4 py-2 text-sm", default: "px-6 py-4 text-lg", lg: "px-8 py-5 text-xl", }, }, compoundVariants: [ { variant: "default", emphasis: "border", class: "hover:border-primary/80 focus-visible:border-primary", }, { variant: "secondary", emphasis: "border", class: "hover:border-secondary/80 focus-visible:border-secondary", }, { variant: "default", emphasis: "ring", class: " hover:ring-primary/60 focus-visible:ring-primary/80", }, { variant: "secondary", emphasis: "ring", class: "hover:ring-secondary/60 focus-visible:ring-secondary/80", }, ], defaultVariants: { variant: "default", emphasis: "none", size: "default", }, },);export interface ShiningButtonProps extends React.ComponentProps<"button">, VariantProps<typeof shiningButtonVariants> { asChild?: boolean; // use Slot to style a child element (e.g., next/link)}export default function ShiningButton({ className, variant = "default", size, emphasis, asChild = false, children, ...props}: ShiningButtonProps) { const Comp = asChild ? Slot : "button"; // shadcn-style Slot return ( <Comp data-slot="button" className={cn( shiningButtonVariants({ variant, size, emphasis, className, }), )} {...props} > {children} </Comp> );}// Pre-built arrow components for composition with ShiningButton.// Use them inside children to avoid breaking asChild single-child constraint on the root.export function ShiningButtonLeftArrow({ className, ...rest}: React.ComponentProps<"svg">) { return ( <ArrowLeft aria-hidden="true" className={cn( "pointer-events-none size-5 shrink-0 transition-transform duration-300 group-hover:-translate-x-2 group-hover:scale-110", className, )} {...rest} /> );}export function ShiningButtonRightArrow({ className, ...rest}: React.ComponentProps<"svg">) { return ( <ArrowRight aria-hidden="true" className={cn( "pointer-events-none size-5 shrink-0 transition-transform duration-300 group-hover:translate-x-2 group-hover:scale-110", className, )} {...rest} /> );}