/ src / components / Button.tsx
Button.tsx
  1  /**
  2   * ACDC Button Component
  3   * Chain-aware button with all variants
  4   */
  5  
  6  import React from 'react'
  7  import type { Chain } from '../tokens'
  8  
  9  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
 10    variant?: 'primary' | 'secondary' | 'ghost' | 'destructive' | 'success'
 11    size?: 'sm' | 'md' | 'lg'
 12    chain?: Chain
 13    loading?: boolean
 14    children: React.ReactNode
 15  }
 16  
 17  export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
 18    (
 19      {
 20        variant = 'primary',
 21        size = 'md',
 22        chain,
 23        loading = false,
 24        disabled,
 25        className = '',
 26        children,
 27        ...props
 28      },
 29      ref
 30    ) => {
 31      // Wise-inspired: pill shapes, generous padding, smooth transitions
 32      const baseStyles = `
 33        inline-flex items-center justify-center
 34        font-semibold tracking-tight
 35        transition-all duration-300 ease-out
 36        focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
 37        disabled:opacity-50 disabled:pointer-events-none
 38      `
 39  
 40      const sizeStyles = {
 41        sm: 'h-9 px-4 text-sm rounded-full',
 42        md: 'h-11 px-6 text-base rounded-full',
 43        lg: 'h-14 px-8 text-lg rounded-full',
 44      }
 45  
 46      // Wise-inspired: clean colors, subtle hover lift, soft shadows
 47      const variantStyles = {
 48        primary: `
 49          bg-[var(--btn-primary-bg)] text-white
 50          shadow-sm
 51          hover:bg-[var(--btn-primary-bg-hover)]
 52          hover:shadow-md hover:-translate-y-0.5
 53          active:bg-[var(--btn-primary-bg-active)]
 54          active:translate-y-0 active:shadow-sm
 55        `,
 56        secondary: `
 57          bg-white/10 backdrop-blur-sm
 58          border-2 border-[var(--border-default)]
 59          text-[var(--text-primary)]
 60          hover:border-[var(--btn-secondary-border-hover)]
 61          hover:bg-white/20
 62          hover:-translate-y-0.5
 63          active:translate-y-0
 64        `,
 65        ghost: `
 66          bg-transparent text-[var(--text-secondary)]
 67          hover:bg-[var(--bg-tertiary)]
 68          hover:text-[var(--text-primary)]
 69        `,
 70        destructive: `
 71          bg-[var(--error)] text-white
 72          shadow-sm
 73          hover:bg-[var(--error-hover)]
 74          hover:shadow-md hover:-translate-y-0.5
 75          active:bg-[var(--error-active)]
 76          active:translate-y-0 active:shadow-sm
 77        `,
 78        success: `
 79          bg-[var(--success)] text-white
 80          shadow-sm
 81          hover:bg-[var(--success-hover)]
 82          hover:shadow-md hover:-translate-y-0.5
 83          active:bg-[var(--success-active)]
 84          active:translate-y-0 active:shadow-sm
 85        `,
 86      }
 87  
 88      return (
 89        <button
 90          ref={ref}
 91          data-chain={chain}
 92          disabled={disabled || loading}
 93          className={`${baseStyles} ${sizeStyles[size]} ${variantStyles[variant]} ${className}`}
 94          {...props}
 95        >
 96          {loading ? (
 97            <>
 98              <svg
 99                className="animate-spin -ml-1 mr-2 h-4 w-4"
100                fill="none"
101                viewBox="0 0 24 24"
102              >
103                <circle
104                  className="opacity-25"
105                  cx="12"
106                  cy="12"
107                  r="10"
108                  stroke="currentColor"
109                  strokeWidth="4"
110                />
111                <path
112                  className="opacity-75"
113                  fill="currentColor"
114                  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
115                />
116              </svg>
117              Loading...
118            </>
119          ) : (
120            children
121          )}
122        </button>
123      )
124    }
125  )
126  
127  Button.displayName = 'Button'