SPB Git

Repository Public

button.tsx 3.1 KB 76 lines tsx

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
Files main
Menu

Theme