From 1e4704ed3f9d2f256139ac8c800941a9b0dc9740 Mon Sep 17 00:00:00 2001 From: Sean O'Connor Date: Mon, 11 May 2026 01:34:15 -0400 Subject: [PATCH] Scale up radix-mira component sizes for a portfolio context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new shadcn 'radix-mira' style targets dense dashboards. For a personal academic site the ultra-compact defaults (10px badge, 12px button/card, 14px card title) read as broken. Updated to readable sizes: - card.tsx: text-xs/relaxed → text-sm base; CardTitle text-sm font-medium → text-base font-semibold leading-tight; CardDescription text-xs → text-sm - badge.tsx: text-[0.625rem] h-5 px-2 → text-xs h-6 px-2.5 - button.tsx: text-xs h-7 px-2 → text-sm h-9 px-4 (default size) - tabs.tsx: TabsTrigger text-xs px-1.5 → text-sm px-3; TabsContent text-xs/relaxed removed; TabsList h-8 → h-10 Co-Authored-By: Claude Sonnet 4.6 --- src/components/ui/badge.tsx | 50 +++++++----- src/components/ui/button.tsx | 83 ++++++++++--------- src/components/ui/card.tsx | 153 +++++++++++++++++++---------------- src/components/ui/tabs.tsx | 131 +++++++++++++++++++----------- 4 files changed, 244 insertions(+), 173 deletions(-) diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index c64f5a4..4d676fb 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -1,37 +1,49 @@ -import * as React from "react"; -import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" -import { cn } from "~/lib/utils"; +import { cn } from "~/lib/utils" const badgeVariants = cva( - "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + "group/badge inline-flex h-6 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2.5 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!", { variants: { variant: { - default: - "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", secondary: - "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", destructive: - "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", - outline: "text-foreground", + "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", + outline: + "border-border bg-input/20 text-foreground dark:bg-input/30 [a]:hover:bg-muted [a]:hover:text-muted-foreground", + ghost: + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", + link: "text-primary underline-offset-4 hover:underline", }, }, defaultVariants: { variant: "default", }, - }, -); + } +) -export interface BadgeProps - extends - React.HTMLAttributes, - VariantProps {} +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span" -function Badge({ className, variant, ...props }: BadgeProps) { return ( -
- ); + + ) } -export { Badge, badgeVariants }; +export { Badge, badgeVariants } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index ed25872..051dfb4 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,58 +1,65 @@ -import * as React from "react"; -import { Slot } from "@radix-ui/react-slot"; -import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from "radix-ui" -import { cn } from "~/lib/utils"; +import { cn } from "~/lib/utils" const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + "group/button inline-flex shrink-0 items-center justify-center rounded-md 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-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 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", { variants: { variant: { - default: - "bg-primary text-primary-foreground shadow hover:bg-primary/90", - destructive: - "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + default: "bg-primary text-primary-foreground hover:bg-primary/80", outline: - "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + "border-border hover:bg-input/50 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:bg-input/30", secondary: - "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground", + "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", + ghost: + "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", + 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", link: "text-primary underline-offset-4 hover:underline", }, size: { - default: "h-9 px-4 py-2", - sm: "h-8 px-3 text-xs", - lg: "h-10 px-8", - icon: "h-9 w-9 rounded-full", + default: + "h-9 gap-2 px-4 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3 [&_svg:not([class*='size-'])]:size-4", + xs: "h-6 gap-1 rounded-sm px-2 text-xs 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 px-3 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-10 gap-2 px-6 text-base has-data-[icon=inline-end]:pr-4 has-data-[icon=inline-start]:pl-4 [&_svg:not([class*='size-'])]:size-5", + icon: "size-9 [&_svg:not([class*='size-'])]:size-4", + "icon-xs": "size-6 rounded-sm [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-8 [&_svg:not([class*='size-'])]:size-3.5", + "icon-lg": "size-10 [&_svg:not([class*='size-'])]:size-5", }, }, defaultVariants: { variant: "default", size: "default", }, - }, -); + } +) -export interface ButtonProps - extends - React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean; +function Button({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot.Root : "button" + + return ( + + ) } -const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : "button"; - return ( - - ); - }, -); -Button.displayName = "Button"; - -export { Button, buttonVariants }; +export { Button, buttonVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 50f5a48..5590ef3 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -1,83 +1,100 @@ -import * as React from "react"; +import * as React from "react" -import { cn } from "~/lib/utils"; +import { cn } from "~/lib/utils" -const Card = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -Card.displayName = "Card"; +function Card({ + className, + size = "default", + ...props +}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) { + return ( +
img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg", + className + )} + {...props} + /> + ) +} -const CardHeader = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardHeader.displayName = "CardHeader"; +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const CardTitle = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardTitle.displayName = "CardTitle"; +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const CardDescription = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardDescription.displayName = "CardDescription"; +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const CardContent = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardContent.displayName = "CardContent"; +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const CardFooter = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -CardFooter.displayName = "CardFooter"; +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} export { Card, CardHeader, CardFooter, CardTitle, + CardAction, CardDescription, CardContent, -}; +} diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index fc2abae..821a702 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -1,55 +1,90 @@ -"use client"; +"use client" -import * as React from "react"; -import * as TabsPrimitive from "@radix-ui/react-tabs"; +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { Tabs as TabsPrimitive } from "radix-ui" -import { cn } from "~/lib/utils"; +import { cn } from "~/lib/utils" -const Tabs = TabsPrimitive.Root; +function Tabs({ + className, + orientation = "horizontal", + ...props +}: React.ComponentProps) { + return ( + + ) +} -const TabsList = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -TabsList.displayName = TabsPrimitive.List.displayName; +const tabsListVariants = cva( + "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-10 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none", + { + variants: { + variant: { + default: "bg-muted", + line: "gap-1 bg-transparent", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) -const TabsTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; +function TabsList({ + className, + variant = "default", + ...props +}: React.ComponentProps & + VariantProps) { + return ( + + ) +} -const TabsContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -TabsContent.displayName = TabsPrimitive.Content.displayName; +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} -export { Tabs, TabsList, TabsTrigger, TabsContent }; +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }