Components
ScrollArea
스크롤 가능한 영역의 인디케이터 패턴과 스크롤바 스타일 규칙.
Platform: Web · Desktop only (React Native는 native ScrollView 사용).
스크롤 영역은 단순히 overflow를 자르는 것 이상입니다. 사용자가 더 많은 내용이 있음을 인지하게 하는 방법을 맥락에 따라 선택해야 합니다.
Mobile
ScrollArea 컴포넌트는 모바일에 사용하지 않는다. RN의 native ScrollView / FlatList / SectionList를 직접 사용한다. 본 문서의 인디케이터 패턴(fade·gradient mask 등)은 web 전용 시각 장치이며, 모바일은 native 스크롤 동작 자체가 인디케이터 역할을 한다.
- 컴포넌트 선택:
- 짧은 정적 리스트:
ScrollView - 동적/긴 리스트:
FlatList(virtualization) - 섹션 그룹화:
SectionList - 고성능 대량 리스트:
FlashList(@shopify/flash-list)
- 짧은 정적 리스트:
- scrollbar 표시: 기본 native 인디케이터 사용.
showsVerticalScrollIndicator={true}기본값 유지. 커스텀 scrollbar 그리지 말 것 - bouncing: iOS의 over-scroll bounce는 사용자가 끝에 도달했음을 알리는 신호 — 비활성화하지 말 것 (
bounces={true}기본 유지) - momentum scroll: native 기본값 유지. JS로 가속도 조작하지 말 것
- fade indicator: 콘텐츠가 잘리는 가장자리에 fade mask가 필요하면
react-native-linear-gradient로 상/하단 mask layer 별도 배치 - Pull-to-refresh: 리스트 상단 새로고침은
RefreshControl사용. 색상은brown-800(tintColor) - BottomSheet 내부 스크롤: TrueSheet의
scrollableprop을 사용하면 시트 자체가 스크롤 가능. 추가 ScrollView 중첩 시 nestedScrollEnabled 주의 — Dialog Mobile 참조
스크롤 인디케이터 패턴
Tiro에서 사용하는 인디케이터는 두 가지입니다.
| 패턴 | 언제 |
|---|---|
| Scrollbar | 리스트, 패널처럼 사용자가 스크롤을 인지하고 직접 조작하는 영역 |
| Scroll Fog | 더 있다는 힌트만 주면 되는 카드, 프리뷰, 드롭다운, 인라인 콘텐츠 |
1. Scrollbar
언제 사용
- 노트 리스트, 트랜스크립트 뷰어, 설정 패널 등 사용자가 직접 스크롤을 조작하는 주요 영역
- 스크롤 가능 여부를 명확히 알려야 할 때
- 콘텐츠가 많아 사용자가 스크롤을 예상하는 맥락
Token Mapping
| Token | Value |
|---|---|
| track 너비 | 8px (w-2) |
| thumb 시각 너비 | 6px (p-[1px] 내부) |
| thumb 색상 (기본) | stone-200 |
| thumb 색상 (hover) | stone-300 |
| thumb radius | rounded-full |
| track | transparent |
| auto-hide | 스크롤 멈춤 1500ms 후 |
플랫폼별 처리
| 환경 | 동작 |
|---|---|
| macOS | 스크롤 시에만 overlay 방식으로 나타남 — layout shift 없음 |
| Windows | 항상 표시. scrollbar-gutter: stable 적용해 layout shift 방지 |
2. Scroll Fog
콘텐츠가 경계 너머로 이어짐을 그라디언트 페이드로 암시합니다. 스크롤바 없이 조용하게 "더 있음"을 전달합니다.
언제 사용
- 카드 내부 미리보기, 인라인 콘텐츠처럼 암시만 필요할 때
- 드롭다운, 셀렉트 목록처럼 스크롤바가 시각적으로 방해될 때
- 사용자가 스크롤보다 클릭으로 상세 화면으로 이동하는 프리뷰 영역
표시 정책 — 항상 표시
Scroll Fog는 스크롤 위치와 무관하게 항상 표시합니다. 스크롤 상태에 따라 on/off하면 flicker가 발생하고 불필요한 복잡도가 생깁니다.
콘텐츠가 실제로 넘치지 않는 경우에도 fog를 표시해도 괜찮습니다. 그라디언트가 워낙 조용하기 때문에 사용자가 인지하지 못합니다.
크기 스펙
| 위치 | 높이 | 비고 |
|---|---|---|
| Bottom | 80px | 기본. 콘텐츠가 많을수록 효과적 |
| Top | 20px | 위로 스크롤됐을 때 적용 |
| Left / Right | 20px | 수평 스크롤 시 |
최소 20px, 영역 전체 높이의 15–20%를 넘지 않는 범위에서 조정합니다.
배경색 매칭
Fog 색상은 컨테이너 배경색과 반드시 일치해야 합니다. 배경이 달라지면 fog도 바꿉니다.
| 배경 | Fog |
|---|---|
white | from-white |
stone-50 (#FAFAF9) | from-[#FAFAF9] |
stone-100 (#F5F5F4) | from-[#F5F5F4] |
방향 결정
| 상황 | 적용 |
|---|---|
| 초기 상태 (아래에 더 있음) | bottom fog |
| 위로 스크롤된 상태 | top fog |
| 중간 스크롤 | top + bottom 동시 |
패턴 선택 기준
스크롤 영역인가?
│
├─ 사용자가 직접 조작 (리스트, 패널, 트랜스크립트)?
│ └─ Scrollbar
│
└─ 힌트만 필요 (카드 프리뷰, 드롭다운, 인라인)?
└─ Scroll Fog (스크롤바 숨김)Usage
{/* Scrollbar — 리스트/패널 */}
<ScrollArea className="h-80">
<NoteList />
</ScrollArea>
{/* Scroll Fog — 드롭다운 / 카드 프리뷰 */}
<div className="relative h-[180px] overflow-hidden rounded-lg">
<div className="h-full overflow-y-auto [&::-webkit-scrollbar]:hidden">
<OptionList />
</div>
{/* 항상 표시 — 스크롤 상태로 조건부 렌더링 하지 않음 */}
<div className="pointer-events-none absolute bottom-0 inset-x-0 h-20 bg-gradient-to-t from-white to-transparent" />
</div>Do / Don't
- Do: Scroll Fog는 항상 표시 — 스크롤 위치에 따라 on/off 하지 않음
- Do: Fog 색상을 컨테이너 배경색과 정확히 맞출 것
- Do: Windows 환경에
scrollbar-gutter: stable적용 - Don't: Chevron(▼) 등 명시적 스크롤 인디케이터 추가 금지 — Scroll Fog로 충분
- Don't: 같은 영역에 Scrollbar + Scroll Fog 동시 사용 금지
- Don't: 페이지 전체 스크롤을 ScrollArea 내부로 중첩 금지
- Don't: 스크롤이 불필요한 작은 영역에 남용 금지
