File name
Commit message
Commit date
File name
Commit message
Commit date
import type { ComponentProps } from "react";
type ButtonVariant = "primary" | "secondary" | "success" | "ghost";
type ButtonSize = "sm" | "md";
const VARIANT_CLASSES: Record<ButtonVariant, string> = {
primary: "bg-primary text-on-primary hover:opacity-90",
secondary: "border border-border bg-surface text-foreground hover:bg-surface-muted",
// on-success 토큰이 아직 없어(§10.4 대상) primary 필렁 버튼과 동일하게 on-primary를
// 재사용한다 — 강조색(엑셀 다운로드 등) 액션용.
success: "bg-success text-on-primary hover:opacity-90",
ghost: "text-foreground-muted hover:bg-surface-muted hover:text-foreground",
};
const SIZE_CLASSES: Record<ButtonSize, string> = {
sm: "h-8 px-3 text-body-sm",
md: "h-10 px-4 text-body-md",
};
interface ButtonProps extends ComponentProps<"button"> {
variant?: ButtonVariant;
size?: ButtonSize;
}
/**
* 공용 버튼. 상태를 가지지 않는 프레젠테이션 컴포넌트 — 클릭 동작·로딩 처리는
* 호출부(feature) 책임이다. `type` 기본값은 `button`(폼 안에서 의도치 않은
* submit 방지) — 제출 버튼은 명시적으로 `type="submit"`을 전달한다.
*/
export function Button({
variant = "primary",
size = "md",
type = "button",
className,
...props
}: ButtonProps) {
return (
<button
type={type}
className={`inline-flex items-center justify-center gap-2 rounded-md transition-colors duration-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:pointer-events-none disabled:opacity-50 ${VARIANT_CLASSES[variant]} ${SIZE_CLASSES[size]} ${className ?? ""}`}
{...props}
/>
);
}