UI Phase B — shadcn을 SSoT로, 컴포넌트 디자인 정리
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:
Claude (gbpark)
2026-04-22 22:16:22 +09:00
parent 3f481acd8e
commit 1a4586e126
5 changed files with 39 additions and 15 deletions
+9 -7
View File
@@ -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: {
+1 -4
View File
@@ -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",
+13 -3
View File
@@ -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",
)}
+1 -1
View File
@@ -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,
+15
View File
@@ -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.