레이아웃 시스템
4·8·12컬럼 반응형 그리드, 6단계 브레이크포인트, 컨테이너 규칙, 8px 스페이싱 리듬, 그리고 표준 화면 레이아웃 패턴을 정의합니다.
마지막 업데이트 2026-06-12
레이아웃 원칙#
- 화이트스페이스가 위계를 만든다 — 구분선과 박스보다 여백을 먼저 사용합니다
- 8px 그리드 — 모든 간격은
--wds-space-*토큰의 배수입니다 - 콘텐츠 폭 우선 — 화면이 넓어져도 본문 가독 폭을 유지합니다
- 모바일에서 깨지지 않는다 — 320px이 최소 보장 폭입니다
레이아웃 프리미티브#
문서가 정의한 레이아웃을 재사용 가능한 토큰 백킹 컴포넌트로 제공합니다 — 모두 무상태(RSC 가능)입니다.
<Stack>— flex 1D 적층.gap은--wds-space-*스케일,direction·align·justify·wrap.<Container>— 중앙 정렬 폭 제한.size(content 1200 · prose 760 · full) +padding(거터).<Grid>/<GridItem>— 12컬럼(기본) 그리드.GridItem의span·start로 컬럼 점유.<Split>— 52/48 분할. compact(600px 미만)에서 세로로 적층.
박스는 자리표시용 자식입니다 — 보이는 건 박스가 아니라 그 사이 간격이며, 값은 --wds-space-* 토큰으로 정합니다.
반응형 그리드#
컬럼 수는 Material window size class(ADR-012)와 정합합니다 — compact 4 · medium 8 · expanded 12. 1280px 미만에서는 마진이 고정되고 컨테이너가 유동하며, 1280px부터는 컨테이너가 1200px로 고정되고 마진이 가변합니다.
| 구간 | 폭 | 컬럼 | 거터 | 마진 | 컨테이너 |
|---|---|---|---|---|---|
| compact | 600px 미만 | 4 | 16px | 16px 고정 | 유동 |
| medium | 600–840px | 8 | 24px | 24px 고정 | 유동 |
| expanded | 840px 이상 | 12 | 24px | 24px 고정 | 유동 (1200px까지) |
| 데스크톱 | 1280px 이상 | 12 | 24px | 가변 | 1200px 고정 |
핸들을 직접 끌어 보세요 — 뷰포트가 320↔1920px로 연속 변하며 컬럼이 4 → 8 → 12로
reflow되고 모든 수치가 실시간 갱신됩니다. 아래 실제 적용은 같은 뷰포트로
실물 Grid/GridItem 카드 레이아웃이 재배치되는 미리보기입니다 — 각 카드의
span이 size class별 규칙(stat 3→4→4 · main 8→8→4 · aside 4→8→4)으로 살아서
바뀝니다. size class 판정은 라이브러리 실물(windowSizeClass)을 그대로 사용합니다.
기준 폭별 정적 스펙은 디바이스 탭으로 확인합니다.
위 리사이즈 랩의 적용 미리보기가 실물 Grid/GridItem을 그대로 렌더하며,
각 카드의 span 라벨(예: span 8 = 12칸 중 8칸 점유)이 size class별로 재배치됩니다.
레이아웃 토큰은 SSOT에서 자동 생성된 표로 확인합니다.
| 토큰 | 값 | 설명 |
|---|---|---|
| — | ||
| 문서 본문 가독 폭 — 한글 약 38~42자 (07 레이아웃) | ||
| medium·expanded 거터 | ||
| expanded(≥840) 컬럼 수 | ||
| medium·expanded 고정 마진 — 1280 미만 유동 컨테이너 구간. ≥1280은 마진 가변(컨테이너 1200 고정) | ||
| compact(<600) 컬럼 수 — Material window size class 정합 (ADR-012) | ||
| compact 거터 | ||
| compact 고정 마진 | ||
| medium(600–840) 컬럼 수 | ||
| 비주얼·콘텐츠 | ||
| 폼·액션 |
브레이크포인트#
6단계 기준 폭에서 시각 검증을 수행합니다. 이 포털 셸의 반응형 규칙이
기준 구현(Reference)이며, 경계값은 SSOT(tokens/core/breakpoint.json)에서 파생됩니다.
| 기준 폭 | 구간 | 포털 셸 동작 |
|---|---|---|
| 320px | 모바일 최소 | 단일 컬럼 · 드로어 내비 · 아이콘 검색 |
| 380px | 모바일 | 헤더 검색이 아이콘 → 풀 입력으로 전환 |
| 768px | 태블릿 | 단일 컬럼 유지 · 헤더 풀 구성 |
| 1024px | 데스크톱 시작 | 좌측 사이드바 고정 노출 |
| 1280px | 와이드 데스크톱 | 우측 TOC 노출 · 본문 폭 고정 |
| 1920px | 최대 | 셸 폭 1920px 캡 — 콘텐츠가 무한히 늘어나지 않음 |
미디어 쿼리는 모바일 우선이 아닌 구간 명시 방식을 사용합니다 —
숨김/노출이 일어나는 경계(1024 · 1280)를 코드에서 그대로 읽을 수 있어야 합니다.
셸 CSS의 너비 미디어쿼리 리터럴이 이 SSOT와 어긋나면 build-tokens 드리프트
가드(breakpoint-css-parity.test)가 빌드를 실패시킵니다 — 경계는 브레이크포인트
토큰 한 곳에서만 바뀝니다.
셸 전환점 6개와 적응형 window size class 경계 2개(600 · 840)를 하나의 룰러에서 확인합니다 — 두 체계는 서로 다른 레이어입니다.
컨테이너와 콘텐츠 폭#
| 용도 | 폭 | 근거 |
|---|---|---|
| 마케팅/홈 컨테이너 | --wds-container-max (1200px) | 12컬럼 그리드 기준 폭 |
| 문서 본문(prose) | 760px | 한글 기준 약 38–42자 — 가독 최적 |
| 셸 전체 | 1920px 캡 | 와이드 모니터에서 과확장 방지 |
본문이 760px을 넘는 요소(코드 블록, 넓은 표)는 가로 스크롤 래퍼로 감쌉니다 — 컨테이너를 깨뜨리지 않습니다.
prose · 760px한 줄에 한글 약 38–42자 — 문서 본문의 가독 최적 폭입니다.
스페이싱 리듬#
간격은 의미 단위로 선택합니다 — 요소 내부(1–3) · 요소 사이(4–6) · 블록 사이(8–12) · 섹션 사이(16–32).
4px 베이스 유닛 — 모든 토큰은 4의 배수이며, space-4(16px)가 본문 리듬의 기준 한 칸입니다. 결번(5·7·9 …)은 의도입니다 — 위로 갈수록 성기게 증가해 선택을 단순하게 만듭니다.
원시 토큰 값은 SSOT에서 자동 생성된 표로 확인합니다.
| 토큰 | 값 | 설명 |
|---|---|---|
| 4px | ||
| 8px | ||
| 12px | ||
| 16px | ||
| 20px | ||
| 24px | ||
| 32px | ||
| 40px | ||
| 48px | ||
| 64px | ||
| 80px | ||
| 96px | ||
| 128px |
표준 레이아웃 패턴#
사이드바 레이아웃#
문서·관리 화면의 기본형 — 고정 사이드바(264px) + 유동 콘텐츠. 이 포털이 기준 구현입니다. 1024px 미만에서 사이드바는 드로어로 전환됩니다.
분할 레이아웃 (52 / 48)#
로그인·온보딩 화면의 기본형 — 좌측 비주얼·콘텐츠 52%(--wds-split-left),
우측 폼·액션 48%(--wds-split-right). 기존 로그인 화면이 이 비율의
정본이며, P4 Patterns의 Login 패턴으로 재구현됩니다.
디바이스 탭으로 각 뷰포트에서 52/48 비율·px·거터와 compact 적층을 측정값으로 확인합니다 — 위 반응형 그리드 스펙과 같은 블루프린트 방식입니다.
비주얼·콘텐츠 · 52%
폼·액션 · 48%
대시보드 그리드#
12컬럼 위에 카드 위젯을 배치하는 패턴 — 상세 규칙과 라이브 데모는 P4 패턴 섹션에서 정의됩니다.
구성과 정렬#
나란히 놓인 표면은 폭만 같아서는 정렬된 것이 아닙니다. 바깥 박스가 같은 높이여도
내부 콘텐츠 블록이 서로 다른 높이에서 시작·종료하면 시선이 흔들리고, 짧은 카드에
죽은 여백이 생깁니다. 같은 행의 형제(카드·패널·셀)는 바깥 높이뿐 아니라
앵커 지점·내부 리듬·정보 밀도까지 정렬되어야 합니다. 기준 구현은 플레이그라운드
셸(playground.module.css의 .stage)로, grid-template-columns: minmax(0, 1fr) minmax(16rem, 18rem)에
align-items를 두지 않아 기본 stretch(동일 높이)를 얻습니다 — 이 관습을 규칙으로 고정합니다.
동일 높이#
- 규칙 — 한 행에 나란히 놓인 패널·카드는 같은 높이를 가집니다 — Grid/Flex 기본
stretch에 맡기고,align-items: start로 높이를 끊지 않습니다. - 근거 — 들쭉날쭉한 바닥선은 "정렬되지 않음"으로 읽힙니다. 같은 높이는 형제가 같은 위계임을 시각적으로 선언합니다.
- 기법 — 그리드 컨테이너에
align-items를 명시하지 않습니다(기본값stretch). 유일한 예외는 내재 크기가 의미인 칩·뱃지·태그 행으로, 이때만align-items: center로 콘텐츠 크기를 보존합니다. - 내부 박스까지 동일 크기 — 동일 높이는 바깥 카드에서 끝나지 않습니다. 형제 카드 안의 눈에 띄는 내부 박스(명세 박스·데이터 패널·내부 카드)도 콘텐츠 양(행 수 등)이 달라 크기가 갈리면 통일성이 깨집니다. 박스를 콘텐츠에 맞춰 줄이지 말고, 그 박스 자체에
flex: 1을 주어 짧은 쪽을 늘려 형제와 같은 크기로 맞춥니다 —flex: 1은 가변 텍스트가 아니라 크기를 맞출 박스에 둡니다(짧은 카드는 박스 안 아래 여백이 생기는 게 정답). - 별도 셀의 하위 블록 — 형제의 하위 영역이 각자 다른 그리드 셀에 있으면(예: 카드마다의 컬러 씬) 묶을 공유 행이 없어
stretch가 닿지 않습니다. 이때는 가장 큰 콘텐츠에 맞춘 공유min-height바닥값으로 높이를 맞추되, 더 큰 콘텐츠가 추가되면 바닥값을 재보정해야 하는 제약을 코드에 명시합니다.
앵커 정렬#
- 규칙 — 형제 카드의 길이가 가변일 때, 구조적 앵커(메타데이터 푸터·레시피 라인·기본 CTA)는 카드 간 같은 선에서 정렬됩니다.
- 근거 — 같은 높이여도 앵커가 카드 중간에 떠 있으면 비교가 깨집니다. 사용자는 "이 줄은 모든 카드에서 같은 위치"라는 암묵 계약을 읽습니다.
- 기법 — 카드를 flex 세로 컬럼으로 만들고, 가변 영역(설명문)에
flex: 1을 주어 빈 공간을 흡수시킵니다. 고정 앵커는 자연스럽게 카드 하단에 핀되어 형제 간 같은 선에 정렬됩니다. 전체 메타 박스에만flex: 1을 주면 내부 블록이 위로 적층돼 설명 길이차로 푸터가 카드마다 다른 높이에 뜹니다. - 한계 — 진짜로 핀되는 건 맨 아래 블록뿐입니다. 그 위의 고정 블록(명세 박스 등)은 자기 아래 콘텐츠(노트 등)의 높이가 형제 간 같을 때만 함께 정렬됩니다 — 그래서 콘텐츠 패리티(노트를 평행한 길이로 유지)가 장식이 아니라 앵커 정렬을 지탱하는 조건입니다. 특정 블록을 반드시 정렬하려면 그 블록을 맨 아래에 두세요.
내부 리듬 일관성#
- 규칙 — 형제 카드는 같은 내부 섹션 순서, 같은
gap스케일, 같은 패딩을 공유합니다 — 바깥 크기뿐 아니라 구조의 패리티까지 일치시킵니다. - 근거 — 한 카드는
space-3, 옆 카드는space-4로 호흡하면 같은 행인데 다른 리듬으로 읽혀 정렬감이 무너집니다. - 기법 — 형제 카드는 같은 클래스로
gap·padding을 단일 출처에 고정합니다 — 카드별 인라인 오버라이드를 금지합니다. 간격은 스페이싱 리듬(요소 내부 1–3 · 블록 사이 8–12)을 따라 카드 전체가 같은 단계를 씁니다.
콘텐츠 패리티#
- 규칙 — 형제 카드는 비교 가능한 정보 밀도를 가집니다 — 한 카드가 거의 비어 있고 옆 카드는 가득 찬 상태를 두지 않으며, 카피는 구조적으로 평행합니다.
- 근거 — 같은 골격·같은 앵커여도 한쪽이 빈약하면 짝이 깨져 보입니다. 정렬은 픽셀만의 문제가 아니라 내용의 무게가 평행할 때 완성됩니다.
- 기법 — 데이터 모델에서 형제가 같은 필드 집합을 채우도록 강제하고(빈 슬롯은 플레이스홀더로 자리를 지킴), 카피는 같은 문형으로 평행하게 작성합니다.
온-스케일 치수#
- 규칙 — 높이·여백·간격은
--wds-space-*(4px 스케일) 토큰에서 고릅니다. 임의 px(104px·176px같은)는 같은 종류의 표면끼리 리듬을 깹니다. - 기법 — 같은 측정 표면(예: 스펙 바)은 하나의 높이 토큰을 공유합니다 — 그리드 스펙 바와 분할 스펙 바가 같은 행 높이(80px)를 쓰듯. 한쪽만 104px이면 나란히 봤을 때 어긋납니다.
컨테이너 채움 (shrink-wrap 금지)#
- 규칙 — flex/grid 자식이 가운데 정렬(
justify-content: center) 컨테이너 안에서 의도한 폭으로 렌더되려면width: 100%(또는align-self: stretch)가 필요합니다. 없으면 콘텐츠 폭으로 쪼그라들어(shrink-wrap) 작게 중앙에 박힙니다. - 기법 — 데모 캔버스처럼 자식을 가운데 정렬하는 부모에 폭이 콘텐츠 의존인 컴포넌트(
Split·Carousel등)를 넣을 땐 폭 100% 래퍼로 감쌉니다 — 실제로 두 컴포넌트 데모가 이 누락으로 깨진 적이 있습니다.
표현 일관성#
- 규칙 — 같은 개념을 두 번 표현하면(측정 목업 + 라이브 데모) 색·치수·마커 언어를 통일합니다. 한쪽은 primary 틴트, 한쪽은 다른 회색이면 같은 것으로 안 읽힙니다.
- 기법 — 역할별 색은 목업·라이브가 같은 토큰을 씁니다 — 예: 비주얼·콘텐츠=
primary-subtle·폼·액션=surface-muted를 측정 스펙과 라이브 데모 양쪽 모두.
인접 컨트롤 크롬#
- 규칙 — 나란히 놓인 컨트롤(필드·버튼·셀렉트)은 같은 크롬(테두리·표면·높이·반경 토큰)을 공유합니다. 채워진 필드 옆의 보조 버튼이 테두리·표면 없는 맨 글리프면 짝이 안 맞아 빈약·미완성으로 읽힙니다.
- 근거 — 짝 컨트롤이 다른 무게(채운 면 vs 투명 글리프)로 보이면 의도된 그룹이 아니라 어긋난 요소로 읽힙니다.
- 기법 — 보조
IconButton에 옆 필드와 같은border·background: surface·border-radius: control을 부여하거나 같은 채움 variant를 씁니다 — 채워진 필드 옆ghost단독은 금지합니다.
캡션·문단 정렬#
- 규칙 — 다줄 캡션·설명문은 의도된 정렬을 갖습니다 — 본문성 텍스트는 좌측 정렬(가운데 정렬 금지), 문단 사이는 토큰 간격으로 분리합니다.
- 근거 — 가운데 정렬한 다줄 텍스트는 줄 길이가 들쭉날쭉(고아 단어)해 "정렬 안 됨"으로 읽힙니다.
- 기법 —
text-align: left+text-wrap: pretty(고아 줄 회피), 문단 분리는--wds-space-*에서 —2px같은 비-토큰 간격을 쓰지 않습니다.
컨트롤 경계 수용#
- 규칙 — 인터랙티브 컨트롤(캐러셀 화살표·슬라이더 핸들 등)은 섹션 콘텐츠 박스 안에 머뭅니다. 섹션 가장자리에 닿거나 넘으면 깨진 것으로 읽힙니다.
- 근거 —
padding-inline: calc(50% - X)같은 % 중앙정렬 패딩은 containing block 기준이라, flex 형제(화살표)가 폭을 뺏는 좁은 컨테이너에서 트랙의 최소폭이 가용폭을 초과해 컨트롤을 바깥으로 밀어냅니다. - 기법 — 거터 컨트롤을
position: absolute오버레이로 두어 트랙이 풀폭이 되게 하면50%가 정확히 해석됩니다 — 또는 % 패딩을 형제 폭만큼 보정합니다. 좁은 컨테이너에서 컨트롤 가장자리가 섹션 안인지 프리뷰로 실측합니다.
인터랙션 일관성#
- 규칙 — 같은 동작(복사·확장·선택 등)은 시스템 전역에서 같은 패턴·프리미티브로 구현합니다 — 표면마다 다른 방식(한쪽 hover 아이콘, 한쪽 클릭+툴팁)을 두지 않습니다.
- 근거 — 같은 의미의 동작이 표면마다 다르게 작동하면 학습 비용이 늘고 "한 시스템"으로 안 읽힙니다 — 표현 일관성의 인터랙션 판본입니다.
- 기법 — 복사는 공유
CopyTile(또는useCopyToClipboard+copyTip, 타일 클릭 → 복사 + "복사됨" 툴팁)을 씁니다 — 새 표면이 ad-hoc 복사 UI(hover 아이콘 등)를 만들지 않게 합니다.
| 규칙 | 적용 | 안티패턴 |
|---|---|---|
| 동일 높이 | 행 안의 형제는 Grid/Flex 기본 stretch로 같은 높이 (.stage 기준); 내부 박스도 flex: 1로 동일 크기 | align-items: start로 바닥선을 끊거나, 내부 박스를 콘텐츠에 맞춰 줄여 형제와 크기 불일치 |
| 앵커 정렬 | 카드를 flex 컬럼으로, 가변 본문에 flex: 1 → 푸터·CTA가 카드 간 같은 선에 핀 | 메타 박스 전체에만 flex: 1 → 설명 길이차로 앵커가 카드 중간에 떠다님 |
| 내부 리듬 일관성 | 형제가 같은 섹션 순서·같은 gap·같은 패딩(단일 출처) 공유 | 카드별 인라인 gap/padding 오버라이드로 리듬이 어긋남 |
| 콘텐츠 패리티 | 형제가 같은 필드 집합·평행한 카피·비교 가능한 밀도 | 한 카드는 가득, 옆 카드는 거의 비어 짝이 깨짐 |
| 온-스케일 치수 | height·여백을 --wds-space-*에서; 같은 종류 표면은 같은 높이 토큰 | 임의 px(104·176)로 행끼리 리듬 깨짐 |
| 컨테이너 채움 | center 컨테이너의 자식은 width:100%/stretch | shrink-wrap으로 중앙에 작게 박힘 |
| 표현 일관성 | 목업·라이브가 같은 색·치수·마커 토큰 | 같은 것을 다른 색/치수로 표현 |
| 인접 컨트롤 크롬 | 나란한 컨트롤이 같은 테두리·표면·높이·반경 토큰 공유 | 채운 필드 옆 ghost 맨 글리프로 짝이 빈약 |
| 캡션·문단 정렬 | 다줄 캡션 좌측 정렬 + text-wrap:pretty, 문단 토큰 간격 | text-align:center로 줄 길이 들쭉날쭉(고아 단어) |
| 컨트롤 경계 수용 | 캐러셀 화살표 등은 섹션 박스 안(오버레이 또는 % 보정) | calc(50%-X) 패딩이 좁은 폭서 컨트롤을 섹션 밖으로 밀어냄 |
| 인터랙션 일관성 | 복사 등은 공유 프리미티브(CopyTile 클릭→복사+툴팁) | 표면마다 다른 방식(hover 아이콘 vs 클릭) |
이 규칙의 기준 적용은 머티리얼 테마 갤러리 카드입니다 — 두 카드의 설명 길이가 달라도 명세·노트가 같은 선에 핀됩니다.
레이어 위계 (z-index)#
떠 있는 표면의 쌓임 순서는 토큰으로 고정합니다 — 표면이 어느 레이어를 쓰는지가 계약입니다.
컴포넌트 내부 로컬 스태킹(자체 컨텍스트 안 1–3)은 토큰 비대상 — 1000 미만의 임의 전역 z-index는 금지합니다.
| 레이어 | z | 사용 표면 |
|---|---|---|
skip-link | 1600 | 본문 건너뛰기 링크 (접근성 최상위) |
tooltip | 1500 | Tooltip |
toast | 1400 | Toast |
modal | 1300 | Modal · Drawer · BottomSheet · 셸 검색 |
overlay | 1200 | 스크림 · 셸 드로어 내비 |
sticky | 1100 | 셸 헤더 |
dropdown | 1000 | Select · MultiSelect · Autocomplete · DropdownMenu · Popover · 날짜/시간 피커 |
컴포넌트 내부 로컬 스태킹(자체 스태킹 컨텍스트 안의 1–3, 예: DataGrid 스티키 셀, RangeSlider 썸)은 토큰 비대상입니다 — 단, 1000 미만의 임의 전역 z-index는 금지합니다.
| 토큰 | 값 | 설명 |
|---|---|---|
| 앵커 팝업 — Select·DropdownMenu·Popover·날짜/시간 피커 등 | ||
| 고정 크롬 — 셸 헤더 | ||
| 스크림/드로어 내비 | ||
| Modal·Drawer·BottomSheet·셸 검색 | ||
| — | ||
| — | ||
| 본문 건너뛰기 링크 — 접근성 최상위. 어떤 떠 있는 표면보다 위 |