Iconography
Tiro UI 아이콘 시스템. Lucide를 단일 소스로 사용합니다.
Tiro의 아이콘은 기능을 빠르게 인식시키는 기능적 도구입니다. 장식이 아니라 의미를 전달하고, 텍스트를 보조하며, 인터페이스를 조용하게 만드는 역할을 합니다.
Tiro 디자인 언어의 "조용한 권위"에 맞게, 아이콘도 강하게 주장하지 않습니다. Stroke-width를 Lucide 기본(2)보다 한 단계 낮춰 1.75를 기본으로 사용하는 것이 그 반영입니다.
Library
Lucide — Tiro UI 아이콘의 유일한 소스.
| 브라우저 | lucide.dev/icons |
| Web / Desktop | lucide-react |
| Mobile | lucide-react-native |
| 라이선스 | ISC (사용·수정·재배포 자유) |
Lucide에 없을 때만 Custom SVG를 추가합니다. 다른 라이브러리(Font Awesome, Heroicons, Material Icons)는 금지입니다.
Visual Style
Grid & Size
Lucide 아이콘은 24 × 24px viewBox 기준으로 설계되어 있습니다. UI에서는 크기를 4종으로 제한합니다.
| Size | px | Primary use |
|---|---|---|
sm | 14 × 14 | Dense UI, 인라인 텍스트 옆 |
md | 16 × 16 | Default — 대부분의 UI |
lg | 20 × 20 | 리스트 leading, 강조 아이콘 |
xl | 24 × 24 | AppBar, 툴바, hero 아이콘 |
새 크기 추가는 DS Owner 협의 필요. 현재 4단계로 충분합니다.
Rendering Box
아이콘이 찌그러지거나 서로 다른 크기로 보이는 문제를 막기 위해, slot size와 glyph size를 분리합니다.
- Slot: 레이아웃에서 아이콘이 차지하는 고정 정사각형 영역
- Glyph: slot 중앙에 렌더되는 실제 Lucide 아이콘 크기
아이콘은 SVG 자체로 레이아웃을 잡지 않습니다. 항상 고정된 slot 안에 중앙 정렬합니다.
| Context | Slot | Glyph | Use |
|---|---|---|---|
| Inline text | 16 × 16 | 14 × 14 | 문장·chip·dense label 옆 |
| Menu/List prefix | 24 × 24 | 16 × 16 | menu item, list row leading icon |
| Header action | 32 × 32 | 24 × 24 | app bar, top navigation |
| Icon-only button | component size | size별 glyph | Button 문서의 icon-only size 사용 |
| Media icon container | 40 × 40 | 18–20 × 18–20 | Item media, settings row |
// Web
<span className="grid h-6 w-6 shrink-0 place-items-center text-stone-500">
<Folder className="h-4 w-4" strokeWidth={1.75} aria-hidden />
</span>
// React Native
<View className="h-[24px] w-[24px] shrink-0 items-center justify-center">
<Folder size={16} strokeWidth={1.75} color={color} />
</View>SVG에는 stretch, flex-1, w-full, h-full, aspect-auto를 적용하지 않습니다. 부모가 flex-row일 때도 slot은 반드시 shrink-0 / flex: 0 0 auto여야 합니다.
// Good — slot과 glyph 크기 분리
<span className="grid h-6 w-6 shrink-0 place-items-center">
<Search className="h-4 w-4" strokeWidth={1.75} />
</span>
// Bad — SVG 자체가 flex item 크기를 담당
<Search className="flex-1 w-full h-full" />인라인 SVG를 직접 작성해야 하는 경우에도 viewBox="0 0 24 24"와 실제 width/height 비율은 1:1로 유지합니다.
// Good
<svg width="16" height="16" viewBox="0 0 24 24" />
// Bad — 비율이 달라져 찌그러짐
<svg width="20" height="16" viewBox="0 0 24 24" />Custom Shape Fit
Folder, TeamFolder, logo-like mark처럼 fill이 들어가거나 시각적 무게가 큰 custom SVG는 Lucide보다 크게 보일 수 있습니다. 이 경우 SVG를 늘리거나 납작하게 만들지 말고, 같은 slot 안에서 glyph만 1단계 줄입니다.
// 24px slot에서 heavy custom glyph는 14px로 조정
<span className="grid h-6 w-6 shrink-0 place-items-center">
<TeamFolderIcon size={14} color={thread.color} />
</span>Stroke
| 용도 | strokeWidth |
|---|---|
| Default (Tiro) | 1.75 |
| Decorative (배경/일러스트) | 1.5 |
| 강조 (heavy, 드물게) | 2.0 |
Lucide 기본값은 2.0이지만 Tiro는 1.75로 톤다운합니다. Lucide 아이콘을 그대로 쓰면 strokeWidth={1.75} prop을 명시해야 합니다.
Style — Line only
Tiro는 Line(stroke) 스타일만 사용합니다. Fill 아이콘은 단 하나의 예외를 제외하면 사용하지 않습니다.
| Style | 사용 |
|---|---|
| Line (default) | 모든 UI 아이콘 |
| Fill | 녹음 중 dot (Circle fill-rose-600) 단일 예외 |
Color
아이콘은 항상 currentColor를 상속합니다. 색상 하드코딩은 금지입니다. 부모의 텍스트 색상을 따르므로 disabled, muted, primary 맥락에서 자동으로 적절한 톤을 갖습니다.
// ✅
<Search size={20} strokeWidth={1.75} /> // currentColor 상속
// ❌
<Search size={20} color="#3A2018" /> // 하드코딩 금지Naming Convention
Lucide는 kebab-case URL을 제공합니다. React import 시 PascalCase로 변환합니다.
| Lucide URL | Import |
|---|---|
lucide.dev/icons/search | Search |
lucide.dev/icons/chevron-right | ChevronRight |
lucide.dev/icons/more-horizontal | MoreHorizontal |
lucide.dev/icons/file-audio | FileAudio |
모르면 Lucide 사이트에서 "Copy as React" 버튼 사용.
Usage in UI
Decorative vs. Semantic
| 용도 | 처리 |
|---|---|
| 텍스트 옆 보조 아이콘 | aria-hidden={true} |
| 의미 전달 단독 아이콘 | aria-label 또는 accessibilityLabel 필수 |
// Decorative — 텍스트와 함께
<Search aria-hidden size={16} strokeWidth={1.75} />
<span>검색</span>
// Semantic — 아이콘만
<Trash2 size={16} strokeWidth={1.75} aria-label="삭제" />Interactive Icons
클릭 가능한 아이콘은 반드시 IconButton 컴포넌트로 감쌉니다. 아이콘에 직접 onClick을 붙이지 않습니다. 터치 영역, focus ring, a11y가 IconButton에서 처리됩니다.
Tiro 빈출 아이콘
제품 내 자주 쓰이는 아이콘 레퍼런스. 아이콘명이 헷갈릴 때 참고.
| 용도 | Lucide |
|---|---|
| 검색 | Search |
| 설정 | Settings |
| 더보기 (메뉴) | MoreHorizontal (수평) · MoreVertical (리스트 행) |
| 닫기 | X |
| 뒤로 / 앞으로 | ChevronLeft · ChevronRight |
| 펼치기 / 접기 | ChevronDown · ChevronUp |
| 추가 | Plus |
| 편집 | PencilLine |
| 삭제 | Trash2 |
| 공유 | Share2 |
| 다운로드 / 업로드 | Download · Upload |
| 복사 | Copy |
| 즐겨찾기 | Star |
| 알림 | Bell |
| 사용자 | User · UserPlus · Users |
| 파일 / 노트 | FileText |
| 마이크 | Mic · MicOff |
| 재생 / 일시정지 / 정지 | Play · Pause · Square |
| 녹음 중 dot | Circle (fill-rose-600) |
| 정보 | Info |
| 경고 | AlertTriangle · AlertCircle |
| 성공 | CheckCircle2 |
| 외부 링크 | ExternalLink |
| 언어 / 번역 | Languages |
| 필터 / 정렬 | Filter · SlidersHorizontal · ArrowUpDown |
항상 Lucide 사이트를 최종 레퍼런스로 사용합니다. 이 표는 빠른 참고용입니다.
Custom SVG
Lucide에 없는 글리프가 필요할 때만 추가합니다.
추가 전 체크리스트:
- Lucide에서 유사 아이콘 없는지 재확인 (
lucide.dev/icons/?search=...) - 24 × 24 viewBox, stroke-based,
1.75strokeWidth 기준 준수 -
viewBox비율과 렌더링width/height비율이 동일한지 확인 - slot 없이 SVG 자체가 flex/grid item 크기를 담당하지 않는지 확인
- stroke/fill이 viewBox 가장자리에 닿지 않도록 최소 1–2px optical padding 확보
- Lucide 스타일(라운드 캡/조인)과 시각적으로 일관된지 확인
- DS Owner 리뷰
저장 위치:
| 플랫폼 | 경로 |
|---|---|
| Web / Desktop | packages/tds/icons/*.tsx |
| Mobile | apps/mobile/src/components/icons/*.tsx |
포맷: SVG → React 컴포넌트. Lucide와 동일한 props API(size, strokeWidth, color) 노출.
네이밍: 기능 기반 PascalCase — TiroLogoMark, SpeakerDiarizationIcon.
Installation
# Web / Desktop
npm install lucide-react
# Mobile (React Native)
npm install lucide-react-native
# react-native-svg 피어 디펜던시 필요 (Expo는 내장)Do / Don't
- Do: Lucide에서 먼저 찾기 — 1,500+ 아이콘 대부분 커버됨
- Do:
strokeWidth={1.75}기본 사용 - Do: 색상은
currentColor상속 - Do: slot size와 glyph size를 분리해 고정 정사각형 slot 안에 중앙 정렬
- Do: 클릭 가능한 아이콘은
IconButton으로 감싸기 - Do: 의미 전달 단독 아이콘에
aria-label필수 - Don't: Font Awesome · Heroicons · Material Icons 등 혼용 금지
- Don't: 색상 하드코딩 금지 (
color="#3A2018") - Don't: SVG에
flex-1,w-full h-full, 비정사각width/height를 적용해 늘리기 금지 - Don't: Lucide 아이콘 수정 사용 금지 — 필요하면 Custom SVG로 추가
- Don't: 아이콘 색상만으로 상태 전달 금지 — 텍스트 병행
