Skip to main content

使用 tailwind 創建客製化元件

可搭配使用套件

import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import * as React from 'react'
import { twMerge as cn } from 'tailwind-merge'


const buttonVariants = cva(
//預設樣式
'relative flex flex-nowrap items-center justify-center text-md font-semibold enabled:cursor-pointer',
{
variants: {
//定義不同的按鈕類型(primary、secondary、third、outline、text)以及圓角半徑(sm、md、lg)
$variant: {
primary: 'bg-primary-p1 text-primary-on_primary',
secondary: 'bg-background-2nd text-label-l1',
third: 'bg-third-p1 text-third-on_third',
outline:
'border-label-l3 bg-background-1st text-label-l1 disabled:text-label-l3',
text: 'bg-transparent text-primary-p1',
},
$radius: {
sm: 'rounded-[2px]',
md: 'rounded-[6px]',
lg: 'rounded-[8px]',
}
},
// 預設樣式設定
defaultVariants: {
$variant: 'primary',
$radius: 'md'
}
}
)
export type ButtonVariants = VariantProps<typeof buttonVariants>

const sizeVariants = cva('', {
variants: {
$size: {
xs: 'px-[6px] py-[3px]',
sm: 'px-[12px] py-[6px]',
md: 'px-[14px] py-[10px]',
lg: 'px-[18px] py-[14px]',
}
},
defaultVariants: {
$size: 'md'
}
})

const iconSizeVariants = cva('', {
variants: {
$size: {
xs: 'h-[16px] w-[16px]',
sm: 'h-[24px] w-[24px]',
md: 'h-[32px] w-[32px]',
lg: 'h-[40px] w-[40px]',
xl: 'h-[48px] w-[48px]'
}
},
defaultVariants: {
$size: 'md'
}
})

export type SizeVariants = VariantProps<typeof sizeVariants>

interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
ButtonVariants,
SizeVariants {
asChild?: boolean
$icon?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(
// 從 prop 中傳入
{
className,
$variant,
$radius,
$size,
asChild = false,
$icon = false,
...props
},
ref
) => {
const Comp = asChild ? Slot : 'button'
return (
<Comp
className={cn(
buttonVariants({ $radius, $variant }),
$icon ? iconSizeVariants({ $size }) : sizeVariants({ $size }),
className
)}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = 'Button'

export Button

可以如下面方式使用

// 可以透過className加上自定義的樣式
<Button $variant="outline" $size="xs" $radius="md" className="flex-shrink-0">
<span>hello</span>
</Button>
// 建立 icon button
// radius 沒有帶入,使用預設值 md
<Button $variant="secondary" $size="md" $icon={true}>
<ShareIcon />
</Button>