# 레이아웃 (페이지 셸)

| 항목 | 경로 |
|------|------|
| 데모 | `layout.html` |
| CSS | `css/layout.css` (+ 토큰: `tokens.css` · 스킵: `common.css`) |
| JS | `common.js` (TOP · 화면 줌 · 모달 시 `#wrap` aria-hidden) |

관련: [head.md](./head.md) · [tokens.md](./tokens.md) · [menu.md](./menu.md) · [breadcrumb.md](./breadcrumb.md) · [footer.md](./footer.md)

---

## 원칙

| 항목 | 권장 |
|------|------|
| 뼈대 | 스킵(`body` 첫 요소) > `#wrap` > `#header` > `#container` > `#footer` > `#quick` |
| 헤더 순서 | 유틸(`.header-util`) → 로고 → 주메뉴(`#gnb`) |
| 퀵메뉴 | `#quick` fixed · 스크롤 시 `#footer` 상단에서 멈춤 (`common.js`) |
| 스킵 | `href` 대상에 실제 `id` · 스타일은 `common.css` |
| 폭 | `--layout-max: 120rem` · 좌우 `--layout-pad-x` |
| 현재 위치 | 브레드크럼 `aria-current="page"` · GNB `.on` / `aria-current` |
| 좌메뉴 | `aside` + `aria-label` · 현재 링크 `aria-current="page"` |
| 모달 | `#wrap` 유지 (JS가 `aria-hidden` 토글) |
| 화면 줌 | 헤더 `.header-util` · 하한 **90%** · 상한 **200%** · 단계 **10%** (`--page-zoom`) |
| 색 | `css/tokens.css` — `--ccolor`는 KRDS primary-50 |

---

## 헤더 유틸

유틸 그룹은 헤더 상단 오른쪽입니다. 아이콘과 텍스트를 함께 둡니다. 항목이 많아지면 드롭다운을 검토하세요.

```html
<div class="header-util">
  <div class="header-inner">
    <nav class="header-util-nav" aria-label="유틸리티">
      <ul class="header-util-list">
        <li>
          <a href="./login.html">
            <img src="./img/util-login.svg" alt="">로그인
          </a>
        </li>
        <li>
          <a href="./search.html">
            <img src="./img/util-search.svg" alt="">통합검색
          </a>
        </li>
      </ul>
    </nav>
    <!-- 선택: .zoom-ctrl 글자 크기 -->
  </div>
</div>
```

---

## 로고

`img/logo.svg` · 대체 텍스트는 **사이트명만** (`alt="웹접근성 빌드업"`). 「로고」·「홈」은 넣지 않습니다.

```html
<p class="logo">
  <a href="./index.html"><img src="./img/logo.svg" alt="웹접근성 빌드업" width="236" height="48"></a>
</p>
```

현재 페이지가 홈이면 링크에 `aria-current="page"`를 둡니다.

---

## 화면 확대 / 축소

브라우저 줌(Ctrl + / −)과 **별개**인 사이트 위젯입니다. rem 루트(`html` font-size)에 `--page-zoom`을 곱해 전체 UI를 키웁니다. **배치 권장: `#header` 상단 `.header-util` (오른쪽 정렬).**

| 제한 | 값 | 근거 |
|------|-----|------|
| 하한 | **90%** | 공공·기관 사이트 글자크기 위젯 관례. 과도한 축소로 가독성·터치 영역이 깨지지 않게 함. (브라우저 자체 축소는 막지 않음) |
| 상한 | **200%** | [WCAG 2.1 1.4.4 Resize text](https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html) — 텍스트를 200%까지 확대해도 내용·기능이 유지되어야 함 |
| 단계 | **10%** | 기본 100% · ±10% 버튼. 국내 공공사이트 위젯과 동일한 체감 |
| 저장 | `localStorage` (`pageZoomPercent`) | 새로고침 후에도 유지 |

```html
<div class="header-util">
  <div class="header-inner">
    <div class="zoom-ctrl" role="group" aria-label="화면 확대 축소">
      <span class="zoom-ctrl__label" id="zoomCtrlLabel">글자 크기</span>
      <button type="button" class="zoom-ctrl__btn" data-zoom="out" aria-label="화면 축소">−</button>
      <span class="zoom-ctrl__value" data-zoom-value aria-live="polite" aria-labelledby="zoomCtrlLabel">100%</span>
      <button type="button" class="zoom-ctrl__btn" data-zoom="in" aria-label="화면 확대">+</button>
      <button type="button" class="zoom-ctrl__btn zoom-ctrl__btn--reset" data-zoom="reset">기본</button>
    </div>
  </div>
</div>
```

- CSS: `common.css`의 `html { font-size: calc(…% * var(--page-zoom, 1)); }` + `layout.css` `.header-util` / `.zoom-ctrl`
- JS: `common.js` — `[data-zoom="in|out|reset"]` · 한계에서 버튼 `disabled`
- **하지 말 것:** `user-scalable=no` / `maximum-scale`로 브라우저 확대를 막기. 사이트 줌은 보조일 뿐, 브라우저 200%에서도 레이아웃이 유지되어야 함.

---

## 페이지 셸

```html
<nav id="skipToContent" aria-label="건너뛰기">
  <ul>
    <li><a href="#contents">본문 바로가기</a></li>
    <li><a href="#gnb">주메뉴 바로가기</a></li>
    <li><a href="#footer">하단 바로가기</a></li>
  </ul>
</nav>
<div id="wrap">

  <header id="header">
    <div class="header-util">
      <div class="header-inner">
        <!-- .header-util-nav 로그인·통합검색 · .zoom-ctrl 글자 크기 -->
      </div>
    </div>
    <div class="header-inner">
      <p class="logo"><a href="/"><img src="./img/logo.svg" alt="사이트명"></a></p>
      <nav aria-label="주메뉴">
        <ul id="gnb">…</ul>
      </nav>
    </div>
  </header>

  <div id="container">
    <nav id="location" class="breadcrumb" aria-label="브레드크럼">…</nav>
    <div id="contents">
      <div class="ptitle-head"><h2 class="ptitle">페이지 제목</h2></div>
      <!-- 1단 또는 .layout-body--aside -->
    </div>
  </div>

  <footer id="footer">…</footer>
  <div id="quick">
    <div class="gotop">
      <a href="#header">맨 위로</a>
    </div>
  </div>
</div>
```

### 맨 위로 (`#quick .gotop`)

| 항목 | 권장 |
|------|------|
| 요소 | **`<a>`** (페이지 내 이동) — `button` 불필요 |
| `href` | 실제 상단 `id` (`#header` 권장). `#none` · `javascript:;` 금지 |
| 이름 | 링크 안 한글 **맨 위로** (화면 숨김 · 스크린리더용) |
| 아이콘 | `img/gotop.svg` 배경 (`layout.css`) |
| JS | 부드러운 스크롤 + 대상에 초점 (`common.js`). JS 꺼져도 앵커 이동 |

- `a`인 이유: “페이지 상단으로 이동”은 **링크(탐색)** 이고, 실제 `href`가 있으면 JS 없이도 동작합니다.
- `button`은 폼·토글 등 **동작**에 쓰고, 이동 목적에는 링크가 맞습니다.
- 아이콘만 보여도 DOM에 “맨 위로” 텍스트를 남겨 접근 이름을 확보합니다.

---

## 1단 / 2단(좌메뉴)

```html
<!-- 1단: #contents 안 본문만 -->
<p>본문…</p>

<!-- 2단 -->
<div class="layout-body layout-body--aside">
  <aside class="layout-aside" aria-label="서브메뉴">
    <h3 class="layout-aside__tit">메뉴명</h3>
    <ul class="layout-aside__nav">
      <li><a href="…" aria-current="page">현재 메뉴</a></li>
      <li><a href="…">다른 메뉴</a></li>
    </ul>
  </aside>
  <div class="layout-main">
    <p class="layout-main__lead">소개 문구</p>
    …
  </div>
</div>
```

900px 이하에서는 1열로 쌓입니다. 복잡한 LNB·전체메뉴는 [menu.md](./menu.md)를 사용하세요.

---

## CSS 변수

| 변수 | 기본 | 용도 |
|------|------|------|
| `--layout-max` | `120rem` | 헤더·본문·location 최대 폭 |
| `--layout-pad-x` | `2rem` (모바일 `1.6rem`) | 좌우 여백 |
| `--layout-aside-w` | `22rem` | 좌메뉴 폭 |
| `--layout-gap` | `3.2rem` | 좌메뉴↔본문 간격 |
| `--quick-gap` (`#quick`) | `2rem` (모바일 `1.2rem`) | 퀵메뉴 화면·푸터 간격 |

---

## 접근성 체크

- [ ] Tab 첫 포커스가 스킵 링크로 이동
- [ ] 스킵 `href`와 대상 `id` 일치
- [ ] 로고 대체 텍스트에 「로고」를 넣지 않음
- [ ] 로고·GNB·브레드크럼·좌메뉴 현재 위치 표시
- [ ] 본문 시작이 `#contents`로 명확
- [ ] 모바일에서 GNB 가로 스크롤 시 포커스 가능한 링크 유지
- [ ] 맨 위로: `<a href="#header">맨 위로</a>` + `gotop.svg` · `#none`/`javascript:;` 없음
- [ ] 맨 위로 이동 후 상단(`#header`)으로 초점 이동 (JS 또는 브라우저 기본)
- [ ] 화면 줌: 90%·200%에서 축소/확대 버튼 비활성 · `aria-live`로 비율 안내
- [ ] viewport에 `user-scalable=no` / `maximum-scale` 없음
- [ ] 브라우저 200% 확대에서도 본문·기능 사용 가능 (사이트 줌과 별개 검증)

---

## 복사 패키지

```
css/tokens.css
css/layout.css
css/common.css          ← tokens import · #skipToContent · 기본 타이포
img/logo.svg            ← 헤더 로고 (alt는 사이트명, 「로고」 넣지 않음)
img/util-login.svg
img/util-search.svg
img/gotop.svg           ← 맨 위로 아이콘
(선택) breadcrumb.css · footer.css · menu.css
마크업: layout.html
→ 상세: docs/layout.md · docs/tokens.md
```
