Repository Public
1'use client';2import * as React from 'react';3import { cva, type VariantProps } from 'class-variance-authority';4import { cn } from '@/lib/cn';5import { Tooltip } from './tooltip';67const button = cva(8 'pressable inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap rounded-sm font-medium transition-[background-color,border-color,filter,transform] duration-150 select-none disabled:opacity-45 disabled:pointer-events-none pointer-coarse:rounded-full',9 {10 variants: {11 variant: {12 primary: 'bg-accent text-accent-fg shadow-card hover:brightness-110 active:brightness-95',13 brand: 'bg-brand shadow-float hover:brightness-110 active:brightness-95',14 secondary: 'bg-surface text-text border border-line-2 shadow-card hover:border-line-3',15 ghost: 'text-text-2 hover:bg-surface-2 hover:text-text',16 danger: 'bg-error-vivid/12 text-error border border-error-vivid/25 hover:bg-error-vivid/20',17 outline: 'border border-line-2 text-text-2 hover:text-text hover:border-line-3',18 },19 size: {20 sm: 'h-7 px-2.5 text-xs pointer-coarse:h-11 pointer-coarse:px-3.5 pointer-coarse:text-sm',21 md: 'h-8 px-3 text-[13px] pointer-coarse:h-11 pointer-coarse:px-4 pointer-coarse:text-sm',22 lg: 'h-12 px-5 text-[15px] font-semibold',23 },24 },25 defaultVariants: { variant: 'secondary', size: 'md' },26 },27);2829export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof button> {30 loading?: boolean;31}32export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(33 { className, variant, size, loading, disabled, children, type = 'button', ...props },34 ref,35) {36 return (37 <button ref={ref} type={type} className={cn(button({ variant, size }), className)} disabled={disabled || loading} aria-busy={loading || undefined} {...props}>38 {loading && <span className="size-3 animate-spin rounded-full border-[1.5px] border-current border-t-transparent" aria-hidden />}39 {children}40 </button>41 );42});4344export interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {45 /** Accessible name; also shown as a tooltip. */46 label: string;47 active?: boolean;48 size?: 'sm' | 'md';49 tooltipSide?: 'top' | 'right' | 'bottom' | 'left';50 noTooltip?: boolean;51}52export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(53 { label, active, size = 'md', className, children, tooltipSide, noTooltip, type = 'button', ...props },54 ref,55) {56 const btn = (57 <button58 ref={ref}59 type={type}60 aria-label={label}61 aria-pressed={active}62 className={cn(63 'inline-flex shrink-0 items-center justify-center rounded-sm text-text-2 transition-colors duration-150 hover:bg-surface-2 hover:text-text disabled:pointer-events-none disabled:opacity-40',64 size === 'sm' ? 'size-6' : 'size-8',65 'pointer-coarse:size-11',66 active && 'bg-surface-2 text-text',67 className,68 )}69 {...props}70 >71 {children}72 </button>73 );74 return noTooltip ? btn : <Tooltip content={label} side={tooltipSide}>{btn}</Tooltip>;75});76