디자이너와 개발자를 위한 공식 기준
SANDSLab 디자인 시스템은 제품 전반에 걸쳐 일관성 있는 UI/UX를 제공하기 위한 규칙, 컴포넌트, 토큰의 집합입니다. 디자이너는 Figma 작업의 기준으로, 개발자는 구현 시 토큰·컴포넌트 스펙의 근거로 활용합니다.
색상
브랜드 아이덴티티와 UI 의미 전달을 위한 색상 팔레트입니다.
색상을 클릭하면 10단계 색상 베리에이션이 아래에 표시됩니다.
다크 모드에서는 WCAG AA 대비를 확보하기 위해 일부 색상 값이 라이트 모드와 다르게 조정됩니다. Primary와 Sub Warm은 그대로 유지되고, 나머지는 아래 값으로 대체됩니다. 색상을 클릭하면 10단계 색상 베리에이션이 아래에 표시됩니다.
각 배경(color-bg/color-surface) 위에서 사용하는 기본 텍스트(color-text)와 보조 텍스트(color-muted) 색상 조합입니다.
타이포그래피
국문과 영문에 각각 역할에 맞는 폰트 패밀리를 적용합니다.
상태
모든 인터랙티브 요소가 공유하는 상태 처리 규칙입니다. 색상 값은 색상 파운데이션의 시맨틱 토큰을 그대로 사용하고, 이 섹션은 상태별 처리 방식(오버레이·변형·포커스 링)을 정의합니다.
| 상태 | 처리 방식 | 값 |
|---|---|---|
| Default | 기본 배경 · 텍스트 | color-surface / color-text-default |
| Hover | 라이트 서페이스: 전경색 4% 오버레이 · 다크 서페이스: 흰색 6~7% 오버레이 | rgba(44,51,61,.04) / rgba(255,255,255,.06) |
| Active / Pressed | 1px 하강 변형 + 색상 강조(솔리드 버튼은 더 어둡게, 아웃라인 버튼은 solid 반전 채움) | transform: translateY(1px) |
| Focus | 2px 포커스 링, 오프셋 2px, 항상 visible | outline: 2px solid color-brand-primary |
| Selected | 액센트 틴트 배경 | #E9F1FE / #16324E |
| Disabled | 배경 sub-cool, 텍스트 muted, cursor not-allowed | color-disabled / color-text-muted |
| Loading | 인터랙션 차단 + 스켈레톤 또는 스피너 | pointer-events: none |
| Error | 테두리 · 텍스트를 error 색으로, 보조 설명 필수 | color-state-error |
아래 버튼에 마우스를 올리거나 눌러 실제 Hover/Active 처리를 확인하세요.
아이콘
Lucide(lucide.dev) 아이콘 세트를 기본으로 채택합니다. 용도에 맞는 고정 크기 스케일과 사용 원칙입니다.
<script src="https://cdn.jsdelivr.net/npm/lucide@latest/dist/umd/lucide.js"></script>
<i data-lucide="shield" width="20" height="20"></i>
<script>
lucide.createIcons();
</script>
간격
기준 단위 4px 배수 기반의 간격 시스템입니다.
Round
UI 요소의 모서리 곡률을 정의하는 Border Radius 시스템입니다. 기본값은 round-none(0px)입니다 — UI를 디자인할 때는 각진(round-none) 스타일을 우선 적용하고, 명시적인 이유가 있을 때만 더 큰 값을 선택적으로 사용합니다.
테두리
모서리 곡률(Round)과 별개로, 테두리의 두께와 색상 역할을 정의합니다.
| 토큰 | 용도 |
|---|---|
border-default | 일반 테두리 (카드, 인풋) |
border-subtle | 약한 구분선 (테이블, 리스트) |
border-strong | Outlined 변형의 강조 테두리 |
border-brand | 선택됨 · 포커스와 연동되는 브랜드 테두리 |
그림자
요소 간 깊이(elevation)를 표현하는 5단계 그림자 스케일입니다.
--shadow-none: none;
--shadow-xs: 0 1px 2px rgba(44,51,61,.06);
--shadow-sm: 0 2px 6px rgba(44,51,61,.08);
--shadow-md: 0 8px 24px rgba(44,51,61,.12), 0 2px 6px rgba(44,51,61,.06);
--shadow-lg: 0 16px 40px rgba(44,51,61,.16), 0 4px 12px rgba(44,51,61,.08);
라이트 모드용 그림자 색상(옅은 회색)은 어두운 배경에서 거의 보이지 않습니다. 다크 모드에서는 더 짙고 불투명한 그림자를 사용해야 시인성이 유지됩니다.
--shadow-none: none;
--shadow-xs: 0 1px 3px rgba(4,20,35,.4);
--shadow-sm: 0 3px 8px rgba(4,20,35,.45);
--shadow-md: 0 10px 28px rgba(4,20,35,.55), 0 3px 8px rgba(4,20,35,.35);
--shadow-lg: 0 18px 44px rgba(4,20,35,.6), 0 5px 14px rgba(4,20,35,.4);
모션
상태 전환에 사용하는 지속 시간과 이징(easing) 표준입니다. 이 문서 곳곳의 버튼 · 토글 hover가 이미 이 표준을 따릅니다.
| 토큰 | 값 | 용도 |
|---|---|---|
duration-fast | 120ms | 색상 · 배경 hover 전환 |
duration-base | 150ms | 버튼, 토글, 카드 상태 전환 |
duration-slow | 220ms | 테마 전환, 큰 영역 배경색 전환 |
| 토큰 | 값 | 용도 |
|---|---|---|
easing-standard | cubic-bezier(.4,0,.2,1) | 대부분의 상태 전환 |
easing-decelerate | cubic-bezier(0,0,.2,1) | 진입 — 모달, 토스트 등장 |
easing-accelerate | cubic-bezier(.4,0,1,1) | 퇴장 — 모달, 토스트 소멸 |
레이어
이 문서를 포함한 실제 구현 기준의 z-index 스케일입니다. 새 레이어가 필요하면 이 표에 토큰을 추가하고, 임의의 숫자를 인라인으로 넣지 않습니다.
| 토큰 | 값 | 용도 |
|---|---|---|
layer-base | 0 | 기본 콘텐츠, 정적 레이아웃 |
layer-sticky | 1 | 고정 테이블 헤더 |
layer-sidebar | 100 | 2depth 사이드바 |
layer-nav | 200 | 상단 GNB |
layer-modal | 1000 | 모달 오버레이 |
layer-toast | 2000 | 토스트 — 항상 최상단 |
콘텐츠
UI 카피, 에러 메시지, 빈 상태 문구를 작성할 때 따르는 원칙입니다.
그리드 & 레이아웃
반응형 화면을 구성하기 위한 브레이크포인트와 그리드 기준입니다.
| 이름 | 범위 | 기기 |
|---|---|---|
xs | ~ 479px | 소형 모바일 |
sm | 480px ~ 767px | 모바일 |
md | 768px ~ 1023px | 태블릿 |
lg | 1024px ~ 1279px | 소형 데스크탑 |
xl | 1280px ~ 1535px | 데스크탑 |
2xl | 1536px ~ | 대형 데스크탑 |
| 브레이크포인트 | 컬럼 | 거터 | 여백(사이드) |
|---|---|---|---|
| xs / sm | 4 | 16px | 16px |
| md | 8 | 24px | 32px |
| lg / xl | 12 | 24px | 48px |
| 2xl | 12 | 32px | auto (max-width 1440px) |
로고
SANDSLab 브랜드 로고입니다. 배경 밝기에 따라 워드마크 색상을 전환해 항상 충분한 대비를 유지합니다.
SVG 파일을 그대로 다운로드하거나, 코드를 복사해 바로 사용할 수 있습니다.
<svg class="logo" role="img" aria-label="SANDSLab" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1550 350">
<defs>
<style>.cls-1{fill:url(#_g5b);}.cls-2{fill:#fff;}.cls-3{fill:url(#_g205b);}</style>
<radialGradient id="_g205b" cx="-239.94" cy="690.7" fx="-239.94" fy="690.7" r="1.07" gradientTransform="translate(-103752.49 21422.56) rotate(90) scale(88.42 -150.5)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#7c181d"/>
<stop offset="1" stop-color="#c34752"/>
</radialGradient>
<linearGradient id="_g5b" x1="254.05" y1="153.38" x2="126.36" y2="110.11" gradientTransform="translate(0 352) scale(1 -1)" gradientUnits="userSpaceOnUse">
<stop offset=".06" stop-color="#e49198"/>
<stop offset="1" stop-color="#c34752"/>
</linearGradient>
</defs>
<path class="cls-2" d="M1391.84,247.94V104.31l23.98-4.31v51.5c8.59-7.11,18.84-10.67,30.71-10.67,10.11,0,19.19,2.46,27.25,7.39,8.07,4.79,14.41,11.29,19.05,19.49,4.78,8.07,7.18,17.24,7.18,27.5s-2.39,19.29-7.18,27.5c-4.77,8.21-11.27,14.77-19.46,19.7-8.05,4.79-17.13,7.18-27.23,7.18-5.6,0-11.07-.82-16.39-2.46-5.19-1.78-9.98-4.24-14.34-7.39v8.21h-23.57ZM1443.05,228.86c6.43,0,12.09-1.44,17.01-4.31,5.05-3.01,9.02-7.04,11.87-12.11,3-5.06,4.51-10.81,4.51-17.24s-1.51-12.17-4.51-17.24c-2.86-5.06-6.82-9.03-11.87-11.9-5.05-3.01-10.73-4.51-17.01-4.51-5.45,0-10.57,1.03-15.36,3.08-4.64,1.92-8.61,4.72-11.87,8.41v44.53c3.13,3.56,7.09,6.36,11.87,8.41,4.91,1.91,10.03,2.87,15.36,2.87Z"/>
<path class="cls-2" d="M1314.01,249.8c-7.52,0-14.21-1.37-20.08-4.1-5.73-2.74-10.23-6.5-13.52-11.29-3.27-4.92-4.91-10.53-4.91-16.82,0-10.12,3.75-17.99,11.27-23.6,7.64-5.61,18.09-8.41,31.34-8.41,9.84,0,18.98,1.64,27.46,4.92v-9.44c0-6.84-2.05-11.97-6.16-15.39-3.95-3.42-9.89-5.13-17.82-5.13-4.64,0-9.55.68-14.75,2.05-5.18,1.37-11.12,3.63-17.82,6.77l-8.61-18.06c8.07-3.69,15.71-6.43,22.94-8.21,7.25-1.92,14.55-2.87,21.92-2.87,13.93,0,24.71,3.35,32.37,10.05,7.64,6.7,11.46,16.14,11.46,28.32v69.35h-23.55v-8.41c-4.51,3.56-9.36,6.16-14.55,7.8-5.19,1.64-10.86,2.46-17,2.46ZM1298.23,216.97c0,4.65,1.98,8.41,5.94,11.29,3.96,2.73,9.21,4.1,15.78,4.1,5.05,0,9.69-.68,13.93-2.05,4.36-1.5,8.25-3.69,11.68-6.57v-16.83c-3.69-1.78-7.59-3.08-11.68-3.9-3.96-.96-8.34-1.44-13.12-1.44-6.96,0-12.48,1.37-16.59,4.1-3.96,2.74-5.94,6.5-5.94,11.29Z"/>
<path class="cls-2" d="M1155.85,247.94V104.31h25.19v121.06h80.72v22.57h-105.91Z"/>
<path class="cls-2" d="M1035.98,250c-11.46,0-22.66-2.19-33.59-6.57-10.78-4.51-20.35-10.81-28.67-18.88l14.95-18.06c8.05,7.52,15.98,13.06,23.76,16.62,7.93,3.56,16.32,5.33,25.19,5.33,6.43,0,12.02-.89,16.8-2.67,4.91-1.78,8.68-4.24,11.27-7.39,2.73-3.28,4.09-7.04,4.09-11.29,0-5.75-2.04-10.12-6.14-13.13-3.96-3.15-10.71-5.47-20.28-6.98l-26.01-4.51c-12.7-2.19-22.25-6.43-28.67-12.72-6.41-6.43-9.62-14.77-9.62-25.03,0-8.62,2.18-16.07,6.55-22.37,4.5-6.43,10.86-11.35,19.05-14.77,8.19-3.42,17.82-5.13,28.89-5.13,10.37,0,20.69,1.71,30.92,5.13,10.25,3.42,19.39,8.28,27.46,14.57l-13.94,19.08c-15.16-11.63-30.51-17.44-46.08-17.44-5.73,0-10.78.82-15.16,2.46-4.37,1.51-7.78,3.69-10.25,6.57-2.32,2.73-3.48,6.02-3.48,9.85,0,5.2,1.78,9.16,5.34,11.9,3.68,2.74,9.62,4.79,17.82,6.16l24.98,4.1c14.89,2.46,25.89,6.98,32.99,13.54,7.09,6.57,10.64,15.46,10.64,26.67,0,9.03-2.46,16.96-7.37,23.8-4.78,6.7-11.61,11.9-20.48,15.6-8.75,3.69-19.05,5.54-30.94,5.54Z"/>
<path class="cls-2" d="M861.59,225.37h27.44c6.98,0,13.46-1.23,19.46-3.69,6.02-2.46,11.27-5.95,15.78-10.46,4.5-4.51,7.98-9.71,10.45-15.6,2.59-5.88,3.89-12.31,3.89-19.29s-1.3-13.47-3.89-19.49c-2.46-6.02-5.94-11.22-10.45-15.59-4.51-4.51-9.77-8-15.78-10.46-6-2.6-12.48-3.9-19.46-3.9h-27.44v98.49ZM836.39,247.94V104.31h52.64c10.8,0,20.76,1.85,29.92,5.54,9.14,3.56,17.13,8.62,23.96,15.18,6.82,6.43,12.16,14.02,15.98,22.78,3.82,8.75,5.73,18.26,5.73,28.52s-1.91,19.77-5.73,28.52c-3.82,8.62-9.16,16.14-15.98,22.57-6.84,6.43-14.82,11.49-23.96,15.18-9.16,3.56-19.12,5.33-29.92,5.33h-52.64Z"/>
<path class="cls-2" d="M684.55,247.94V104.31h24.17l76,100.34v-100.34h23.97v143.63h-22.53l-77.84-103.41v103.41h-23.76Z"/>
<path class="cls-2" d="M524,247.94l58.99-143.63h29.5l58.58,143.63h-27.45l-15.77-40.01h-61.86l-15.77,40.01h-26.22ZM574.18,187.2h45.89l-22.94-58.27-22.94,58.27Z"/>
<path class="cls-2" d="M462.27,250c-11.47,0-22.67-2.19-33.59-6.57-10.79-4.51-20.35-10.81-28.68-18.88l14.95-18.06c8.06,7.52,15.98,13.06,23.76,16.62,7.92,3.56,16.32,5.33,25.2,5.33,6.42,0,12.02-.89,16.8-2.67,4.92-1.78,8.67-4.24,11.27-7.39,2.73-3.28,4.1-7.04,4.1-11.29,0-5.75-2.05-10.12-6.15-13.13-3.96-3.15-10.72-5.47-20.28-6.98l-26.02-4.51c-12.7-2.19-22.26-6.43-28.68-12.72-6.42-6.43-9.63-14.77-9.63-25.03,0-8.62,2.19-16.07,6.55-22.37,4.51-6.43,10.86-11.35,19.05-14.77,8.19-3.42,17.82-5.13,28.88-5.13,10.38,0,20.69,1.71,30.93,5.13,10.24,3.42,19.39,8.28,27.45,14.57l-13.93,19.08c-15.16-11.63-30.52-17.44-46.09-17.44-5.73,0-10.79.82-15.16,2.46-4.37,1.51-7.78,3.69-10.24,6.57-2.32,2.73-3.48,6.02-3.48,9.85,0,5.2,1.78,9.16,5.33,11.9,3.69,2.74,9.63,4.79,17.82,6.16l24.99,4.1c14.88,2.46,25.88,6.98,32.98,13.54,7.1,6.57,10.65,15.46,10.65,26.67,0,9.03-2.46,16.96-7.37,23.8-4.78,6.7-11.61,11.9-20.48,15.6-8.74,3.69-19.05,5.54-30.93,5.54Z"/>
<path class="cls-3" d="M154.7,177.62l26.4-120.95c.85-3.88,4.41-6.66,8.53-6.66h20.68c4.17,0,7.76,2.84,8.56,6.77l24.62,120.84,103.44,84.67c3.28,2.68,4.03,7.26,1.77,10.79l-14.7,22.94c-2.1,3.28-6.25,4.76-10.06,3.59l-124.85-38.13-122.98,38.09c-3.82,1.18-7.99-.3-10.1-3.58l-14.72-22.97c-2.24-3.5-1.52-8.05,1.72-10.74l101.68-84.66Z"/>
<path class="cls-1" d="M243.44,177.39l.04.25,4.85,98.88-49.24-15.04h-.02s-84.61-50.35-84.61-50.35l40.23-33.5,79.01-48.07,9.74,47.82Z"/>
</svg>
Button
사용자 행동을 유도하는 버튼입니다. 우선순위에 따라 variant를 선택하세요.
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">기본 버튼</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">주요 버튼</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">기본</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">주요</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-slate-500 border-0 rounded-none cursor-pointer">보조</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-red-600 border-0 rounded-none cursor-pointer">포인트</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer">텍스트</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-[#1f1f1f] border-0 rounded-none cursor-pointer">블랙</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer">위험</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer">기본</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-0 rounded-none cursor-pointer">보조</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-medium no-underline tracking-tight whitespace-nowrap text-slate-500 bg-transparent border-0 rounded-none cursor-pointer">저채도</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-[#000] bg-transparent border-0 rounded-none cursor-pointer">검정</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-0 rounded-none cursor-pointer">위험</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">기본</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-[rgba(44,51,61,0.04)] border-[1.5px] border-stone-300 rounded-none cursor-pointer">호버</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-slate-800 border-[1.5px] border-stone-300 rounded-none cursor-pointer">눌림</button>
<button class="inline-flex items-center justify-center gap-1.5 h-7 py-0 px-2.5 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">아주 작게</button>
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">작게</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">보통</button>
<button class="inline-flex items-center justify-center gap-1.5 h-12 py-0 px-6 text-base font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">크게</button>
<!-- 아이콘 단독 — aria-label 필수 -->
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-label="편집">
<svg width="16" height="16" viewBox="0 0 16 16">…</svg>
</button>
<!-- 아이콘 + 레이블 -->
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">
<svg width="16" height="16" viewBox="0 0 16 16">…</svg>
편집하기
</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-default opacity-85" aria-busy="true">
<span class="w-[13px] h-[13px] border-2 border-[rgba(255,255,255,0.4)] rounded-full" aria-hidden="true"></span>저장 중
</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" disabled>주요</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" disabled>기본</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer" disabled>위험</button>
Input
Input은 한 줄 텍스트 입력 필드입니다. Select, Textarea, DateInput은 별도 컴포넌트로 다루며 Input의 variant로 확장하지 않습니다.
<div class="flex flex-col gap-[7px]">
<label class="text-xs font-semibold text-slate-800">이메일</label>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" placeholder="이메일을 입력하세요">
<span class="text-xs text-slate-500">사용할 이메일 주소를 입력해 주세요.</span>
</div>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" value="hello@sandslab.io">
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" value="hello@invalid"
aria-invalid="true" aria-describedby="email-error">
<span class="text-xs text-red-600" id="email-error">올바른 이메일 형식이 아닙니다.</span>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" value="비활성 상태" disabled>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none input-sm" type="text" placeholder="sm · 32px">
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none input-md" type="text" placeholder="md · 40px">
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none input-lg" type="text" placeholder="lg · 48px">
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" placeholder="클릭하여 포커스">
<!-- 포커스 링은 :focus-visible 스타일로 처리되며 별도 클래스가 필요 없습니다 -->
Card
연관 정보를 그룹화하는 컨테이너입니다. 세 가지 스타일 변형이 있습니다.
card에 square 클래스를 추가하면 모서리가 각지게 바뀝니다.
배지
상태·분류·심각도·개수를 짧은 레이블로 표시합니다. 클릭·제거 동작이 없으며 색상은 항상 텍스트 레이블과 함께 사용합니다.
Modal
사용자 확인이 필요한 중요 액션이나 폼을 오버레이로 표시합니다.
버튼을 클릭하면 모달이 열립니다
modal에 square 클래스를 추가하면 모서리가 각지게 바뀝니다.
Toast / Notification
작업 결과를 비침습적으로 알리는 알림 컴포넌트입니다. 4초 후 자동으로 닫힙니다.
toast-static에 square 클래스를 추가하면 모서리가 각지게 바뀝니다.
테이블
밀도 높은 정형 데이터 탐색용 컴포넌트. IOC 목록, 이벤트 로그, 자산 목록 등 보안 운영 화면에 사용합니다.
| 심각도 | 탐지 수 | 지표 (IOC) | 유형 | 타임스탬프 | 상태 | 액션 | |
|---|---|---|---|---|---|---|---|
| Critical | 847 | 185.220.101[.]47 | IP 주소 | 2분 전 | Active | ||
| High | 23 | 3f2a9c1e…b7d40e91 | 파일 해시 | 14분 전 | 조사 중 | ||
| High | 156 | malware-c2[.]example[.]com | 도메인 | 1시간 전 | Active | ||
| Medium | 12 | 10.0.0[.]254 | IP 주소 | 3시간 전 | 해결됨 | ||
| Low | 4 | payload_v2[.]exe | 파일명 | 1일 전 | 해결됨 |
| 해시 | 파일 타입 | 크기 | 탐지명 | VT 탐지 |
|---|---|---|---|---|
| a3f9d2c1b84e7a60f5d3c9e2814b7a40e9c1f3d2b6a8e74c09f1d3e582b6a9c0 | Win32 EXE | 2.6 MB |
Trojan.Lumma Stealer
aka Aco4cpki | Disco | LummaC2.Gen
|
53 / 76 |
| 7b2e9f4d1c06a38e5c4d7b9e2f1a3c6d8e0b4f2a7c9d1e3f5b8a2c4e6d0f9b3e | Win32 DLL | 841 KB |
Trojan.GenericKD.71234512
aka Malware.AI.Lumma | SuspiciousPE.Generic
|
38 / 72 |
| d5a1e8b3c07f2d9e4a6c8b1e5d3f7a0c2e4b6d8f0a2c5e7d9b1f3a5c7e9d2b4f | Win32 EXE | 1.1 MB |
Backdoor.Agent.MSIL
aka AsyncRAT | NetWireRC.Variant
|
21 / 74 |
GNB
제품의 최상위(1depth) 메뉴를 담는 전역 내비게이션 바입니다. 이 사이트 자체가 실제 구현 예시입니다.
<header class="flex items-center gap-7 fixed top-0 left-0 right-0 h-[60px] py-0 px-5 bg-slate-800 border-b border-[rgba(255,255,255,0.07)]">
<div class="flex items-center gap-[9px] text-white cursor-pointer">SANDSLab</div>
<nav class="flex flex-1 gap-0.5 min-w-0">
<button class="py-2 px-4 text-sm font-semibold whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-current="page">Foundations</button>
<button class="py-2 px-4 text-sm font-semibold whitespace-nowrap text-[rgba(255,255,255,0.56)] bg-transparent border-0 rounded-none cursor-pointer">Components</button>
</nav>
<div class="flex items-center gap-3.5">
<button class="gnb-login">로그인</button>
<div class="flex items-center gap-2 cursor-pointer" role="button" aria-label="테마 변경">…</div>
<div class="flex border border-stone-300 rounded-none overflow-hidden" role="group" aria-label="언어 변경">
<button class="py-1 px-3 text-xs font-semibold tracking-wider leading-relaxed text-white bg-slate-800 border-0 cursor-pointer">KO</button><button class="py-1 px-3 text-xs font-semibold tracking-wider leading-relaxed text-slate-500 bg-[#F2F0EA] border-0 cursor-pointer">EN</button>
</div>
</div>
</header>
<!-- Light -->
<header class="flex items-center gap-7 fixed top-0 left-0 right-0 h-[60px] py-0 px-5 bg-slate-800 border-b border-[rgba(255,255,255,0.07)] gnb-light">…</header>
<!-- Dark (기본값) -->
<header class="flex items-center gap-7 fixed top-0 left-0 right-0 h-[60px] py-0 px-5 bg-slate-800 border-b border-[rgba(255,255,255,0.07)]">…</header>
SNB
GNB에서 선택한 메뉴의 하위(2depth) 항목을 그룹으로 보여주는 좌측 내비게이션입니다.
<nav class="w-[160px] py-3.5 px-0 text-white bg-slate-800 rounded-lg" data-page="foundations">
<div class="sm-label">Foundations</div>
<a class="sm-item current" href="#colors" aria-current="page">Color</a>
<a class="sm-item" href="#typography">Typography</a>
</nav>
<!-- Light — 배경: color-bg -->
<nav class="w-[160px] py-3.5 px-0 text-white bg-slate-800 rounded-lg snb-light">…</nav>
<!-- Dark (기본값) -->
<nav class="w-[160px] py-3.5 px-0 text-white bg-slate-800 rounded-lg">…</nav>
Page Layout
GNB, SNB, Main 영역을 조합하는 애플리케이션의 기본 페이지 골격입니다.
<header class="flex items-center gap-7 fixed top-0 left-0 right-0 h-[60px] py-0 px-5 bg-slate-800 border-b border-[rgba(255,255,255,0.07)]">…</header>
<main class="flex-1 min-w-0 mt-[60px] ml-[224px]" style="margin-left:0">…</main>
<header class="flex items-center gap-7 fixed top-0 left-0 right-0 h-[60px] py-0 px-5 bg-slate-800 border-b border-[rgba(255,255,255,0.07)]">…</header>
<aside class="flex flex-col fixed top-[60px] left-0 w-[224px] h-[calc(100vh_-_60px)] bg-slate-800">…</aside>
<main class="flex-1 min-w-0 mt-[60px] ml-[224px]">…</main>
Action Bar
화면이나 섹션 상단에서 제목과 주요 액션을 한 줄에 배치하는 영역입니다.
<div class="flex items-center justify-between w-full py-2.5 px-0 border-b border-stone-300">
<h2 class="text-sm font-bold">IOC 목록</h2>
<div class="flex items-center gap-2">
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">내보내기</button>
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">지표 추가</button>
</div>
</div>
<div class="flex items-center justify-between w-full py-2.5 px-0 border-b border-stone-300">
<div class="flex items-center flex-1 gap-2">
<select class="w-full h-10 pt-0 pr-9 pb-0 pl-3.5 text-sm text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-label="보기 선택">…</select>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none has-leading" type="search" placeholder="검색" aria-label="검색">
</div>
<div class="flex items-center gap-2">
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">추가</button>
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">내보내기</button>
<span class="w-px h-5 my-0 mx-0.5 bg-stone-300" aria-hidden="true"></span>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="더 보기">⋯</button>
</div>
</div>
SNBToggle
SNB를 접거나 펼쳐 콘텐츠 영역의 폭을 조절하는 아이콘 버튼입니다.
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer"
aria-label="사이드바 접기" aria-expanded="true" aria-controls="sidebar">
<svg>…</svg>
</button>
UserConsole
아바타와 이름을 트리거로 삼아 계정 관련 Menu를 여는 GNB의 사용자 영역입니다.
<button class="flex items-center gap-2 py-1.5 px-2.5 text-[rgba(255,255,255,0.6)] rounded-full cursor-pointer" aria-haspopup="menu" aria-expanded="false">
<img class="flex items-center justify-center w-8 h-8 text-xs font-bold text-slate-800 bg-slate-200 rounded-full" src="…" alt="">
<span class="text-xs text-[rgba(255,255,255,0.8)]">김지훈</span>
</button>
<div role="menu">
<div role="menuitem">프로필</div>
<div role="menuitem">설정</div>
<div role="separator"></div>
<div role="menuitem" class="danger">로그아웃</div>
</div>
<button class="flex items-center gap-2 p-[7px] text-[rgba(255,255,255,0.6)] rounded-full cursor-pointer" aria-haspopup="menu" aria-expanded="false" aria-label="계정 메뉴">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
</button>
<button class="user-console-trigger square" aria-haspopup="menu" aria-expanded="false">
<img class="list-avatar" src="…" alt="">
<span class="user-console-name">김지훈</span>
</button>
<button class="user-console-trigger icon-only square" aria-haspopup="menu" aria-expanded="false" aria-label="계정 메뉴">
<svg>…</svg>
</button>
<!-- Light -->
<button class="flex items-center gap-2 py-1.5 px-2.5 text-slate-500 rounded-full cursor-pointer">…</button>
<!-- Dark (기본값) -->
<button class="flex items-center gap-2 py-1.5 px-2.5 text-[rgba(255,255,255,0.6)] rounded-full cursor-pointer">…</button>
Form Layout
여러 Field Item을 폼 안에 배치하는 구조 패턴입니다. 짧은 필드는 2단, 긴 필드는 전체 폭을 사용합니다.
<form class="grid gap-4 w-full grid-cols-[1fr_1fr]">
<div class="flex flex-col gap-[7px]">이름 필드</div>
<div class="flex flex-col gap-[7px]">이메일 필드</div>
<div class="flex flex-col gap-[7px] full-row">설명 필드</div>
<div class="flex justify-end gap-2 mt-1">
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">취소</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">저장하기</button>
</div>
</form>
IconButton
아이콘만으로 액션을 표현하는 정사각형 버튼입니다. Button의 icon 전용 형태로, 시각적 레이블이 없어 접근성 요구가 더 엄격합니다.
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="검색">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-label="추가">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-slate-500 border-0 rounded-none cursor-pointer" aria-label="검색">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-red-600 border-0 rounded-none cursor-pointer" aria-label="즐겨찾기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-[#1f1f1f] border-0 rounded-none cursor-pointer" aria-label="더보기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer" aria-label="삭제">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 pl-0.5 pr-0.5 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-12 h-12 py-0 px-6 pl-0.5 pr-0.5 text-base font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-medium no-underline tracking-tight whitespace-nowrap text-slate-500 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 pl-0.5 pr-0.5 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-0 rounded-none cursor-pointer" aria-label="삭제">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-[rgba(44,51,61,0.04)] border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-slate-800 border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-12 h-12 py-0 px-6 text-base font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-label="편집">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-default opacity-85" aria-label="저장 중" aria-busy="true">
<span class="w-[13px] h-[13px] border-2 border-[rgba(255,255,255,0.4)] rounded-full" aria-hidden="true"></span>
</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer" aria-label="편집" disabled>…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="검색" disabled>…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer" aria-label="삭제" disabled>…</button>
ButtonGroup
관련된 액션이나 필터 옵션을 하나의 컨트롤로 묶어 보여주는 세그먼트형 버튼 묶음입니다.
<div class="inline-flex" role="group" aria-label="기간 선택">
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer is-selected" aria-pressed="true">일간</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-pressed="false">주간</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-pressed="false">월간</button>
</div>
<div class="inline-flex" role="group" aria-label="주요">…</div>
<div class="inline-flex bg-point" role="group" aria-label="포인트">…</div>
<div class="inline-flex bg-secondary" role="group" aria-label="보조">…</div>
<div class="inline-flex bg-black" role="group" aria-label="블랙">…</div>
<div class="inline-flex bg-danger" role="group" aria-label="위험">…</div>
IconButtonGroup
아이콘 버튼을 가로로 묶은 툴바형 그룹입니다. 서식 지정, 보기 전환 등 밀도 높은 액션 묶음에 사용합니다.
<div class="inline-flex" role="group" aria-label="보기 전환">
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer is-selected" aria-label="목록 보기" aria-pressed="true">…</button>
<button class="inline-flex items-center justify-center gap-1.5 w-10 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer" aria-label="그리드 보기" aria-pressed="false">…</button>
</div>
<div class="inline-flex" role="group" aria-label="주요">…</div>
<div class="inline-flex bg-point" role="group" aria-label="포인트">…</div>
<div class="inline-flex bg-secondary" role="group" aria-label="보조">…</div>
<div class="inline-flex bg-black" role="group" aria-label="블랙">…</div>
<div class="inline-flex bg-danger" role="group" aria-label="위험">…</div>
Breadcrumb
현재 위치까지의 계층 경로를 보여주는 내비게이션입니다.
<!-- 1단계 -->
<nav class="flex items-center flex-wrap gap-1.5 text-xs" aria-label="이동 경로">
<span class="current" aria-current="page">현재 페이지</span>
</nav>
<!-- 2단계 -->
<nav class="flex items-center flex-wrap gap-1.5 text-xs" aria-label="이동 경로">
<a href="/">최초 메뉴</a><span class="sep">/</span>
<span class="current" aria-current="page">현재 페이지</span>
</nav>
<!-- 3단계 -->
<nav class="flex items-center flex-wrap gap-1.5 text-xs" aria-label="이동 경로">
<a href="/">최초 메뉴</a><span class="sep">/</span>
<a href="/prev">이전 페이지</a><span class="sep">/</span>
<span class="current" aria-current="page">현재 페이지</span>
</nav>
<!-- 4단계 이상 — 중간 항목 축약 -->
<nav class="flex items-center flex-wrap gap-1.5 text-xs" aria-label="이동 경로">
<a href="/">최초 메뉴</a><span class="sep">/</span>
<span class="ellipsis" aria-hidden="true">…</span><span class="sep">/</span>
<a href="/prev">이전 페이지</a><span class="sep">/</span>
<span class="current" aria-current="page">현재 페이지</span>
</nav>
Tab
같은 컨텍스트 안에서 콘텐츠 패널을 전환하는 탭입니다. 페이지 이동이 아니라 화면 내부 전환에 사용합니다.
<div role="tablist" aria-label="상태 필터">
<button role="tab" aria-selected="true">전체</button>
<button role="tab" aria-selected="false">활성</button>
<button role="tab" aria-selected="false">보관됨</button>
</div>
<div role="tabpanel">…</div>
<div class="flex flex-wrap gap-3" role="tablist">
<button class="flex items-center gap-2 py-2.5 px-4 text-sm font-semibold text-rose-700 bg-white border-[1.5px] border-stone-300 rounded-[10px] cursor-pointer" role="tab" aria-selected="true">
<svg>…</svg>
<span>tab label</span>
<span class="tci-badge">99</span>
</button>
<button class="flex items-center gap-2 py-2.5 px-4 text-sm font-semibold text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-[10px] cursor-pointer" role="tab" aria-selected="false">
<svg>…</svg>
<span>tab label</span>
<span class="tci-badge">99</span>
</button>
</div>
<div class="tab-card-group" role="tablist">
<button class="tab-card-item square active" role="tab" aria-selected="true">
<svg>…</svg>
<span>tab label</span>
<span class="tci-badge">99</span>
</button>
</div>
<div class="flex bg-white border border-stone-300 rounded-lg overflow-hidden" role="tablist">
<button class="flex-1 py-3 px-5 text-sm font-semibold text-center text-slate-500 bg-transparent border-0 border-b-[3px] border-transparent border-r border-stone-300 cursor-pointer" role="tab" aria-selected="false">Home</button>
<button class="flex-1 py-3 px-5 text-sm font-semibold text-center text-rose-700 border-0 border-b-[3px] border-transparent border-r border-stone-300 cursor-pointer" role="tab" aria-selected="true">Products</button>
<button class="flex-1 py-3 px-5 text-sm font-semibold text-center text-slate-500 bg-transparent border-0 border-b-[3px] border-transparent border-r border-stone-300 cursor-pointer" role="tab" aria-selected="false">Contact</button>
</div>
PillTabs
알약형 배경 하이라이트로 선택 상태를 표현하는 가벼운 탭입니다.
<div class="inline-flex gap-0.5 p-1 bg-stone-50 border border-stone-300 rounded-full" role="tablist">
<button class="py-1.5 px-4 text-xs font-semibold text-slate-800 bg-white border-0 rounded-full shadow-[0_1px_2px_rgba(0,0,0,0.08)] cursor-pointer" role="tab" aria-selected="true">일간</button>
<button class="py-1.5 px-4 text-xs font-semibold text-slate-500 bg-transparent border-0 rounded-full cursor-pointer" role="tab" aria-selected="false">주간</button>
<button class="py-1.5 px-4 text-xs font-semibold text-slate-500 bg-transparent border-0 rounded-full cursor-pointer" role="tab" aria-selected="false">월간</button>
</div>
<div class="pill-tabs square" role="tablist">
<button class="pill-tab active" role="tab" aria-selected="true">일간</button>
<button class="pill-tab" role="tab" aria-selected="false">주간</button>
<button class="pill-tab" role="tab" aria-selected="false">월간</button>
</div>
<button class="py-1.5 px-4 text-xs font-semibold text-slate-500 bg-transparent border-0 rounded-full cursor-pointer">기본</button>
<button class="py-1.5 px-4 text-xs font-semibold text-slate-800 border-0 rounded-full cursor-pointer">호버</button>
<button class="py-1.5 px-4 text-xs font-semibold text-slate-800 bg-slate-200 border-0 rounded-full cursor-pointer">눌림</button>
TabAdd
탭을 열고 닫을 수 있는 동적 탭 그룹입니다. 여러 조회·문서를 동시에 다루는 워크스페이스 화면에 사용합니다.
<div class="flex items-center gap-0.5 border-b border-stone-300" role="tablist">
<div class="flex items-center gap-2 pt-2 pr-2 pb-2 pl-3.5 mb-[-1px] text-xs font-semibold text-rose-700 border-b-[2px] border-transparent" role="tab" aria-selected="true">
쿼리 1 <span class="flex items-center justify-center w-4 h-4 text-xs text-slate-500 rounded cursor-pointer" role="button" aria-label="쿼리 1 탭 닫기">✕</span>
</div>
<div class="flex items-center gap-2 pt-2 pr-2 pb-2 pl-3.5 mb-[-1px] text-xs font-semibold text-slate-500 border-b-[2px] border-transparent" role="tab" aria-selected="false">쿼리 2 …</div>
<button class="flex items-center justify-center w-7 h-7 text-slate-500 rounded-md cursor-pointer" aria-label="새 탭 추가">+</button>
</div>
Stepper
다단계 절차의 진행 상태를 보여주는 인디케이터입니다.
<nav class="flex items-center w-full" aria-label="설정 진행 단계">
<div class="flex items-center gap-2 done"><div class="flex items-center justify-center w-[26px] h-[26px] text-xs font-bold text-slate-500 border-[1.5px] border-stone-300 rounded-full">✓</div>계정 정보</div>
<div class="flex-1 h-px min-w-6 my-0 mx-2.5 bg-stone-300"></div>
<div class="flex items-center gap-2 active" aria-current="step"><div class="flex items-center justify-center w-[26px] h-[26px] text-xs font-bold text-slate-500 border-[1.5px] border-stone-300 rounded-full">2</div>권한 설정</div>
<div class="flex-1 h-px min-w-6 my-0 mx-2.5 bg-stone-300"></div>
<div class="flex items-center gap-2"><div class="flex items-center justify-center w-[26px] h-[26px] text-xs font-bold text-slate-500 border-[1.5px] border-stone-300 rounded-full">3</div>완료</div>
</nav>
Pagination
목록 데이터를 페이지 단위로 나눠 이동하는 컨트롤입니다. Table §14에서 이미 다루는 페이지네이션 원칙과 동일합니다.
<nav class="flex items-center gap-1" aria-label="페이지네이션">
<button class="h-8 min-w-8 py-0 px-2 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-label="이전 페이지">‹</button>
<button class="h-8 min-w-8 py-0 px-2 text-xs text-white bg-rose-700 border border-stone-300 rounded-none cursor-pointer" aria-current="page">1</button>
<button class="h-8 min-w-8 py-0 px-2 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer">2</button>
<button class="h-8 min-w-8 py-0 px-2 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer">3</button>
<span class="py-0 px-1 text-xs text-slate-500">…</span>
<button class="h-8 min-w-8 py-0 px-2 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer">10</button>
<button class="h-8 min-w-8 py-0 px-2 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-label="다음 페이지">›</button>
</nav>
<nav class="flex items-center gap-2.5" aria-label="페이지네이션">
<button class="flex items-center justify-center w-8 h-8 text-slate-500 bg-white border border-stone-300 rounded-none cursor-pointer" aria-label="이전 페이지" disabled><svg>…</svg></button>
<span class="flex items-center justify-center w-8 h-8 text-xs font-medium text-white bg-rose-700 rounded-none" aria-current="page">1</span>
<span class="text-sm font-semibold text-slate-800">Of</span>
<span class="flex items-center justify-center w-8 h-8 text-xs font-medium text-rose-700 border border-rose-700 rounded-none">24</span>
<button class="flex items-center justify-center w-8 h-8 text-rose-700 bg-white border border-stone-300 rounded-none cursor-pointer" aria-label="다음 페이지"><svg>…</svg></button>
</nav>
DateInput
달력 팝업(CalendarSurface)과 함께 사용하는 날짜 전용 입력 필드입니다.
<div class="relative w-full">
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none has-trailing" type="text" placeholder="YYYY-MM-DD" readonly>
<span class="flex absolute top-[50%] right-3 text-slate-500"><svg>…</svg></span>
</div>
DateRangeInput
시작일과 종료일을 함께 선택하는 두 개의 DateInput 조합입니다.
<div class="flex flex-col gap-[7px]">
<label class="text-xs font-semibold text-slate-800">조회 기간</label>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" placeholder="시작일" aria-label="시작일">
<span>~</span>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" type="text" placeholder="종료일" aria-label="종료일">
</div>
Search
검색 아이콘과 지우기 버튼을 갖춘 전용 입력 필드입니다.
<div class="relative w-full">
<span class="flex absolute top-[50%] left-3 text-slate-500"><svg>…</svg></span>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none has-leading has-trailing" type="search" aria-label="검색">
<button class="flex items-center justify-center absolute top-[50%] right-2.5 w-[18px] h-[18px] text-xs text-slate-500 bg-transparent border-0 rounded-full cursor-pointer" aria-label="검색어 지우기">✕</button>
</div>
Select
미리 정의된 옵션 목록에서 하나를 선택하는 필드입니다.
<label class="text-xs font-semibold text-slate-800" for="priority">우선순위</label>
<select class="w-full h-10 pt-0 pr-9 pb-0 pl-3.5 text-sm text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" id="priority">
<option>낮음</option>
<option selected>보통</option>
<option>높음</option>
</select>
<!-- 닫힘 (placeholder) -->
<div class="flex items-center justify-between gap-2 w-full h-11 py-0 px-3.5 text-sm text-slate-500 bg-white border border-stone-300 rounded-none cursor-pointer" role="combobox" aria-expanded="false">
예) 한국 금융 부문 <svg class="combo-chevron">…</svg>
</div>
<!-- 열림 -->
<div class="flex items-center justify-between gap-2 w-full h-11 py-0 px-3.5 text-sm text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" role="combobox" aria-expanded="true">
KR, JP, US <svg class="combo-chevron">…</svg>
</div>
<div class="mt-1 bg-white border border-stone-300 rounded-none shadow-[0_8px_20px_rgba(0,0,0,0.08)] overflow-hidden" role="listbox">
<div class="flex items-center gap-3 py-3 px-4 text-sm border-b border-stone-300 cursor-pointer" role="option"><span class="opt-code">US</span><span class="opt-desc">US (21)</span></div>
<div class="flex items-center gap-3 py-3 px-4 text-sm bg-stone-50 border-b border-stone-300 cursor-pointer" role="option" aria-selected="true"><span class="opt-code">US</span><span class="opt-desc">US (21)</span></div>
</div>
<!-- 닫힘 (선택값) -->
<div class="flex items-center justify-between gap-2 w-full h-11 py-0 px-3.5 text-sm text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" role="combobox" aria-expanded="false">
US <svg class="combo-chevron">…</svg>
</div>
Textarea
여러 줄 텍스트 입력을 위한 필드입니다. Input의 variant가 아니라 별도 컴포넌트로 다룹니다.
<label class="text-xs font-semibold text-slate-800" for="memo">메모</label>
<textarea class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" id="memo" rows="3" aria-describedby="memo-help"></textarea>
<span class="text-xs text-slate-500" id="memo-help">최대 500자까지 입력할 수 있습니다.</span>
FieldLabel
모든 입력 컴포넌트가 공유하는 레이블 요소입니다. 필수 표시, 보조 텍스트를 함께 다룰 수 있습니다.
<label class="text-xs font-semibold text-slate-800" for="email">이메일<span class="ml-[3px] text-red-600" aria-hidden="true">*</span></label>
<input id="email" aria-required="true">
Field Item
FieldLabel, 입력 컨트롤, Helper/Error 텍스트를 하나의 단위로 묶는 조합 패턴입니다.
<div class="flex flex-col gap-[7px]">
<label class="text-xs font-semibold text-slate-800" for="email">이메일<span class="ml-[3px] text-red-600">*</span></label>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" id="email" aria-invalid="true" aria-describedby="email-err">
<span class="text-xs text-red-600" id="email-err">올바른 이메일 형식이 아닙니다.</span>
</div>
Slider
정해진 범위 안에서 드래그로 값을 조절하는 컨트롤입니다.
<input type="range" min="0" max="100" value="60" aria-label="민감도">
<output>60 / 100</output>
<div class="slider-track square">
<div class="slider-fill" style="width:60%"></div>
<div class="slider-thumb" style="left:60%" role="slider" aria-valuemin="0" aria-valuemax="100" aria-valuenow="60"></div>
</div>
CalendarSurface
월 단위 날짜를 그리드로 보여주는 달력 표면입니다. DateInput·DateRangeInput의 팝업 콘텐츠로 사용됩니다.
<div class="grid gap-0.5 w-[252px] grid-cols-7" role="grid" aria-label="2026년 7월">
<div class="py-1 px-0 text-[11px] font-bold text-center text-slate-500">일</div> …
<div class="flex items-center justify-center text-xs text-slate-800 rounded-md" role="gridcell" aria-selected="true">15</div>
</div>
<div class="grid gap-0.5 w-[252px] grid-cols-7" role="grid" aria-label="2026년 7월">
<div class="py-1 px-0 text-[11px] font-bold text-center text-slate-500">일</div> …
<div class="cal-cell square" role="gridcell" aria-selected="true">15</div>
</div>
<div class="w-[fit-content] p-6 text-white bg-slate-800 rounded-lg">
<div class="flex items-center justify-between flex-wrap gap-4 mb-5">
<div class="text-sm font-bold">기간 설정</div>
<div class="flex flex-wrap gap-2">
<button class="py-2 px-3.5 text-xs text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">오늘</button>
<button class="py-2 px-3.5 text-xs text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">7일</button>
<button class="py-2 px-3.5 text-xs text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">30일</button>
<button class="py-2 px-3.5 text-xs text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">60일</button>
<button class="py-2 px-3.5 text-xs text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">90일</button>
<button class="py-2 px-3.5 text-xs text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">커스텀</button>
</div>
</div>
<div class="flex flex-wrap gap-8">
<div>
<div class="flex items-center gap-1.5 mb-4">
<button class="flex items-center justify-center w-[26px] h-[26px] text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer" aria-label="이전 달">‹</button>
<button class="flex items-center gap-1 py-1.5 px-2.5 text-sm text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">2026년…</button>
<button class="flex items-center gap-1 py-1.5 px-2.5 text-sm text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer">2월…</button>
<button class="flex items-center justify-center w-[26px] h-[26px] text-white bg-transparent border border-[rgba(255,255,255,0.15)] rounded-none cursor-pointer" aria-label="다음 달">›</button>
</div>
<div class="grid grid-cols-[repeat(7,_32px)]" role="grid" aria-label="2026년 2월">
<div class="pb-2 text-xs font-bold text-center text-[rgba(255,255,255,0.4)]">일</div> …
<div class="flex items-center justify-center h-8 text-xs font-bold text-white">16</div> …
</div>
</div>
<!-- 두 번째 달(3월)도 동일 구조로 반복 -->
</div>
<div class="flex justify-end gap-2.5 mt-6">
<button class="h-[38px] py-0 px-[18px] text-xs font-semibold text-white bg-transparent border border-[rgba(255,255,255,0.25)] rounded-none cursor-pointer">취소</button>
<button class="h-[38px] py-0 px-[18px] text-xs font-bold text-white bg-red-600 border-0 rounded-none cursor-pointer">확인</button>
</div>
</div>
<!-- 각 달 그리드 아래에 시간 입력을 추가 -->
<div class="flex items-center gap-1.5 mt-[18px]">
<input class="w-10 h-[34px] text-sm font-bold text-center text-white bg-transparent border border-[rgba(255,255,255,0.2)] rounded-none" value="15" aria-label="시">
<span class="text-[rgba(255,255,255,0.4)]">:</span>
<input class="w-10 h-[34px] text-sm text-center text-white bg-transparent border border-[rgba(255,255,255,0.2)] rounded-none" value="00" aria-label="분">
<span class="text-[rgba(255,255,255,0.4)]">:</span>
<input class="w-10 h-[34px] text-sm text-center text-white bg-transparent border border-[rgba(255,255,255,0.2)] rounded-none" value="00" aria-label="초">
</div>
<div class="flex gap-1.5 mt-1"><span>시</span><span>분</span><span>초</span></div>
<button class="inline-block mt-2 text-xs underline text-red-600 bg-transparent border-0 cursor-pointer" type="button">현재 시각으로 설정</button>
<!-- light 클래스만 추가하면 라이트 배경으로 전환된다 -->
<div class="w-[fit-content] p-6 text-slate-800 bg-white border border-stone-300 rounded-lg">
<div class="flex items-center justify-between flex-wrap gap-4 mb-5">…</div>
<div class="flex flex-wrap gap-8">…</div>
<div class="flex justify-end gap-2.5 mt-6">…</div>
</div>
Checkbox
독립적인 on/off 값을 선택하는 컨트롤입니다.
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer">
<input type="checkbox" checked>
<span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded"><svg>…</svg></span>
이용약관에 동의합니다
</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer">
<input type="checkbox" checked>
<span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded-none"><svg>…</svg></span>
이용약관에 동의합니다
</label>
Radio
여러 옵션 중 하나만 배타적으로 선택하는 컨트롤의 개별 항목입니다.
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer">
<input type="radio" name="priority" value="medium" checked>
<span class="flex items-center justify-center w-[18px] h-[18px] bg-white border-[1.5px] border-stone-300 rounded-full"></span>
보통
</label>
Switch
클릭 즉시 적용되는 on/off 설정 컨트롤입니다.
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer">
<input type="checkbox" role="switch" checked>
<span class="relative w-9 h-5 bg-stone-300 rounded-full"><span class="absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow-[0_1px_2px_rgba(0,0,0,0.2)]"></span></span>
이메일 알림
</label>
Checkbox Group
관련된 여러 옵션을 동시에 선택할 수 있게 체크박스를 묶은 그룹입니다.
<fieldset class="p-0 m-0 border-0">
<legend class="p-0 mb-2.5 text-xs font-bold">알림 유형</legend>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="checkbox" checked><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded">…</span>보안 경고</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="checkbox" checked><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded">…</span>시스템 공지</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="checkbox"><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded"></span>마케팅 소식</label>
</fieldset>
<fieldset class="p-0 m-0 border-0">
<legend class="p-0 mb-2.5 text-xs font-bold">알림 유형</legend>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="checkbox" checked><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded-none">…</span>보안 경고</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="checkbox" checked><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded-none">…</span>시스템 공지</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="checkbox"><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded-none"></span>마케팅 소식</label>
</fieldset>
Radio Group
정확히 하나의 옵션만 선택할 수 있게 Radio를 묶은 그룹입니다.
<fieldset class="p-0 m-0 border-0">
<legend class="p-0 mb-2.5 text-xs font-bold">우선순위</legend>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="radio" name="priority" value="low"><span class="flex items-center justify-center w-[18px] h-[18px] bg-white border-[1.5px] border-stone-300 rounded-full"></span>낮음</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="radio" name="priority" value="medium" checked><span class="flex items-center justify-center w-[18px] h-[18px] bg-white border-[1.5px] border-stone-300 rounded-full"></span>보통</label>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer"><input type="radio" name="priority" value="high"><span class="flex items-center justify-center w-[18px] h-[18px] bg-white border-[1.5px] border-stone-300 rounded-full"></span>높음</label>
</fieldset>
Switch Group
여러 개의 독립적인 설정을 각각 즉시 전환할 수 있게 Switch를 묶은 목록입니다.
<div class="flex items-start justify-between gap-4">
<span>이메일 알림</span>
<label class="inline-flex items-center gap-2 relative text-sm text-slate-800 cursor-pointer">
<input type="checkbox" role="switch" checked aria-label="이메일 알림">
<span class="relative w-9 h-5 bg-stone-300 rounded-full"><span class="absolute top-0.5 left-0.5 w-4 h-4 bg-white rounded-full shadow-[0_1px_2px_rgba(0,0,0,0.2)]"></span></span>
</label>
</div>
DropdownList
트리거를 눌러 목록을 펼치고 하나의 옵션을 선택하는 드롭다운입니다.
<button class="inline-flex items-center gap-1.5 h-9 py-0 px-3 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-haspopup="listbox" aria-expanded="true">보통</button>
<div class="w-[220px] p-1.5 bg-white border border-stone-300 rounded-lg shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]" role="listbox">
<div class="flex items-center gap-2 py-2 px-2.5 text-xs rounded-md cursor-pointer" role="option">낮음</div>
<div class="flex items-center gap-2 py-2 px-2.5 text-xs font-semibold text-rose-700 bg-[rgba(172,59,70,0.08)] rounded-md cursor-pointer" role="option" aria-selected="true">보통</div>
</div>
<button class="inline-flex items-center gap-1.5 h-9 py-0 px-3 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-haspopup="listbox" aria-expanded="true">보통</button>
<div class="w-[220px] p-1.5 bg-white border border-stone-300 rounded-none shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]" role="listbox">
<div class="flex items-center gap-2 py-2 px-2.5 text-xs rounded-md cursor-pointer" role="option">낮음</div>
<div class="flex items-center gap-2 py-2 px-2.5 text-xs font-semibold text-rose-700 bg-[rgba(172,59,70,0.08)] rounded-md cursor-pointer" role="option" aria-selected="true">보통</div>
</div>
DropdownCheckboxList
여러 항목을 동시에 선택할 수 있는 드롭다운입니다. 선택해도 패널이 닫히지 않습니다.
<button class="inline-flex items-center gap-1.5 h-9 py-0 px-3 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-haspopup="menu" aria-expanded="true">상태 (2)</button>
<div role="menu">
<div role="menuitemcheckbox" aria-checked="true">Active</div>
<div role="menuitemcheckbox" aria-checked="true">조사 중</div>
<div role="menuitemcheckbox" aria-checked="false">해결됨</div>
</div>
<button class="inline-flex items-center gap-1.5 h-9 py-0 px-3 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-haspopup="menu" aria-expanded="true">상태 (2)</button>
<div class="w-[220px] p-1.5 bg-white border border-stone-300 rounded-none shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]" role="menu">
<div role="menuitemcheckbox" aria-checked="true"><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded-none">…</span>Active</div>
<div role="menuitemcheckbox" aria-checked="false"><span class="flex items-center justify-center w-[18px] h-[18px] text-white bg-white border-[1.5px] border-stone-300 rounded-none"></span>해결됨</div>
</div>
DropdownRadioboxList
하나의 항목만 선택할 수 있고, 선택 즉시 패널이 닫히는 드롭다운입니다.
<button class="inline-flex items-center gap-1.5 h-9 py-0 px-3 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-haspopup="menu">최신순</button>
<div role="menu">
<div role="menuitemradio" aria-checked="true">최신순</div>
<div role="menuitemradio" aria-checked="false">오래된순</div>
</div>
<button class="inline-flex items-center gap-1.5 h-9 py-0 px-3 text-xs text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer" aria-haspopup="menu">최신순</button>
<div class="w-[220px] p-1.5 bg-white border border-stone-300 rounded-none shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]" role="menu">
<div role="menuitemradio" aria-checked="true">최신순</div>
<div role="menuitemradio" aria-checked="false">오래된순</div>
</div>
List
소량의 데이터를 세로로 나열하는 범용 목록입니다. 밀도 높은 대량 데이터는 Table을 사용합니다.
<ul class="flex flex-col w-full">
<li class="flex items-center gap-3 py-2.5 px-1 border-b border-slate-200">
<span class="flex items-center justify-center w-8 h-8 text-xs font-bold text-slate-800 bg-slate-200 rounded-full">JK</span>
<div class="row-main"><span class="row-title">김지훈</span><span class="row-sub">보안 운영팀</span></div>
<span class="row-meta">2분 전</span>
</li>
</ul>
<ul class="flex flex-col w-full">
<li class="flex items-center gap-3 py-2.5 px-1 border-b border-slate-200">
<span class="flex items-center justify-center w-8 h-8 text-xs font-bold text-slate-800 bg-slate-200 rounded-none">JK</span>
<div class="row-main"><span class="row-title">김지훈</span><span class="row-sub">보안 운영팀</span></div>
<span class="row-meta">2분 전</span>
</li>
</ul>
Tree Panel Composition
부모-자식 관계가 있는 계층 데이터를 펼치고 접으며 탐색하는 패널입니다.
<ul role="tree" aria-label="자산 그룹">
<li role="treeitem" aria-expanded="true">자산 그룹
<ul role="group">
<li role="treeitem" aria-expanded="true" aria-selected="true">웹 서버
<ul role="group"><li role="treeitem">web-01</li></ul>
</li>
</ul>
</li>
</ul>
<ul role="tree" aria-label="자산 그룹">
<li role="treeitem" class="square" aria-expanded="true">자산 그룹
<ul role="group">
<li role="treeitem" class="square" aria-expanded="true" aria-selected="true">웹 서버
<ul role="group"><li role="treeitem" class="square">web-01</li></ul>
</li>
</ul>
</li>
</ul>
Filter & Multi Sort
적용된 필터를 칩으로, 다중 정렬 기준을 우선순위 번호로 함께 보여주는 툴바입니다.
<div class="flex items-center flex-wrap gap-2" role="group" aria-label="필터 및 정렬">
<span class="inline-flex items-center gap-1.5 py-[5px] px-2.5 text-xs bg-white border border-stone-300 rounded-full">심각도: Critical, High
<button aria-label="심각도 필터 제거">✕</button>
</span>
<button class="inline-flex items-center gap-1.5 py-[5px] px-2.5 text-xs bg-white border border-stone-300 rounded-md cursor-pointer" aria-label="탐지 수, 1순위 내림차순 정렬">
<span class="sort-num">1</span>탐지 수 ↓
</button>
</div>
<div class="flex items-center flex-wrap gap-2" role="group" aria-label="필터 및 정렬">
<span class="inline-flex items-center gap-1.5 py-[5px] px-2.5 text-xs bg-white border border-stone-300 rounded-none">심각도: Critical, High
<button aria-label="심각도 필터 제거">✕</button>
</span>
<button class="inline-flex items-center gap-1.5 py-[5px] px-2.5 text-xs bg-white border border-stone-300 rounded-none cursor-pointer" aria-label="탐지 수, 1순위 내림차순 정렬">
<span class="sort-num">1</span>탐지 수 ↓
</button>
</div>
InlineAlert
본문 흐름 안에 배치되는 상태 배너입니다. Toast와 달리 스스로 사라지지 않고 자리를 차지합니다.
<div class="flex gap-2.5 py-3 px-3.5 text-xs leading-relaxed text-slate-800 bg-[rgba(34,197,94,0.08)] rounded-md" role="status">
<span class="ia-icon" aria-hidden="true">✓</span>변경 사항이 저장되었습니다.
</div>
<div class="flex gap-2.5 py-3 px-3.5 text-xs leading-relaxed text-slate-800 bg-[rgba(208,15,55,0.08)] rounded-md" role="alert">
<span class="ia-icon" aria-hidden="true">✕</span>파일 업로드에 실패했습니다.
</div>
<div class="inline-alert success square" role="status">
<span class="ia-icon" aria-hidden="true">✓</span>변경 사항이 저장되었습니다.
</div>
<div class="inline-alert error square" role="alert">
<span class="ia-icon" aria-hidden="true">✕</span>파일 업로드에 실패했습니다.
</div>
InlineMessage
필드나 요소 바로 아래에 붙는 가벼운 보조 텍스트입니다. InlineAlert보다 시각적으로 조용합니다.
<span class="flex items-start gap-1.5 text-xs leading-normal text-slate-500">
<span class="im-icon" aria-hidden="true">ℹ</span>8자 이상, 숫자와 특수문자를 포함해야 합니다.
</span>
Tooltip
요소에 마우스를 올리거나 포커스했을 때 나타나는 짧은 보조 설명입니다.
<button aria-label="정보" aria-describedby="tt1">…</button>
<span class="absolute bottom-[calc(100%_+_8px)] left-[50%] py-1.5 px-2.5 text-xs whitespace-nowrap text-white bg-slate-800 rounded-md shadow-[0_4px_12px_rgba(0,0,0,0.15)]" id="tt1" role="tooltip">
CVE-2026-1234에 대한 상세 설명
</span>
<button aria-label="정보" aria-describedby="tt1">…</button>
<span class="tooltip-bubble square" id="tt1" role="tooltip">
CVE-2026-1234에 대한 상세 설명
</span>
Tag
항목을 분류하는 제거 가능한 레이블입니다. Badge와 달리 클릭으로 제거할 수 있습니다.
<span class="inline-flex items-center gap-1.5 py-[5px] px-2.5 text-xs bg-white border border-stone-300 rounded-full">
인프라
<button class="chip-remove" aria-label="인프라 태그 제거">✕</button>
</span>
<span class="filter-chip square">
인프라
<button class="chip-remove" aria-label="인프라 태그 제거">✕</button>
</span>
StatusDot
작은 색상 점으로 실시간 상태(온라인/대기/오프라인/오류)를 표시합니다.
<span class="inline-flex items-center gap-1.5 text-xs">
<span class="inline-block w-2 h-2 bg-green-700 rounded-full" aria-hidden="true"></span>온라인
</span>
PriorityIndicator
신호 막대 형태로 작업이나 티켓의 우선순위 단계를 표시합니다.
<span class="inline-flex items-center gap-2 text-xs">
<span class="inline-flex items-end gap-0.5 h-3.5 level-3" aria-hidden="true">…</span>높음
</span>
ProgressBar
완료까지 시간이 걸리는 작업의 진행 정도를 선형 바로 표시합니다.
<div class="w-full h-2 bg-slate-200 rounded-full overflow-hidden">
<div class="h-full bg-rose-700 rounded-full" style="width:68%"
role="progressbar" aria-valuenow="68" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<span class="mt-1.5 text-xs text-slate-500">스캔 중… 68%</span>
<div class="progress-track square">
<div class="progress-fill" style="width:68%" role="progressbar" aria-valuenow="68" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<span class="progress-label">스캔 중… 68%</span>
Divider
관련된 콘텐츠 그룹 사이의 약한 경계를 표시하는 선입니다.
<hr class="w-full h-px bg-stone-300">
<div class="flex items-center gap-3 w-full text-xs text-slate-500" role="separator">또는</div>
Popover
트리거 근처에 뜨는 비모달 오버레이로, 버튼 등 상호작용 요소를 담을 수 있습니다.
<div class="w-[240px] p-3.5 bg-white border border-stone-300 rounded-lg shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]" role="dialog" aria-labelledby="pop-title">
<div class="mb-1.5 text-xs font-bold" id="pop-title">알림 규칙 삭제</div>
<div class="mb-3 text-xs leading-relaxed text-slate-500">이 규칙과 연결된 모든 알림이 함께 삭제됩니다.</div>
<div class="flex justify-end gap-2">
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">취소</button>
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer">삭제</button>
</div>
</div>
<div class="w-[240px] p-3.5 bg-white border border-stone-300 rounded-none shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]" role="dialog" aria-labelledby="pop-title">
<div class="mb-1.5 text-xs font-bold" id="pop-title">알림 규칙 삭제</div>
<div class="mb-3 text-xs leading-relaxed text-slate-500">이 규칙과 연결된 모든 알림이 함께 삭제됩니다.</div>
<div class="flex justify-end gap-2">
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">취소</button>
<button class="inline-flex items-center justify-center gap-1.5 h-8 py-0 px-3 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer">삭제</button>
</div>
</div>
Disclosure
제목을 클릭하면 숨겨진 콘텐츠가 펼쳐지는 단일 확장 패턴입니다.
<button class="flex items-center justify-between w-full py-3 px-3.5 text-sm font-semibold text-slate-800 bg-transparent border-0 cursor-pointer" aria-expanded="true" aria-controls="disc1">
IOC 디팽(defang)이란 무엇인가요?
<span class="chev" aria-hidden="true">▾</span>
</button>
<div class="pt-0 px-3.5 pb-3.5 pt-3 text-xs leading-relaxed text-slate-500 border-t border-stone-300" id="disc1">…</div>
<div class="w-full border border-stone-300 rounded-none overflow-hidden">
<button class="flex items-center justify-between w-full py-3 px-3.5 text-sm font-semibold text-slate-800 bg-transparent border-0 cursor-pointer" aria-expanded="true" aria-controls="disc1">
IOC 디팽(defang)이란 무엇인가요?
<span class="chev" aria-hidden="true">▾</span>
</button>
<div class="pt-0 px-3.5 pb-3.5 pt-3 text-xs leading-relaxed text-slate-500 border-t border-stone-300" id="disc1">…</div>
</div>
Skeleton
실제 콘텐츠가 로드되기 전, 그 형태를 미리 보여주는 펄스 애니메이션 자리표시자입니다.
<div class="w-10 h-10 bg-slate-200 rounded-full" aria-hidden="true"></div>
<div class="h-[9px] bg-slate-200 rounded-[3px]" style="width:60%"></div>
<div class="h-[9px] bg-slate-200 rounded-[3px]" style="width:40%"></div>
<div class="w-10 h-10 bg-slate-200 rounded-none" aria-hidden="true"></div>
<div class="h-[9px] bg-slate-200 rounded-none" style="width:60%"></div>
<div class="h-[9px] bg-slate-200 rounded-none" style="width:40%"></div>
Surface Foundation
카드형 컴포넌트가 공통으로 사용하는 배경·테두리·그림자의 기본 레시피입니다.
<div class="bg-stone-50">…</div>
<div class="bg-white border border-stone-300">…</div>
<div class="bg-white shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)]">…</div>
<div class="bg-stone-50 square">…</div>
<div class="bg-white border border-stone-300 square">…</div>
<div class="bg-white shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)] square">…</div>
Widget Container
대시보드에서 독립된 데이터 블록(차트, 통계 등)을 담는 컨테이너입니다.
<section class="w-full bg-white border border-stone-300 rounded-lg" aria-labelledby="w-title">
<div class="flex items-center justify-between py-3.5 px-4 border-b border-stone-300">
<h3 class="text-xs font-bold" id="w-title">최근 24시간 탐지</h3>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 pl-0.5 pr-0.5 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="위젯 옵션">⋯</button>
</div>
<div class="flex items-center justify-center py-5 px-4 text-xs text-slate-500">…</div>
</section>
<section class="w-full bg-white border border-stone-300 rounded-none" aria-labelledby="w-title">
<div class="flex items-center justify-between py-3.5 px-4 border-b border-stone-300">
<h3 class="text-xs font-bold" id="w-title">최근 24시간 탐지</h3>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 pl-0.5 pr-0.5 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="위젯 옵션">⋯</button>
</div>
<div class="flex items-center justify-center py-5 px-4 text-xs text-slate-500">…</div>
</section>
Section Container
eyebrow, 제목, 설명, 콘텐츠로 페이지를 논리적 구획으로 나누는 컨테이너입니다.
<section aria-labelledby="sec-title">
<div class="flex items-center gap-2 mb-2.5 text-xs font-bold uppercase tracking-widest text-rose-700">Components</div>
<h2 class="mb-2 text-[27px] font-bold tracking-tighter leading-tight" id="sec-title">섹션 제목</h2>
<p class="max-w-[520px] mb-6 text-sm text-slate-500">섹션에 대한 한 줄 설명입니다.</p>
…
</section>
Drawer Container
화면 가장자리에서 밀려나오는 패널로, 목록 맥락을 유지하며 상세를 보여줄 때 사용합니다.
<div class="relative h-[200px] bg-stone-50 border border-stone-300 rounded-lg shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)] overflow-hidden">
<div class="absolute inset-0 p-4 flex flex-col gap-4">
<div class="flex items-center gap-2.5">
<span class="w-[22px] h-[22px] rounded-md bg-rose-700"></span>
<span class="h-2.5 rounded flex-1 bg-slate-500"></span>
</div> …
</div>
<div class="absolute inset-0 bg-[rgba(44,51,61,0.55)]"></div>
<div class="absolute top-0 bottom-0 right-0 w-[58%] p-4 bg-white border-l border-stone-300 rounded-l-lg shadow-[-8px_0_24px_rgba(44,51,61,0.08)]" role="dialog" aria-labelledby="dr-title">
<div class="flex items-center justify-between mb-2.5">
<h3 id="dr-title">IOC 상세</h3>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 pl-0.5 pr-0.5 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">✕</button>
</div>
…
</div>
</div>
<div class="relative h-[200px] bg-stone-50 border border-stone-300 rounded-none shadow-[0_8px_24px_rgba(44,51,61,0.12),_0_2px_6px_rgba(44,51,61,0.06)] overflow-hidden">
<div class="absolute inset-0 p-4 flex flex-col gap-4">
<div class="flex items-center gap-2.5">
<span class="w-[22px] h-[22px] rounded-md bg-rose-700"></span>
<span class="h-2.5 rounded flex-1 bg-slate-500"></span>
</div> …
</div>
<div class="absolute inset-0 bg-[rgba(44,51,61,0.55)]"></div>
<div class="absolute top-0 bottom-0 right-0 w-[58%] p-4 bg-white border-l border-stone-300 shadow-[-8px_0_24px_rgba(44,51,61,0.08)]" role="dialog" aria-labelledby="dr-title">
<div class="flex items-center justify-between mb-2.5">
<h3 id="dr-title">IOC 상세</h3>
<button class="inline-flex items-center justify-center gap-1.5 w-8 h-8 py-0 px-3 pl-0.5 pr-0.5 text-xs font-semibold no-underline tracking-tight whitespace-nowrap text-rose-700 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">✕</button>
</div>
…
</div>
</div>
Table Cell
Table의 기본 셀입니다. 텍스트는 좌측, 숫자는 우측 정렬하고 긴 값은 말줄임 처리합니다.
<td title="이 값은 셀보다 길어서 말줄임 처리된 전체 텍스트입니다">이 값은 셀보다 길어서 말줄임…</td>
<td style="text-align:right">1,204</td>
Data Table Cell
IOC, 심각도처럼 데이터 성격에 맞춘 전용 서식을 가진 Table 셀 타입입니다. Table §14.4 컬럼 타입 규칙을 구현합니다.
<td class="text-xs">
<span class="inline-block max-w-[170px] whitespace-nowrap overflow-hidden">185.220.101[.]47</span>
<button class="py-0.5 px-[5px] text-[11px] text-slate-500 bg-transparent border-0 rounded-[3px] cursor-pointer opacity-0" aria-label="복사">⎘</button>
</td>
Sheet Table Cell
더블클릭이나 Enter로 즉시 편집 모드에 들어가는 스프레드시트형 셀입니다.
<td class="py-2 px-2.5 text-xs border border-transparent rounded" role="gridcell" tabindex="0">읽기 상태</td>
<td class="py-2 px-2.5 text-xs bg-white border border-transparent rounded shadow-[0_0_0_2px_rgba(172,59,70,0.12)]" role="gridcell">
<input type="text" value="편집 중인 값" aria-label="셀 값 편집">
</td>
Blankslate
데이터가 없는 화면을 위한 기본 빈 상태 컴포넌트입니다. Empty State 패턴과 Table §14.5의 기반입니다.
<div class="flex flex-col items-center justify-center gap-2 p-6 text-center bg-white border border-slate-200 rounded-none" role="status">
<div class="text-2xl leading-none opacity-28" aria-hidden="true">◎</div>
<div class="mt-1 text-xs font-semibold text-slate-800">탐지된 지표가 없습니다</div>
<div class="text-xs text-slate-500">현재 필터 조건과 일치하는 IOC가 없습니다.</div>
<button class="py-[5px] px-3.5 mt-1.5 text-xs font-semibold text-slate-800 bg-white border border-stone-300 rounded-none cursor-pointer">필터 초기화</button>
</div>
Icon
Lucide(lucide.dev) 아이콘이 실제 화면 컨텍스트에서 어떻게 보이는지 확인하는 시안 페이지입니다. 크기 스케일과 전체 라이브러리는 Foundations > Icon을 참고하세요.
<!-- color는 부모에서 currentColor로 상속됩니다 -->
<div style="color:#FFFFFF;"><i data-lucide="shield"></i></div> <!-- 다크모드 -->
<div style="color:#2C333D;"><i data-lucide="shield"></i></div> <!-- 라이트모드 -->
<div style="color:var(--c-point);"><i data-lucide="shield"></i></div> <!-- 포인트 컬러 -->
Bar Chart
항목별 값을 세로 막대로 비교합니다.
Grouped Bar Chart
여러 그룹(심각도)을 기간별로 나란히 비교합니다.
Radar Chart
여러 항목을 다차원으로 동시에 비교합니다.
Histogram
데이터 구간별 빈도를 보여줍니다.
Box Plot
중앙값, 사분위수, 이상치를 함께 보여줍니다.
Violin Plot
히스토그램과 박스플롯을 결합한 형태로 분포의 밀도를 보여줍니다.
Scatter Plot
두 변수 간 상관관계를 보여줍니다.
Bubble Chart
산점도에 세 번째 변수를 크기로 추가합니다. Top 5 / Top 10으로 항목 수를 조절할 수 있습니다.
Heatmap
날짜별 활동량을 색 농도로 보여줍니다. 1개월/1년 두 주기로 볼 수 있고, 색은 포인트 컬러 한 계열 안에서 농도만 다르게 씁니다.
Pie / Donut Chart
전체 대비 비율을 보여줍니다(범주가 적을 때). Top 5 / Top 10으로 항목 수를 조절할 수 있습니다.
Stacked Bar Chart
시간/항목별 구성 변화를 100% 기준으로 보여줍니다.
Treemap
계층적 비율 구조를 면적으로 보여줍니다.
Line Chart
시계열 추세를 보여줍니다.
Area Chart
누적된 시계열 추세를 채워진 영역으로 보여줍니다.
Candlestick
기간별 등락(시가·고가·저가·종가)을 보여줍니다. 상승은 밝은 외곽선, 하락은 채워진 막대로 색 외에도 형태로 구분합니다.
Workflow
처리 흐름을 노드와 연결선으로 보여주는 다이어그램입니다. 영역을 드래그해 이동하고, 확대·축소하고, 노드를 클릭하면 상세 모달을 엽니다. 노드 추가·수정·삭제는 이 컴포넌트의 범위 밖입니다.
<div class="relative w-full h-[480px] bg-stone-50 border border-stone-300 rounded-none overflow-hidden cursor-grab" onmousedown="wfPanStart(event)">
<div class="flex items-center gap-0.5 absolute top-3 right-3 bg-white border border-stone-300 rounded-none shadow-[0_2px_8px_rgba(0,0,0,0.08)]">
<button onclick="wfZoom('out')">−</button>
<button onclick="wfZoom('in')">+</button>
</div>
<div class="absolute top-0 left-0 w-[900px] h-[600px]" id="wf-canvas">
<svg class="absolute top-0 left-0 w-full h-full overflow-visible" id="wf-edges"></svg> <!-- paths drawn from node rects -->
<div class="absolute w-[240px] py-3 px-3.5 bg-white border border-stone-300 rounded-none shadow-[0_1px_3px_rgba(0,0,0,0.06)] cursor-pointer" tabindex="0" role="button" onclick="wfOpenNode('start')">
<div class="text-xs font-bold text-slate-800">시작</div>
</div>
</div>
</div>
디자인 토큰
코드와 Figma 간 단일 진실 공급원(Single Source of Truth)입니다.
| Token | Value | Description |
|---|---|---|
| color-brand-primary | #AC3B46 | 주요 브랜드 컬러 |
| color-brand-point | #D00F37 | 강조 / CTA |
| color-brand-secondary | #2C333D | 배경, 텍스트 다크 |
| color-sub-cool | #E2E8F0 | 배경, 구분선 (쿨) |
| color-sub-warm | #F2CC8F | 강조 배경 (웜) |
| color-state-success | #22C55E | 성공, 완료 |
| color-state-warning | #F59E0B | 주의, 경고 |
| color-state-error | #D00F37 | 오류 |
| color-state-info | #3B82F6 | 정보 안내 |
| Token | Value | Usage |
|---|---|---|
| round-none | 0px | 테두리 없음, 표 셀, 인라인 코드 등 각진 요소 |
| round-regular | 4px | 인풋, 버튼 sm, 태그, 칩 |
| round-medium | 6px | 버튼 md, 드롭다운, 툴팁 |
| round-large | 8px | 버튼 lg, 카드 내부 요소 |
| round-x-large | 12px | 카드, 모달, 패널 등 대형 컨테이너 |
| Token | Value | Usage |
|---|---|---|
| font-point-ko | Paperlogy, serif | 국문 포인트 |
| font-text-ko | Pretendard, sans-serif | 국문 본문 |
| font-point-en | Ivy Mode, serif | 영문 포인트 |
| font-text-en | Red Hat Display, sans-serif | 영문 본문 |
| text-display | 56px / 1.1 / 700 | 히어로 타이틀 |
| text-h1 | 40px / 1.2 / 700 | 페이지 제목 |
| text-h2 | 32px / 1.25 / 700 | 섹션 제목 |
| text-body-sm | 14px / 1.5 / 400 | 기본 본문 |
| text-caption | 12px / 1.4 / 400 | 캡션 |
데이터 시각화
차트·그래프에 사용하는 4가지 컬러 팔레트입니다. 데이터의 역할(범주 / 크기 / 발산 / 상태)을 먼저 정한 뒤 팔레트를 선택하세요.
패턴 & 템플릿
여러 화면에서 반복적으로 등장하는 UI 조합, 화면 구성, 그리고 행동 기준선입니다.
화면 전체의 레이아웃과 구성 요소 조합을 정의하는 패턴입니다. 각 카드는 필요한 컴포넌트와 적용 기준을 함께 보여줍니다.
특정 화면이 아니라, 반복되는 사용자 행동이 어떤 상태를 반드시 거쳐야 하는지에 대한 기준입니다.
<div class="w-10 h-10 bg-slate-200 rounded-full" aria-hidden="true"></div>
<div class="h-[9px] bg-slate-200 rounded-[3px]" style="width:60%"></div>
<div class="h-[9px] bg-slate-200 rounded-[3px]" style="width:40%"></div>
<div class="w-7 h-7 border-[3px] border-slate-200 rounded-full" role="status" aria-label="로딩 중"></div>
<div class="flex flex-col items-center justify-center gap-2 p-6 text-center bg-white border border-slate-200 rounded-none" role="status">
<div class="text-2xl leading-none opacity-28" aria-hidden="true">⚠</div>
<div class="mt-1 text-xs font-semibold text-slate-800">데이터를 불러올 수 없습니다</div>
<div class="text-xs text-slate-500">서버 응답 오류 (503). 잠시 후 다시 시도하세요.</div>
<button class="py-[5px] px-3.5 mt-1.5 text-xs font-semibold text-red-600 bg-[rgba(208,15,55,0.05)] border border-stone-300 rounded-none cursor-pointer">다시 시도</button>
</div>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-red-600 bg-transparent border-[1.5px] border-red-600 rounded-none cursor-pointer" onclick="openModal('modal-delete')">삭제 확인 모달 열기</button>
<div class="hidden items-center justify-center fixed bg-[rgba(44,51,61,0.55)]" id="modal-delete">
<div class="w-[460px] max-w-[calc(100vw_-_32px)] bg-white rounded-[16px] shadow-[0_24px_64px_rgba(44,51,61,0.22)] overflow-hidden">
<div class="flex items-start justify-between gap-3 pt-[22px] px-[22px] pb-[18px] border-b border-rose-700">
<div class="text-[19px] font-bold tracking-tighter">정말 삭제하시겠어요?</div>
<button class="flex items-center justify-center w-8 h-8 text-base text-slate-500 bg-transparent border-0 rounded-none cursor-pointer" aria-label="닫기">✕</button>
</div>
<div class="py-5 px-[22px] text-sm leading-relaxed text-slate-500">이 작업은 되돌릴 수 없습니다. …</div>
<div class="flex justify-end gap-2 py-4 px-[22px] border-t border-slate-200">
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-slate-800 bg-transparent border-[1.5px] border-stone-300 rounded-none cursor-pointer">취소</button>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap border-0 rounded-none cursor-pointer">삭제하기</button>
</div>
</div>
</div>
<div class="flex flex-col gap-[7px]">
<label class="text-xs font-semibold text-slate-800" for="email">이메일<span class="ml-[3px] text-red-600">*</span></label>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none" id="email" aria-invalid="true" aria-describedby="email-err">
<span class="text-xs text-red-600" id="email-err">올바른 이메일 형식이 아닙니다.</span>
<button class="inline-flex items-center justify-center gap-1.5 h-10 py-0 px-4 text-sm font-semibold no-underline tracking-tight whitespace-nowrap text-white bg-rose-700 border-0 rounded-none cursor-pointer">제출</button>
</div>
<div class="relative w-full">
<span class="flex absolute top-[50%] left-3 text-slate-500"><svg>…</svg></span>
<input class="w-full h-10 py-0 px-3.5 text-sm text-slate-800 bg-white border-[1.5px] border-stone-300 rounded-none has-leading has-trailing" type="search" aria-label="검색">
<button class="flex items-center justify-center absolute top-[50%] right-2.5 w-[18px] h-[18px] text-xs text-slate-500 bg-transparent border-0 rounded-full cursor-pointer">✕</button>
</div>
<ul class="flex flex-col w-full">
<li class="flex items-center gap-3 py-2.5 px-1 border-b border-slate-200">
<div class="row-main"><span class="row-title">방화벽 정책 #1024</span><span class="row-sub">네트워크 보안</span></div>
</li>
</ul>
<div class="flex flex-col items-center justify-center gap-2 p-6 text-center bg-white border border-slate-200 rounded-none" role="status">
<div class="text-2xl leading-none opacity-28" aria-hidden="true">◎</div>
<div class="mt-1 text-xs font-semibold text-slate-800">검색 결과가 없습니다</div>
<div class="text-xs text-slate-500">다른 검색어로 다시 시도해보세요.</div>
</div>
접근성
WCAG 2.1 AA 기준을 최소 요건으로 합니다.
| 텍스트 유형 | 최소 대비율 |
|---|---|
| 일반 텍스트 (18px 미만) | 4.5 : 1 |
| 큰 텍스트 (18px 이상 / 굵은 14px 이상) | 3 : 1 |
| UI 컴포넌트 / 그래픽 | 3 : 1 |
반응형 해상도 기준
Mobile, Tablet, Web 각 화면에서 디자인·개발 시 기준으로 삼는 대표 해상도입니다.
| 구분 | 기준 해상도 | 대응 브레이크포인트 | 비고 |
|---|---|---|---|
| Mobile | 375 × 812px | sm (480px ~ 767px) | iPhone 기준, 세로 모드 |
| Tablet | 768 × 1024px | md (768px ~ 1023px) | iPad 기준, 세로 모드 |
| Web | 1440 × 900px | xl (1280px ~ 1535px) | 데스크탑 기준, 콘텐츠 최대 너비(1440px)와 동일 |
기여 가이드
새 컴포넌트를 추가하거나 기존 항목을 변경할 때 따르는 절차입니다.
| 대상 | 규칙 | 예시 |
|---|---|---|
| 토큰 | kebab-case, 역할-속성 순 | color-brand-primary |
| 컴포넌트 | PascalCase | ButtonPrimary |
| Variant | camelCase | sizeLg, stateError |
전체
이 사이트의 원본이 된 design.md 전문입니다.
레이아웃
개요, 색상, 타이포그래피, 간격, Round, 그리드 & 레이아웃, 아이콘, 디자인 토큰, 데이터 시각화.
컴포넌트
컴포넌트, 테이블, 배지 문서 원문.
가이드라인
패턴 & 템플릿, 접근성, 기여 가이드 문서 원문.