Icons

Interactive UI Explorer

아래에서 각 컴포넌트의 다양한 Variants States를 문서화된 형태로 테스트해볼 수 있습니다.

Live Preview

Icons

Standardized SVG icons as reusable components.

General Icons

Frequently used icons across the application.

Source Code
<PlusIcon className="h-5 w-5 text-sky-600" />
<ChevronLeftIcon className="h-5 w-5 text-slate-600" />
<ChevronRightIcon className="h-5 w-5 text-slate-600" />
<PencilSquareIcon className="h-5 w-5 text-violet-600" />
<TrashIcon className="h-5 w-5 text-red-600" />
<EyeIcon className="h-5 w-5 text-green-600" />
<HeartIcon className="h-5 w-5 text-blue-600" />
<InfoIcon className="h-5 w-5 text-yellow-600" />
<XIcon className="h-5 w-5 text-gray-600" />
<MenuIcon className="h-5 w-5 text-purple-600" />
<ArrowRightIcon className="h-5 w-5 text-pink-600" />
<ChevronLeftIcon className="h-5 w-5 text-teal-600" />
<ChevronRightIcon className="h-5 w-5 text-indigo-600" />
PlusIcon
SearchIcon
TrashIcon
EyeIcon
HeartIcon
InfoIcon
XIcon
MenuIcon
ArrowRightIcon
ChevronLeftIcon
ChevronRightIcon
PencilSquareIcon
Live Preview
Implementation

제작 코드

이 컴포넌트가 실제로 어떻게 구현되어 있는지 — 본체 .tsx 파일을 그대로 보여줍니다. variant 매핑·접근성 처리·forwardRef 패턴 등 디테일을 그대로 확인할 수 있어요.

Generaltypescript
import { cn } from "@/lib/cn";

interface IconProps {
  className?: string;
  strokeWidth?: number;
}

export const PlusIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" />
  </svg>
);

export const SearchIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
  </svg>
);

export const TrashIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
  </svg>
);

/** 클립보드 복사 (URL 등) */
export const ClipboardIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    viewBox="0 0 24 24"
    aria-hidden
  >
    <path
      strokeLinecap="round"
      strokeLinejoin="round"
      d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
    />
  </svg>
);

/** 새 탭 / 외부 링크 열기 */
export const ArrowTopRightOnSquareIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    viewBox="0 0 24 24"
    aria-hidden
  >
    <path
      strokeLinecap="round"
      strokeLinejoin="round"
      d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
    />
  </svg>
);

export const EyeIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
    <path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.522 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.478 0-8.268-2.943-9.542-7z" />
  </svg>
);

export const HeartIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
  </svg>
);

/** 댓글 수 등 (쇼케이스·블로그 카드) */
export const ChatBubbleIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24" aria-hidden>
    <path
      strokeLinecap="round"
      strokeLinejoin="round"
      d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
    />
  </svg>
);

export const InfoIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
  </svg>
);

export const XIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
  </svg>
);

export const MenuIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M4 6h16M4 12h16M4 18h16" />
  </svg>
);
export const ArrowRightIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg className={cn("h-4 w-4", className)} fill="none" stroke="currentColor" strokeWidth={strokeWidth} viewBox="0 0 24 24">
    <path strokeLinecap="round" strokeLinejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3" />
  </svg>
);

/** 이전 페이지 (Pagination 등) */
export const ChevronLeftIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M15 18l-6-6 6-6" />
  </svg>
);

/** 다음 페이지 (Pagination 등) */
export const ChevronRightIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M9 18l6-6-6-6" />
  </svg>
);

/** 아코디언·드롭다운 펼침 등 */
export const ChevronDownIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M19 9l-7 7-7-7" />
  </svg>
);

/** 다시 시도 / 새로고침 (퀴즈 다시 풀기 등) */
export const ArrowPathIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M4 4v5h5M20 20v-5h-5" />
    <path d="M19 9a8 8 0 00-14.32-2.5M5 15a8 8 0 0014.32 2.5" />
  </svg>
);

/** 공유하기 (노드 연결 형태) */
export const ShareIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <circle cx="18" cy="5" r="3" />
    <circle cx="6" cy="12" r="3" />
    <circle cx="18" cy="19" r="3" />
    <path d="M8.59 13.51l6.83 3.98M15.41 6.51l-6.82 3.98" />
  </svg>
);

/** 드래그 손잡이 (세로 그립 점) */
export const GripVerticalIcon = ({ className, strokeWidth = 2 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="currentColor"
    stroke="none"
    strokeWidth={strokeWidth}
    aria-hidden
  >
    <circle cx="9" cy="6" r="1.6" />
    <circle cx="9" cy="12" r="1.6" />
    <circle cx="9" cy="18" r="1.6" />
    <circle cx="15" cy="6" r="1.6" />
    <circle cx="15" cy="12" r="1.6" />
    <circle cx="15" cy="18" r="1.6" />
  </svg>
);

/** 홈 / 메인으로 */
export const HomeIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M3 10.5L12 3l9 7.5" />
    <path d="M5 9.5V20a1 1 0 001 1h12a1 1 0 001-1V9.5" />
  </svg>
);

/** 목록 / 문항 수 (퀴즈 문항 등) */
export const QueueListIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" />
  </svg>
);

/** 시계 / 시간 제한 */
export const ClockIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <circle cx="12" cy="12" r="9" />
    <path d="M12 7v5l3 2" />
  </svg>
);

/** 물음표 원형 / 객관식·도움말 */
export const QuestionMarkCircleIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <circle cx="12" cy="12" r="9" />
    <path d="M9.5 9.5a2.5 2.5 0 014.9.7c0 1.7-2.4 2.1-2.4 3.8" />
    <path d="M12 17h.01" />
  </svg>
);

/** 새싹 / 초급 (난이도) */
export const SproutIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M12 22V11" />
    <path d="M12 11C12 7.5 9.5 5 5 5c0 4 2.5 6.5 7 6.5z" />
    <path d="M12 9.5C12 6.5 14.2 4 18.5 4c0 3.5-2.2 6-6.5 6z" />
  </svg>
);

/** 로켓 / 중급 (난이도) */
export const RocketIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 00-2.91-.09z" />
    <path d="M12 15l-3-3a22 22 0 014-6.5C15.5 2.5 18 2 22 2c0 4-.5 6.5-3.5 9.5A22 22 0 0112 15z" />
    <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" />
    <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" />
  </svg>
);

/** 불꽃 / 상급 (난이도) */
export const FireIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M8.5 14.5A2.5 2.5 0 0011 12c0-1.38-.5-2-1-3-1.07-2.14-.22-4.05 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 11-14 0c0-1.15.43-2.29 1-3a2.5 2.5 0 002.5 2.5z" />
  </svg>
);

/** 수정 / 편집 (연필·문서, Heroicons 스타일) */
export const PencilSquareIcon = ({ className, strokeWidth = 2.5 }: IconProps) => (
  <svg
    className={cn("h-4 w-4", className)}
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    strokeWidth={strokeWidth}
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden
  >
    <path d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125" />
  </svg>
);