File name
Commit message
Commit date
08-14
File name
Commit message
Commit date
import type { ReactNode } from "react";
interface IconButtonProps {
/** 접근성 이름(`aria-label`)과 네이티브 툴팁(`title`)으로 쓰인다. 생략하면 두
* 속성을 렌더하지 않는다 — 이 경우 children에 포함된 `sr-only` 텍스트가 접근성
* 이름이 되어야 한다(예: 아이콘 여러 개를 CSS로 전환 노출하며 각각 옆에 개별
* `sr-only` 라벨을 두는 구성). */
label?: string;
/** 아이콘(들). 장식용 아이콘은 자체적으로 `aria-hidden`을 갖는다 — 접근성 이름이
* 필요하면 `label`을 전달하거나 children에 `sr-only` 텍스트를 포함해야 한다. */
children: ReactNode;
type?: "button" | "submit";
/** 2-상태 토글일 때만 전달 → `aria-pressed` 렌더. 3-상태 순환 등에서는 생략 권장
* (`aria-pressed`는 이진 개념이라 의미가 어긋난다). */
isPressed?: boolean;
onClick?: () => void;
disabled?: boolean;
/** 배치(레이아웃) 조정용 클래스 패스스루만 허용 — 디자인 값 주입 금지. */
className?: string;
}
/**
* 아이콘 전용 버튼 비주얼. 상태를 가지지 않는 프레젠테이션 컴포넌트 — 클릭 동작·상태
* 관리는 호출부(feature) 책임이다.
*/
export function IconButton({
label,
children,
type = "button",
isPressed,
onClick,
disabled,
className,
}: IconButtonProps) {
return (
<button
type={type}
onClick={onClick}
disabled={disabled}
aria-label={label}
title={label}
aria-pressed={isPressed}
className={`inline-flex size-9 items-center justify-center rounded-md border border-border text-foreground-muted transition-colors duration-200 hover:bg-surface-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:pointer-events-none disabled:opacity-50 ${className ?? ""}`}
>
{children}
</button>
);
}