基础组件
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" | 按钮的尺寸。 |
disabled | boolean | false | 禁用按钮,阻止交互并降低视觉透明度。 |
asChild | boolean | false | 将按钮样式代理给唯一子元素,而非渲染 <button>。 |