Skip to main content
PolyUI/docs

基础组件

Button 按钮

主操作按钮,支持 default / secondary / outline / ghost / destructive 变体与 sm / default / lg 尺寸。

安装

bash
npx polyui add button

变体

提供 6 种视觉变体:default、secondary、outline、ghost、destructive、link。

tsx
import { Button } from "@polyui/react/button"
export function ButtonVariants() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button variant="default">Default</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="link">Link</Button>
    </div>
  )
}

尺寸

支持 sm、default、lg 三种尺寸,以及专为图标设计的 icon 尺寸。

tsx
import { Button } from "@polyui/react/button"
import { PlusIcon, SearchIcon, StarIcon, BellIcon } from "lucide-react"
export function ButtonSizes() {
  return (
    <div className="flex flex-col gap-3">
      <div className="flex flex-wrap items-center gap-2">
        <Button size="xs">Extra Small</Button>
        <Button size="sm">Small</Button>
        <Button size="default">Default</Button>
        <Button size="lg">Large</Button>
      </div>
      <div className="flex flex-wrap items-center gap-2">
        <Button size="icon-xs" variant="outline" aria-label="icon xs">
          <PlusIcon />
        </Button>
        <Button size="icon-sm" variant="outline" aria-label="icon sm">
          <SearchIcon />
        </Button>
        <Button size="icon" variant="outline" aria-label="icon">
          <StarIcon />
        </Button>
        <Button size="icon-lg" variant="outline" aria-label="icon lg">
          <BellIcon />
        </Button>
      </div>
    </div>
  )
}

带图标

tsx
import { Button } from "@polyui/react/button"
import { PlusIcon, ArrowRightIcon, ArrowLeftIcon, TrashIcon, Undo2Icon, Redo2Icon, DownloadIcon, BookmarkIcon } from "lucide-react"
export function ButtonIcons() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button>
        <PlusIcon />
        New
      </Button>
      <Button variant="outline">
        <Undo2Icon />
        Undo
      </Button>
      <Button variant="outline">
        Redo
        <Redo2Icon />
      </Button>
      <Button className="group">
        Get In Touch
        <ArrowRightIcon className="transition-transform duration-200 group-hover/button:translate-x-0.5" />
      </Button>
      <Button variant="ghost" className="group">
        <ArrowLeftIcon className="transition-transform duration-200 group-hover/button:-translate-x-0.5" />
        Go to Settings
      </Button>
      <Button variant="outline">
        <DownloadIcon />
        Download
      </Button>
      <Button size="icon" variant="outline" aria-label="bookmark">
        <BookmarkIcon />
      </Button>
      <Button size="icon" variant="ghost" aria-label="delete">
        <TrashIcon />
      </Button>
    </div>
  )
}

加载中

通过 disabled 属性禁用按钮,配合 Loader2 图标展示加载中状态。

tsx
import { Button } from "@polyui/react/button"
import { LoaderCircleIcon } from "lucide-react"
export function ButtonLoading() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button disabled>
        <LoaderCircleIcon className="animate-spin" />
        Loading
      </Button>
      <Button variant="outline" disabled>
        <LoaderCircleIcon className="animate-spin" />
        Saving...
      </Button>
      <Button variant="secondary" disabled>
        <LoaderCircleIcon className="animate-spin" />
        Processing
      </Button>
      <Button size="icon" disabled aria-label="loading">
        <LoaderCircleIcon className="animate-spin" />
      </Button>
    </div>
  )
}

徽章

tsx
import { Button } from "@polyui/react/button"
import { BellIcon, MailCheckIcon, HeartIcon } from "lucide-react"
export function ButtonBadge() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Button variant="outline">
        <MailCheckIcon />
        Messages
        <span className="inline-flex items-center justify-center rounded-full bg-destructive px-1.5 py-px text-xs font-medium text-destructive-foreground tabular-nums leading-none">
          99+
        </span>
      </Button>
      <Button variant="outline" className="relative">
        <BellIcon />
        Notifications
        <span className="absolute -top-2.5 -right-2.5 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-destructive px-1 text-xs font-medium text-destructive-foreground tabular-nums">
          8
        </span>
      </Button>
      <Button variant="outline" size="icon" className="relative" aria-label="messages">
        <MailCheckIcon />
        <span className="absolute -top-2.5 -right-2.5 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-destructive px-1 text-xs font-medium text-destructive-foreground tabular-nums">
          8
        </span>
      </Button>
      <Button variant="ghost">
        <HeartIcon />
        2.0K
      </Button>
    </div>
  )
}

分组

tsx
import { Button } from "@polyui/react/button"
import { Undo2Icon, Redo2Icon, DownloadIcon, ShieldCheckIcon, EllipsisIcon, ShieldAlertIcon } from "lucide-react"
export function ButtonGroup() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <div className="flex gap-2">
        <Button variant="secondary">Cancel</Button>
        <Button>Save Changes</Button>
      </div>
      <div className="flex gap-2">
        <Button className="bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40">
          Reject
          <ShieldAlertIcon />
        </Button>
        <Button className="bg-green-600/10 text-green-600 hover:bg-green-600/20 focus-visible:ring-green-600/20 dark:bg-green-400/10 dark:text-green-400 dark:hover:bg-green-400/20 dark:focus-visible:ring-green-400/40">
          Approve
          <ShieldCheckIcon />
        </Button>
      </div>
      <div className="flex" data-slot="button-group">
        <Button size="icon-sm" variant="outline" className="rounded-r-none" aria-label="undo">
          <Undo2Icon />
        </Button>
        <Button size="icon-sm" variant="outline" className="rounded-l-none border-l-0" aria-label="redo">
          <Redo2Icon />
        </Button>
      </div>
      <div className="flex gap-1">
        <Button variant="outline" size="sm">
          <DownloadIcon />
          Export
        </Button>
        <Button variant="ghost" size="icon-sm" aria-label="more options">
          <EllipsisIcon />
        </Button>
      </div>
    </div>
  )
}

语义颜色

tsx
import { Button } from "@polyui/react/button"
import { Trash2Icon, CopyIcon, SaveIcon, ShieldCheckIcon, AlertTriangleIcon } from "lucide-react"
export function ButtonSemantic() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button className="bg-amber-600/10 text-amber-600 hover:bg-amber-600/20 focus-visible:ring-amber-600/20 dark:bg-amber-400/10 dark:text-amber-400 dark:hover:bg-amber-400/20 dark:focus-visible:ring-amber-400/40">
        <AlertTriangleIcon />
        Caution
      </Button>
      <Button className="bg-sky-600/10 text-sky-600 hover:bg-sky-600/20 focus-visible:ring-sky-600/20 dark:bg-sky-400/10 dark:text-sky-400 dark:hover:bg-sky-400/20 dark:focus-visible:ring-sky-400/40">
        <SaveIcon />
        Save
      </Button>
      <Button className="bg-green-600/10 text-green-600 hover:bg-green-600/20 focus-visible:ring-green-600/20 dark:bg-green-400/10 dark:text-green-400 dark:hover:bg-green-400/20 dark:focus-visible:ring-green-400/40">
        <ShieldCheckIcon />
        Approved
      </Button>
      <Button className="bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40">
        <Trash2Icon />
        Discard
      </Button>
      <Button
        variant="outline"
        className="border-sky-600 text-sky-600! hover:bg-sky-600/10 focus-visible:border-sky-600 focus-visible:ring-sky-600/20 dark:border-sky-400 dark:text-sky-400! dark:hover:bg-sky-400/10 dark:focus-visible:border-sky-400 dark:focus-visible:ring-sky-400/40"
      >
        <CopyIcon />
        Duplicate
      </Button>
      <Button
        variant="outline"
        className="hover:bg-destructive/10! text-destructive! border-destructive! focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40"
      >
        <Trash2Icon />
        Discard
      </Button>
    </div>
  )
}

渐变

tsx
import { Button } from "@polyui/react/button"
import { TrashIcon, ZapIcon, ShieldAlertIcon } from "lucide-react"
export function ButtonGradient() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button className="from-primary via-primary/60 to-primary bg-transparent bg-gradient-to-r [background-size:200%_auto] hover:bg-transparent hover:bg-[99%_center]">
        Get Started
      </Button>
      <Button className="bg-transparent bg-gradient-to-r from-amber-600 via-amber-600/60 to-amber-600 [background-size:200%_auto] text-white hover:bg-transparent hover:bg-[99%_center] focus-visible:ring-amber-600/20 dark:from-amber-400 dark:via-amber-400/60 dark:to-amber-400 dark:focus-visible:ring-amber-400/40">
        Upgrade
        <ZapIcon />
      </Button>
      <Button className="from-destructive via-destructive/60 to-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 bg-transparent bg-gradient-to-r [background-size:200%_auto] text-white hover:bg-transparent hover:bg-[99%_center]">
        <TrashIcon />
        Delete
      </Button>
      <Button
        size="icon"
        className="from-destructive via-destructive/60 to-destructive focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 bg-transparent bg-gradient-to-r [background-size:200%_auto] text-white hover:bg-transparent hover:bg-[99%_center]"
        aria-label="security alert"
      >
        <ShieldAlertIcon />
      </Button>
    </div>
  )
}

纯图标

tsx
import { Button } from "@polyui/react/button"
import { CopyIcon, BellIcon, BookmarkIcon, CheckCheckIcon, MenuIcon, SunIcon } from "lucide-react"
export function ButtonIconOnly() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Button size="icon" variant="outline" aria-label="bookmark">
        <BookmarkIcon />
      </Button>
      <Button size="icon" variant="ghost" aria-label="menu">
        <MenuIcon />
      </Button>
      <Button size="icon" variant="outline" aria-label="theme">
        <SunIcon />
      </Button>
      <Button
        size="icon"
        className="bg-green-600/10 text-green-600 hover:bg-green-600/20 focus-visible:ring-green-600/20 dark:bg-green-400/10 dark:text-green-400 dark:hover:bg-green-400/20 dark:focus-visible:ring-green-400/40"
        aria-label="check"
      >
        <CheckCheckIcon />
      </Button>
      <Button variant="outline" size="icon" className="relative" aria-label="notifications">
        <BellIcon />
        <span className="absolute -top-0.5 -right-0.5 size-2 animate-bounce rounded-full bg-sky-600 dark:bg-sky-400" />
      </Button>
      <Button
        size="icon"
        className="rounded-full bg-sky-600 text-white hover:bg-sky-600/90 focus-visible:ring-sky-600/20 dark:bg-sky-400/60 dark:focus-visible:ring-sky-400/40"
        aria-label="copy link"
      >
        <CopyIcon />
      </Button>
    </div>
  )
}

形状

https://polyui.dev/docs/components/button

tsx
import { Button } from "@polyui/react/button"
import { StarIcon, CopyIcon, ShareIcon } from "lucide-react"
export function ButtonShapes() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Button className="rounded-full">
        <StarIcon />
        Star
      </Button>
      <Button variant="outline" className="h-12 rounded-full px-2.5">
        <span className="bg-primary text-primary-foreground flex size-7 items-center justify-center rounded-full">
          <ShareIcon />
        </span>
        Publish
      </Button>
      <div className="flex h-11.5 items-center overflow-hidden rounded-full border px-1">
        <p className="text-muted-foreground max-w-48 truncate overflow-hidden px-2.5 text-sm">
          https://polyui.dev/docs/components/button
        </p>
        <Button
          size="icon"
          className="rounded-full bg-sky-600 text-white hover:bg-sky-600/90 focus-visible:ring-sky-600/20 dark:bg-sky-400/60 dark:focus-visible:ring-sky-400/40"
          aria-label="copy"
        >
          <CopyIcon />
        </Button>
      </div>
    </div>
  )
}

悬停效果

tsx
import { Button } from "@polyui/react/button"
export function ButtonHover() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button className="ring-offset-background hover:ring-primary/90 transition-all duration-300 hover:ring-2 hover:ring-offset-2">
        Ring Hover
      </Button>
      <Button
        className="relative overflow-hidden before:absolute before:inset-0 before:rounded-[inherit]
          before:bg-[linear-gradient(45deg,transparent_25%,rgba(255,255,255,0.5)_50%,transparent_75%,transparent_100%)]
          before:bg-[length:250%_250%,100%_100%] before:bg-[position:200%_0,0_0] before:bg-no-repeat
          before:transition-[background-position_0s_ease] before:duration-1000
          hover:before:bg-[position:-100%_0,0_0]
          dark:before:bg-[linear-gradient(45deg,transparent_25%,rgba(0,0,0,0.2)_50%,transparent_75%,transparent_100%)]"
      >
        Shine Hover
      </Button>
      <Button
        variant="link"
        className="after:bg-primary relative !no-underline after:absolute after:bottom-2 after:h-px after:w-2/3 after:origin-bottom-right after:scale-x-0 after:transition-transform after:duration-300 after:ease-in-out hover:after:origin-bottom-left hover:after:scale-x-100"
      >
        Contact Us
      </Button>
    </div>
  )
}

虚线

tsx
import { Button } from "@polyui/react/button"
import { PlusIcon, DownloadIcon } from "lucide-react"
export function ButtonDashed() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button variant="outline" className="border-dashed border-primary shadow-none">
        <DownloadIcon />
        Download
      </Button>
      <Button variant="outline" className="border-dashed">
        <PlusIcon />
        Add Item
      </Button>
    </div>
  )
}

社交登录

tsx
import { Button } from "@polyui/react/button"
export function ButtonSocial() {
  return (
    <div className="flex flex-col gap-3">
      <div className="flex flex-wrap gap-2 items-center">
        <Button variant="outline" size="icon" aria-label="Google">
          <img
            src="https://cdn.shadcnstudio.com/ss-assets/brand-logo/google-icon.png?width=20&height=20&format=auto"
            alt="Google"
            className="size-5"
          />
        </Button>
        <Button variant="outline" size="icon" aria-label="X / Twitter">
          <img
            src="https://cdn.shadcnstudio.com/ss-assets/brand-logo/twitter-icon.png?width=20&height=20&format=auto"
            alt="X"
            className="size-5 dark:invert"
          />
        </Button>
        <Button variant="outline" size="icon" aria-label="GitHub">
          <img
            src="https://cdn.shadcnstudio.com/ss-assets/brand-logo/github-icon.png?width=20&height=20&format=auto"
            alt="GitHub"
            className="size-5 dark:invert"
          />
        </Button>
      </div>
      <div className="flex flex-col gap-2 w-full max-w-64">
        <Button variant="outline" className="!border-[#e84133] !text-[#e84133]">
          <img
            src="https://cdn.shadcnstudio.com/ss-assets/brand-logo/google-icon.png?width=20&height=20&format=auto"
            alt="Google"
            className="size-5"
          />
          <span className="flex flex-1 justify-center">Continue with Google</span>
        </Button>
        <Button variant="outline" className="border-black text-black dark:border-white dark:text-white">
          <img
            src="https://cdn.shadcnstudio.com/ss-assets/brand-logo/github-icon.png?width=20&height=20&format=auto"
            alt="GitHub"
            className="size-5 dark:invert"
          />
          <span className="flex flex-1 justify-center">Continue with GitHub</span>
        </Button>
      </div>
    </div>
  )
}

禁用

tsx
import { Button } from "@polyui/react/button"
import { Trash2Icon } from "lucide-react"
export function ButtonDisabled() {
  return (
    <div className="flex flex-wrap gap-2">
      <Button disabled>Disabled Default</Button>
      <Button variant="outline" disabled>
        Disabled Outline
      </Button>
      <Button variant="secondary" disabled>
        Verify Email
      </Button>
      <Button variant="destructive" disabled>
        <Trash2Icon />
        Delete
      </Button>
      <Button variant="ghost" disabled>
        Ghost Disabled
      </Button>
    </div>
  )
}

特殊效果

tsx
import { Button } from "@polyui/react/button"
import { CheckIcon } from "lucide-react"
export function ButtonSpecial() {
  return (
    <div className="flex flex-wrap gap-2">
      <button className="group relative rounded-lg border-2 border-sky-500 bg-sky-500 px-4 py-2 text-sm font-medium text-white hover:shadow-lg">
        <span className="absolute top-0 left-0 size-full rounded-md border border-dashed border-sky-50 shadow-inner shadow-white/30 group-active:shadow-white/10" />
        <span className="absolute top-0 left-0 size-full rotate-180 rounded-md border-sky-50 shadow-inner shadow-black/30 group-active:shadow-black/10" />
        Stitches Button
      </button>
      <Button variant="outline" className="relative disabled:opacity-100">
        <span className="scale-100 opacity-100">
          <CheckIcon className="stroke-green-600 dark:stroke-green-400" />
        </span>
        Copied!
      </Button>
    </div>
  )
}

As Child

使用 asChild 将按钮样式应用到子元素上,常用于包裹 <a> 链接或 Next.js Link。

tsx
import { Button } from "@polyui/react/button"
export function ButtonAsChild() {
  return (
    <div className="flex gap-2">
      <Button asChild>
        <a href="/signup">Get Started</a>
      </Button>
      <Button asChild variant="outline">
        <a href="/docs">Read the docs</a>
      </Button>
    </div>
  )
}

属性

属性类型默认值说明
variant"default" | "secondary" | "outline" | "ghost" | "destructive" | "link""default"按钮的视觉变体。
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"按钮的尺寸。
disabledbooleanfalse禁用按钮,阻止交互并降低视觉透明度。
asChildbooleanfalse将按钮样式代理给唯一子元素,而非渲染 <button>。