Button
Examples
Variant Examples
Showcase different button variants (solid, ghost, light, bordered):
Color Examples
Display buttons in different colors (blue, red, green, dark, light):
Size Examples
Illustrate buttons of various sizes (small, medium, large):
Code
import React, { forwardRef, ReactNode } from 'react'
import { cva, VariantProps, cx } from 'class-variance-authority'
const ANIMATION_CLASS = 'transition-all duration-300 ease-in-out'
const buttonVariants = cva(`inline-grid group grid-flow-col items-center justify-center cursor-pointer !outline-none !ring-0 ${ANIMATION_CLASS} select-none`, {
variants: {
size: {
small: 'px-3 py-1.5 text-sm',
medium: 'px-5 py-2 text-base',
large: 'px-8 py-2 text-lg'
},
radius: {
full: 'rounded-full',
large: 'rounded-lg',
medium: 'rounded-md',
small: 'rounded-sm',
none: 'rounded-none'
},
disabled: {
true: 'opacity-50 cursor-not-allowed',
false: ''
},
color: {
blue: '',
red: '',
green: '',
dark: '',
light: ''
},
variant: {
solid: '',
ghost: 'bg-transparent border',
light: 'bg-transparent',
bordered: 'border'
}
},
compoundVariants: [
{ variant: 'solid', color: 'blue', className: 'bg-blue-600 hover:bg-blue-600/90' },
{ variant: 'solid', color: 'red', className: 'bg-red-500 hover:bg-red-600/90' },
{ variant: 'solid', color: 'green', className: 'bg-green-500 hover:bg-green-600/90' },
{ variant: 'solid', color: 'dark', className: 'bg-zinc-800 hover:bg-zinc-700/50' },
{ variant: 'solid', color: 'light', className: 'text-black bg-zinc-100 hover:bg-zinc-200/90' },
{ variant: 'ghost', color: 'light', className: 'text-white hover:text-black hover:bg-zinc-200 border-zinc-100 dark:border-zinc-700' },
{ variant: 'ghost', color: 'blue', className: 'text-blue hover:text-white hover:bg-blue-600 border-zinc-100 dark:border-zinc-700' },
{ variant: 'ghost', color: 'green', className: 'text-green hover:text-white hover:bg-green-500 hover:border-green-500 border-zinc-100 dark:border-zinc-700' },
{ variant: 'light', color: 'blue', className: 'text-blue-600 hover:bg-blue-600/20' },
{ variant: 'light', color: 'red', className: 'text-red-500 hover:bg-red-500/20' },
{ variant: 'light', color: 'green', className: 'text-green-500 hover:bg-green-500/20' },
{ variant: 'bordered', color: 'blue', className: 'text-blue-600 border-blue-600/30 hover:border-blue-600' },
{ variant: 'bordered', color: 'red', className: 'text-red-500 border-red-500/30 hover:border-red-600' },
{ variant: 'bordered', color: 'green', className: 'text-green-500 border-green-500/30 hover:border-green-600' }
],
defaultVariants: {
size: 'medium',
radius: 'medium',
disabled: false,
color: 'dark',
variant: 'solid'
}
})
const contentAnimation = cva('inline-grid', {
variants: {
animation: {
true: `${ANIMATION_CLASS} grid-cols-[0fr] group-hover:grid-cols-[1fr]`,
false: ''
}
},
defaultVariants: {
animation: false
}
})
const innerContentAnimation = cva('min-w-0', {
variants: {
animation: {
true: `invisible opacity-0 ${ANIMATION_CLASS} group-hover:visible group-hover:opacity-100`,
false: ''
},
position: {
left: '',
right: ''
}
},
compoundVariants: [
{ animation: true, position: 'left', class: 'origin-left translate-x-4 group-hover:translate-x-0 group-hover:mr-1.5' },
{ animation: true, position: 'right', class: 'origin-right -translate-x-4 group-hover:translate-x-0 group-hover:ml-1.5' },
{ animation: false, position: 'left', class: 'mr-1.5' },
{ animation: false, position: 'right', class: 'ml-1.5' }
],
defaultVariants: {
animation: false,
position: 'left' // Default position
}
})
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
asChild?: React.ElementType
children: ReactNode
endContent?: ReactNode
startContent?: ReactNode
animateEndContent?: boolean
animateStartContent?: boolean
} & VariantProps<typeof buttonVariants>
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{ asChild: AsChild, children, className, color, radius, variant, disabled, size, animateStartContent = false, animateEndContent = false, endContent, startContent, ...props },
ref
) => {
const mergedClassName = cx(buttonVariants({ color, radius, variant, disabled, size }), className)
const Component = AsChild ?? 'button'
return (
<Component ref={ref} className={mergedClassName} {...props}>
{startContent && (
<span className={contentAnimation({ animation: animateStartContent }) + ' origin-left'}>
<span className={innerContentAnimation({ animation: animateStartContent, position: 'left' })}>{startContent}</span>
</span>
)}
{children}
{endContent && (
<span className={contentAnimation({ animation: animateEndContent }) + ' origin-right'}>
<span className={innerContentAnimation({ animation: animateEndContent, position: 'right' })}>{endContent}</span>
</span>
)}
</Component>
)
}
)
Button.displayName = 'Button'
export default Button
Usage
<Button color="blue" radius="medium" variant="solid">
Click Me
</Button>Props
Detail the props available for the Button component:
color: Defines the button's color. Options:blue,red,green,dark,light.radius: Sets the border-radius. Options:full,large,medium,small,none.variant: Specifies the style. Options:solid,ghost,light,bordered.disabled: Enables or disables the button. Options:true,false.size: Determines the button's size. Options:small,medium,large.
This structure focuses on clear, concise information and practical examples, making it easy for developers to understand and utilize the Button component in their projects.