# 스크롤 패널 (고정 헤더·툴바 + 목록)

| 항목 | 경로 |
|------|------|
| 데모 | `scroll-panel.html` |
| CSS | `css/scroll-panel.css` |
| JS | 없음 |

관련: [layout.md](./layout.md) · 실서비스 예: 빌라랩 매물목록(`.map-listing`)

---

## 원칙

| 항목 | 권장 |
|------|------|
| 구조 | 고정 `head` / `toolbar` + `overflow-y: auto` 목록 |
| 너비 고정 | `scrollbar-gutter: stable` — 스크롤 생겨도 아이템 폭이 안 흔들림 |
| 여백 조절 | **`--scroll-panel-pad-x`만** 수정 (기본 `1.6rem`) |
| 스크롤바 두께 | `--scroll-panel-sb` · `::-webkit-scrollbar { width }` 와 **동일 값** |
| JS | **금지** — 스크롤 유무로 `padding` / 클래스를 토글하지 않음 |
| 구분선 | 헤더·툴바·아이템이 같은 콘텐츠 열에서 끝나게 (`calc(pad + sb)` 정렬) |
| 키보드 | 목록에 포커스 가능한 항목이 있으면 Tab으로 진입 · 휠/화살표로 스크롤 |
| 이름 | 패널에 `aria-label` 또는 제목과 연결된 `aria-labelledby` |

---

## 왜 이렇게 하나

스크롤바가 생기면 뷰포트가 줄어 **목록만** 좁아지고, 위쪽 고정 헤더는 그대로라  
구분선·버튼 정렬이 어긋납니다. JS로 `padding-right`를 바꾸는 방식은 깜빡임·유지보수 부담이 큽니다.

| CSS | 역할 |
|-----|------|
| `scrollbar-gutter: stable` | 스크롤 없어도 스크롤바 자리를 미리 확보 → 목록 너비 일정 |
| `--scroll-panel-pad-x` | 콘텐츠 좌우 여백 (디자인 간격) |
| `--scroll-panel-sb` | thin 스크롤바 폭 = gutter 폭 |
| `calc(pad + sb)` | 고정 헤더·툴바를 목록 콘텐츠 열과 맞춤 |

스크롤 **유무와 상관없이** 좌우 간격이 같게 유지됩니다.

---

## 변수

```css
.scroll-panel {
  --scroll-panel-pad-x: 1.6rem; /* ← 여백 넓히거나 줄일 때 이것만 */
  --scroll-panel-sb: 0.5rem;    /* 스크롤바 두께 바꿀 때만 함께 수정 */
}
```

| 바꾸고 싶은 것 | 수정할 변수 |
|----------------|-------------|
| 좌우 공백 | `--scroll-panel-pad-x` |
| 스크롤바(얇게/굵게) | `--scroll-panel-sb` (+ webkit `width`는 변수 참조) |

연산은 반드시 `calc()`:

```css
/* ❌ 잘못된 예 */
padding: 1.2rem var(--scroll-panel-pad-x + var(--scroll-panel-sb));

/* ✅ 올바른 예 */
padding: 1.2rem calc(var(--scroll-panel-pad-x) + var(--scroll-panel-sb));
```

---

## 마크업

```html
<aside
  class="scroll-panel"
  style="height: 40rem; width: 36rem;"
  aria-labelledby="scrollPanelTit"
>
  <div class="scroll-panel__head">
    <h2 id="scrollPanelTit" class="scroll-panel__tit">목록</h2>
    <button type="button" aria-label="목록 닫기">닫기</button>
  </div>

  <div class="scroll-panel__toolbar">
    <!-- 정렬·필터 등 -->
    <p>총 <em>28</em>건</p>
  </div>

  <ul class="scroll-panel__list">
    <li class="scroll-panel__item">항목 1</li>
    <li class="scroll-panel__item">항목 2</li>
    <!-- 스크롤 확인용으로 충분히 -->
  </ul>
</aside>
```

- 패널 높이: 부모 flex/`height`로 잡고, 목록만 `flex: 1; min-height: 0; overflow-y: auto`
- 닫기 버튼은 아이콘만이면 `aria-label` 필수 · 텍스트가 보이면 중복 라벨 주의

---

## CSS 핵심

```css
.scroll-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  --scroll-panel-pad-x: 1.6rem;
  --scroll-panel-sb: 0.5rem;
}

.scroll-panel__head,
.scroll-panel__toolbar {
  flex: 0 0 auto;
  padding-left: calc(var(--scroll-panel-pad-x) + var(--scroll-panel-sb));
  padding-right: calc(var(--scroll-panel-pad-x) + var(--scroll-panel-sb));
}

.scroll-panel__list {
  flex: 1 1 auto;
  min-height: 0;
  padding: 0 var(--scroll-panel-pad-x) 0
    calc(var(--scroll-panel-pad-x) + var(--scroll-panel-sb));
  overflow-y: auto;
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: #c5c5c5 transparent;
}

.scroll-panel__list::-webkit-scrollbar {
  width: var(--scroll-panel-sb);
}
```

전체·썸 스타일: `css/scroll-panel.css`

---

## 접근성 체크

| 항목 | 확인 |
|------|------|
| 패널 이름 | `aria-label` / `aria-labelledby` |
| 스크롤 | 마우스 없이 키보드·스크린리더로 목록 탐색 가능 |
| 포커스 | 스크롤 영역 밖(헤더 버튼)과 안(링크·찜 등) Tab 순서 자연스러움 |
| 모션 | 스크롤바 색·두께만 · 깜빡이는 인디케이터 지양 |
| 확대 | 200% 확대해도 목록이 잘리지 않고 패널 내부 스크롤로 읽기 |

---

## 하지 말 것

- 스크롤 생길 때 / 없을 때 `padding-right`를 JS로 바꿈
- 헤더만 `margin-right`로 밀어 좌우 불균형 만들기 (변수·`calc`로 맞출 것)
- `overflow: scroll`로 빈 스크롤바를 항상 강제 (OS·오버레이 스크롤과 충돌)
- `user-scalable=no` 로 확대 막기

---

## 복사 패키지

```
css/scroll-panel.css
마크업: scroll-panel.html
→ 상세: docs/scroll-panel.md
```

프로젝트 스킨 색은 `kit-skin` / 토큰으로 `--scroll-panel` 구분선·글자색만 덮어쓰면 됩니다.
