feat: update UI components (badge, button, card, input, textarea)

This commit is contained in:
zhenyi 2026-05-30 15:07:46 +08:00
parent 9b1f764ab8
commit f658c5ae96
5 changed files with 27 additions and 29 deletions

View File

@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const badgeVariants = cva( const badgeVariants = cva(
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border border-transparent px-2 py-0.5 text-[11px] font-medium whitespace-nowrap transition-all focus-visible:ring-2 focus-visible:ring-ring/30 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/15 [&>svg]:pointer-events-none [&>svg]:size-3!",
{ {
variants: { variants: {
variant: { variant: {
@ -13,11 +13,11 @@ const badgeVariants = cva(
secondary: secondary:
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
destructive: destructive:
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 [a]:hover:bg-destructive/20",
outline: outline:
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
ghost: ghost:
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", "hover:bg-muted hover:text-muted-foreground",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
}, },

View File

@ -4,33 +4,31 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group/button inline-flex shrink-0 items-center justify-center rounded-xl border border-transparent bg-clip-padding text-[13px] font-semibold whitespace-nowrap transition-all outline-none select-none focus-visible:ring-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:scale-[0.97] disabled:pointer-events-none disabled:opacity-40 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 shadow-sm",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", default:
"bg-primary text-primary-foreground hover:bg-primary/90",
outline: outline:
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", "border-border bg-background text-foreground hover:bg-muted hover:border-border/60 aria-expanded:bg-muted shadow-sm",
secondary: secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", "bg-secondary text-secondary-foreground hover:bg-secondary/70 aria-expanded:bg-secondary shadow-sm",
ghost: ghost:
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", "hover:bg-muted hover:text-foreground aria-expanded:bg-muted shadow-none",
destructive: destructive:
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", "bg-destructive/10 text-destructive hover:bg-destructive/15 focus-visible:ring-destructive/20 shadow-sm",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline shadow-none",
}, },
size: { size: {
default: default: "h-9 gap-2 px-4",
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", xs: "h-7 gap-1.5 rounded-lg px-2.5 text-xs [&_svg:not([class*='size-'])]:size-3",
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", sm: "h-8 gap-1.5 rounded-lg px-3 text-[12px] [&_svg:not([class*='size-'])]:size-3.5",
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", lg: "h-10 gap-2 px-5",
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", icon: "size-9",
icon: "size-8", "icon-xs": "size-7 rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-xs": "icon-sm": "size-8 rounded-lg [&_svg:not([class*='size-'])]:size-3.5",
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", "icon-lg": "size-10",
"icon-sm":
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
"icon-lg": "size-9",
}, },
}, },
defaultVariants: { defaultVariants: {

View File

@ -12,7 +12,7 @@ function Card({
data-slot="card" data-slot="card"
data-size={size} data-size={size}
className={cn( className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", "group/card flex flex-col gap-4 overflow-hidden rounded-2xl border border-border bg-card py-5 text-[13px] text-card-foreground shadow-sm has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-4 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-2xl *:[img:last-child]:rounded-b-2xl",
className className
)} )}
{...props} {...props}
@ -25,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-header" data-slot="card-header"
className={cn( className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", "group/card-header @container/card-header grid auto-rows-min items-start gap-1.5 rounded-t-2xl px-5 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
className className
)} )}
{...props} {...props}
@ -38,7 +38,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-title" data-slot="card-title"
className={cn( className={cn(
"font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", "font-heading text-sm leading-snug font-bold group-data-[size=sm]/card:text-[13px]",
className className
)} )}
{...props} {...props}
@ -50,7 +50,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="card-description" data-slot="card-description"
className={cn("text-sm text-muted-foreground", className)} className={cn("text-[13px] text-muted-foreground", className)}
{...props} {...props}
/> />
) )
@ -73,7 +73,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="card-content" data-slot="card-content"
className={cn("px-4 group-data-[size=sm]/card:px-3", className)} className={cn("px-5 group-data-[size=sm]/card:px-4", className)}
{...props} {...props}
/> />
) )
@ -84,7 +84,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-footer" data-slot="card-footer"
className={cn( className={cn(
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3", "flex items-center rounded-b-2xl border-t bg-muted/40 px-5 py-4 group-data-[size=sm]/card:px-4 group-data-[size=sm]/card:py-3",
className className
)} )}
{...props} {...props}

View File

@ -9,7 +9,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type} type={type}
data-slot="input" data-slot="input"
className={cn( className={cn(
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", "h-9 w-full min-w-0 rounded-xl border border-input bg-transparent px-4 py-1.5 text-[13px] transition-colors outline-none shadow-sm file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-[13px] file:font-medium file:text-foreground placeholder:text-muted-foreground/50 hover:border-border/70 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/20 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/15",
className className
)} )}
{...props} {...props}

View File

@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
<textarea <textarea
data-slot="textarea" data-slot="textarea"
className={cn( className={cn(
"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", "flex field-sizing-content min-h-20 w-full rounded-xl border border-input bg-transparent px-4 py-3 text-[13px] transition-colors outline-none shadow-sm placeholder:text-muted-foreground/50 hover:border-border/70 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/20 disabled:cursor-not-allowed disabled:opacity-40 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/15",
className className
)} )}
{...props} {...props}