UI Phase B — shadcn을 SSoT로, 컴포넌트 디자인 정리
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m2s
Build & Deploy to K8s / build-and-deploy (push) Successful in 4m2s
- Badge: variant를 semantic 토큰화 (bg-green-500 하드코딩 제거 → bg-success 등) - border/hover 제거 (클릭 불가 요소에 hover 불필요) - focus-visible ring (focus → focus-visible) - info variant 추가 + solid (filled primary) 추가 - whitespace-nowrap, gap-1 (아이콘 간격 표준) - Button: success/warning variant 제거 (사용처 0건, 상태는 Badge로) - Dialog: size prop 추가 (sm:420 / md:560[default] / lg:720 / xl:960) - 기존 max-w-lg(512) → md(560). className의 max-w-* 가 오면 우선. - 편집 모달의 좁은 폭 문제 해결 - Input: h-10 → h-9 (Button default 높이와 정합) - v5-atomics.css: DEPRECATION NOTICE 주석 추가. 신규 코드는 shadcn <Button>, <Badge> 등 사용. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,18 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full 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",
|
||||
"inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-semibold whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
success: "border-transparent bg-green-500 text-white hover:bg-green-600",
|
||||
warning: "border-transparent bg-yellow-500 text-white hover:bg-yellow-600",
|
||||
default: "bg-primary/12 text-primary",
|
||||
solid: "bg-primary text-primary-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground",
|
||||
destructive: "bg-destructive/12 text-destructive",
|
||||
outline: "border border-border text-foreground",
|
||||
success: "bg-success/12 text-success",
|
||||
warning: "bg-warning/12 text-warning",
|
||||
info: "bg-info/12 text-info",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
@@ -17,10 +17,7 @@ const buttonVariants = cva(
|
||||
secondary: "bg-secondary text-secondary-foreground shadow-xs 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",
|
||||
success:
|
||||
"bg-success text-success-foreground shadow-xs hover:bg-success/90 focus-visible:ring-success/20 dark:focus-visible:ring-success/40",
|
||||
warning:
|
||||
"bg-warning text-warning-foreground shadow-xs hover:bg-warning/90 focus-visible:ring-warning/20 dark:focus-visible:ring-warning/40",
|
||||
// success/warning buttons removed — 액션 의미가 아닌 상태는 <Badge variant="success|warning"/> 사용
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
|
||||
@@ -73,12 +73,21 @@ interface ScopedDialogContentProps
|
||||
container?: HTMLElement | null;
|
||||
/** 탭 비활성 시 포탈 내용 숨김 */
|
||||
hidden?: boolean;
|
||||
/** 모달 크기 — sm: 420, md: 560(default), lg: 720, xl: 960. className의 max-w-* 가 지정되면 그 값이 우선. */
|
||||
size?: "sm" | "md" | "lg" | "xl";
|
||||
}
|
||||
|
||||
const dialogSizeClass = {
|
||||
sm: "max-w-[420px]",
|
||||
md: "max-w-[560px]",
|
||||
lg: "max-w-[720px]",
|
||||
xl: "max-w-[960px]",
|
||||
} as const;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
ScopedDialogContentProps
|
||||
>(({ className, children, container: explicitContainer, hidden: hiddenProp, onInteractOutside, onFocusOutside, style, ...props }, ref) => {
|
||||
>(({ className, children, container: explicitContainer, hidden: hiddenProp, size = "md", onInteractOutside, onFocusOutside, style, ...props }, ref) => {
|
||||
const autoContainer = useModalPortal();
|
||||
const container = explicitContainer !== undefined ? explicitContainer : autoContainer;
|
||||
const scoped = !!container;
|
||||
@@ -143,8 +152,9 @@ const DialogContent = React.forwardRef<
|
||||
onFocusOutside={handleFocusOutside}
|
||||
className={cn(
|
||||
scoped
|
||||
? "bg-background relative z-1 flex w-full max-w-lg max-h-full flex-col gap-4 border p-6 shadow-lg sm:rounded-lg"
|
||||
: "bg-background fixed top-[50%] left-[50%] z-1000 flex w-full max-w-lg translate-x-[-50%] translate-y-[-50%] flex-col gap-4 border p-6 shadow-lg sm:rounded-lg",
|
||||
? "bg-background relative z-1 flex w-full max-h-full flex-col gap-4 border p-6 shadow-lg sm:rounded-lg"
|
||||
: "bg-background fixed top-[50%] left-[50%] z-1000 flex w-full translate-x-[-50%] translate-y-[-50%] flex-col gap-4 border p-6 shadow-lg sm:rounded-lg",
|
||||
dialogSizeClass[size],
|
||||
className,
|
||||
scoped && "max-h-full",
|
||||
)}
|
||||
|
||||
@@ -55,7 +55,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-10 w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"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",
|
||||
className,
|
||||
|
||||
@@ -6,6 +6,21 @@
|
||||
Concept: "Solid + Glow" (no blur on main app).
|
||||
=================================================================== */
|
||||
|
||||
/* ===================================================================
|
||||
⚠️ DEPRECATION NOTICE (2026-04-22)
|
||||
-------------------------------------------------------------------
|
||||
NEW CODE MUST USE shadcn components from @/components/ui/*:
|
||||
<Button>, <Input>, <Table>, <Dialog>, <Badge>, <Select>, ...
|
||||
|
||||
The .v5-* classes below are LEGACY and kept only for:
|
||||
- Atomic patterns inside v5-only prototypes
|
||||
- Gradual migration of existing code (see grep for .v5-btn / .v5-bdg /
|
||||
.v5-tbl / .v5-modal / .v5-fi usages — currently < 20 hits)
|
||||
|
||||
DO NOT reference .v5-* classes in new components.
|
||||
Prefer shadcn ui primitives which wrap Radix + semantic tokens.
|
||||
=================================================================== */
|
||||
|
||||
/* =================================================================
|
||||
Buttons (.v5-btn + variants)
|
||||
Defaults: 30px height, 0.7rem text, primary fills, glow on hover.
|
||||
|
||||
Reference in New Issue
Block a user