refactor(button & combobox): enhance styling and accessibility

- Updated button variants for improved hover effects and accessibility, including aria-loading support.
- Refined combobox trigger styles for better visibility and consistency, including adjustments to error state styling and input placeholder appearance.
- Improved overall class management and organization for both components.
This commit is contained in:
MyPrototypeWhat 2025-11-07 19:29:06 +08:00
parent f2c2a27622
commit 846a7f5ecf
2 changed files with 18 additions and 11 deletions

View File

@ -5,18 +5,23 @@ import { Loader } from 'lucide-react'
import * as React from 'react'
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
cn(
'inline-flex items-center justify-center gap-2 whitespace-nowrap',
'rounded-md text-sm font-medium transition-all',
'disabled:pointer-events-none disabled:opacity-40',
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
'aria-loading:cursor-progress aria-loading:opacity-40',
'hover:shadow-xs'
),
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline:
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input dark:border-input dark:hover:bg-input/50',
default: 'bg-primary hover:bg-primary-hover text-white',
destructive: 'bg-destructive text-white hover:bg-destructive-hover focus-visible:ring-destructive/20',
outline: cn('border border-primary/40 bg-primary/10 text-primary', 'hover:bg-primary/5'),
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: 'text-primary underline-offset-4 hover:underline'
ghost: 'hover:text-primary-hover text-primary',
link: 'text-primary underline-offset-4 hover:underline hover:text-primary-hover'
},
size: {
default: 'min-h-9 px-4 py-2 has-[>svg]:px-3',
@ -72,6 +77,7 @@ function Button({
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
disabled={disabled || loading}
aria-loading={loading}
{...props}>
{/* asChild mode does not support loading because Slot requires a single child element */}
{asChild ? (

View File

@ -23,8 +23,7 @@ const comboboxTriggerVariants = cva(
variants: {
state: {
default: 'border-border aria-expanded:border-primary aria-expanded:ring-3 aria-expanded:ring-primary/20',
error:
'border-destructive ring-2 ring-destructive/20 aria-expanded:border-destructive aria-expanded:ring-destructive/20',
error: 'border border-destructive! aria-expanded:ring-3 aria-expanded:ring-red-600/20',
disabled: 'opacity-50 cursor-not-allowed pointer-events-none'
},
size: {
@ -262,7 +261,9 @@ export function Combobox({
</PopoverTrigger>
<PopoverContent className={cn('p-0 rounded-2xs', popoverClassName)} style={{ width: triggerWidth }}>
<Command>
{searchable && <CommandInput placeholder={searchPlaceholder} className="h-9" onValueChange={onSearch} />}
{searchable && (
<CommandInput placeholder={searchPlaceholder} className="h-9 rounded-none" onValueChange={onSearch} />
)}
<CommandList>
<CommandEmpty>{emptyText}</CommandEmpty>
<CommandGroup>