import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; /** * shadcn "new-york" button, retuned to PIG's tokens (spec §9). * * Note `accent` in tailwind.config.js is the SUBTLE hover surface, not the * brand — that mapping is deliberate and documented there. So the solid CTA * uses `bg-brand text-accent-on`, never `bg-accent`. * * Every variant carries a 1px border (transparent where it has no colour) so * the variants line up to the pixel when they sit in one row; a borderless * ghost next to a bordered outline is otherwise 2px shorter. * * The solid CTA's hover differs by theme on purpose. In dark the fill has a * darker sibling (`brand/90`). In light the neon green has none — darkening it * reads brown — so the hover flips to the subtle ground with the text green, * and the accent-edge border is what keeps it reading as the same control. */ const buttonVariants = cva( 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-transparent font-sans text-sm font-medium transition-colors duration-1 ease-enter focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-bg disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0', { variants: { variant: { default: 'border-accent-edge bg-brand text-accent-on hover:bg-accent-subtle hover:text-accent-fg dark:hover:bg-brand/90 dark:hover:text-accent-on', secondary: 'bg-surface-2 text-fg hover:bg-surface-3', outline: 'border-border-strong bg-surface text-fg hover:bg-surface-2', ghost: 'text-fg hover:bg-surface-2', subtle: 'bg-accent-subtle text-accent-fg hover:border-accent-edge', destructive: 'bg-danger text-danger-on hover:bg-danger/90', link: 'text-accent-fg underline-offset-4 hover:underline', }, size: { // `h-9` is 36px, which is fine for a mouse and too small for a thumb. // Anything a phone visitor taps gets `size="touch"` or the `.tap` // helper on top — see SiteHeader. sm: 'h-8 px-3 text-[0.8125rem] [&_svg]:size-3.5', default: 'h-9 px-4 py-2 [&_svg]:size-4', lg: 'h-11 px-6 [&_svg]:size-4', touch: 'min-h-11 px-4 py-2 [&_svg]:size-4', icon: 'size-9 [&_svg]:size-4', 'icon-touch': 'size-11 [&_svg]:size-[1.125rem]', }, }, defaultVariants: { variant: 'default', size: 'default' }, }, ); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; } function Button({ className, variant, size, asChild = false, ...props }: ButtonProps) { const Comp = asChild ? Slot : 'button'; return ; } export { Button, buttonVariants };