본문으로 건너뛰기

Button

Prerequisite

npm install classnames

Code

components/Button/index.tsx
import classnames from 'classnames'
import { Spinner } from 'components'
import type { FC, DetailedHTMLProps, ButtonHTMLAttributes } from 'react'

export interface Props
extends DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
> {
loading?: boolean
size?: 'xs' | 'sm' | 'md' | 'lg'
theme?: 'danger' | 'primary' | 'success'
shape?: 'text' | 'contained' | 'outlined'
}

const Button: FC<Props> = ({
loading = false,
size = 'md',
theme,
children,
disabled,
className,
shape = 'contained',
...props
}) => {
return (
<button
{...props}
className={classnames(
'leading-6 font-medium transition ease-in-out duration-150 disabled:cursor-not-allowed border',
size === 'xs' ? 'text-xs py-px px-2 rounded gap-1.5' : 'rounded-md',
shape === 'outlined' ? 'group' : 'border-transparent',
{
'inline-flex items-center justify-center': loading,
'hover:brightness-105 active:brightness-90': !loading && !disabled,
'text-sm py-1 px-3 gap-2': size === 'sm',
'text-base py-2 px-4 gap-3': size === 'md',
'text-lg py-3 px-5 gap-3': size === 'lg',
'bg-gray-900 text-white': !theme && shape === 'contained',
'bg-red-600 text-white': theme === 'danger' && shape === 'contained',
'bg-blue-500 text-white':
theme === 'primary' && shape === 'contained',
'bg-emerald-500 text-white':
theme === 'success' && shape === 'contained',
'disabled:bg-gray-300 disabled:text-white':
shape === 'contained' && (loading || disabled),
'bg-transparent disabled:hover:bg-gray-200 disabled:hover:text-white':
shape === 'text',
'hover:bg-gray-200': !theme && shape === 'text',
'text-red-600 hover:bg-red-100':
theme === 'danger' && shape === 'text',
'text-blue-500 hover:bg-blue-100':
theme === 'primary' && shape === 'text',
'text-emerald-500 hover:bg-emerald-100':
theme === 'success' && shape === 'text',
'hover:text-white': shape === 'outlined',
'border-gray-500 text-gray-500 hover:bg-gray-900':
!theme && shape === 'outlined',
'border-blue-500 text-blue-500 hover:bg-blue-500':
theme === 'primary' && shape === 'outlined',
'border-red-500 text-red-500 hover:bg-red-500':
theme === 'danger' && shape === 'outlined',
'border-emerald-500 text-emerald-500 hover:bg-emerald-500':
theme === 'success' && shape === 'outlined'
},
className
)}
disabled={loading || disabled}
>
{loading && (
<Spinner
className={classnames({
'h-3 w-3': size === 'xs',
'h-4 w-4': size === 'sm',
'h-5 w-5': size === 'md',
'h-6 w-6': size === 'lg',
'text-white': shape === 'contained',
'group-hover:text-white': shape === 'outlined',
'text-gray-900':
(shape !== 'contained' && !theme) || shape === 'text',
'text-blue-500': shape !== 'contained' && theme === 'primary',
'text-red-500': shape !== 'contained' && theme === 'danger',
'text-emerald-500': shape !== 'contained' && theme === 'success'
})}
/>
)}
{children}
</button>
)
}

export default Button

Props

NameTypeDefault
sizexs sm md lgmd
isLoadingbooleanfalse
themeprimary success dangerundefined
shapetext contained outlinedcontained

Example