Files
www_projectmycelium_com/src/components/Button.tsx
sasha-astiadi 96c445aa41 refactor: update outline gray button border color from gray-300 to gray-200
- Changed border-gray-300 to border-gray-200 in outline gray variant for consistency with other border color updates across the application
2025-11-19 17:29:30 +01:00

98 lines
3.2 KiB
TypeScript

import { Link } from 'react-router-dom'
import clsx from 'clsx'
const baseStyles = {
solid:
'inline-flex justify-center rounded-full py-2 px-5 text-sm md:text-base font-semibold transition-colors',
outline:
'inline-flex justify-center bg-transparent font-semibold rounded-full border border-2 py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-sm md:text-base transition-colors',
link:
'inline-flex items-center text-sm md:text-base font-semibold transition-colors',
}
const variantStyles = {
solid: {
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors',
white:
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
green: 'bg-green-500 text-white hover:bg-green-600',
},
outline: {
cyan: 'border-cyan-500 text-cyan-500',
gray: 'border-gray-200 text-gray-600 hover:text-cyan-500 hover:border-cyan-500 active:border-cyan-500',
white: 'border-gray-300 text-white hover:text-cyan-500 hover:border-cyan-500 active:border-cyan-500',
},
link: {
cyan: 'text-cyan-400 underline hover:text-cyan-300',
white: 'text-white underline hover:text-cyan-300',
dark: 'text-gray-900 underline hover:text-cyan-500',
},
}
type ButtonProps = (
| {
variant?: 'solid'
color?: keyof typeof variantStyles.solid
}
| {
variant: 'outline'
color?: keyof typeof variantStyles.outline
}
| {
variant: 'link'
color?: keyof typeof variantStyles.link
}
) &
(
| (Omit<React.ComponentPropsWithoutRef<typeof Link>, 'color'> & { to: string; as?: 'link' })
| (Omit<React.ComponentPropsWithoutRef<'a'>, 'color'> & { to: string; as: 'a' })
| (Omit<React.ComponentPropsWithoutRef<'button'>, 'color'> & {
to?: undefined
as?: undefined
})
)
export function Button({ className, as, ...props }: ButtonProps) {
// Set safe defaults per variant so color always matches a valid palette key
if (!props.variant) {
props.variant = 'solid'
}
if (!props.color) {
if (props.variant === 'solid') {
props.color = 'gray'
} else if (props.variant === 'outline') {
props.color = 'gray'
} else if (props.variant === 'link') {
props.color = 'cyan'
}
}
const variant = props.variant
const color = props.color as string
className = clsx(
baseStyles[variant],
variant === 'outline'
? variantStyles.outline[color as keyof typeof variantStyles.outline]
: variant === 'solid'
? variantStyles.solid[color as keyof typeof variantStyles.solid]
: variant === 'link'
? variantStyles.link[color as keyof typeof variantStyles.link]
: undefined,
className,
)
if (typeof props.to === 'undefined') {
return <button className={className} {...props} />
}
if (as === 'a') {
const { to, variant, color, ...rest } = props as any
return <a className={className} href={to} {...rest} />
}
return <Link className={className} {...props} />
}