- 1 기본사항
- 2 Flexbox
- 3 그리드 및 하위 그리드
- 4 컨테이너 쿼리
- 5 네이티브 오버레이, 앵커 위치 지정, 쌓임 맥락
- 6 오버플로 추적 및 레이아웃 안정성
- 7 표시 영역 메커니즘 및 트랙 배포
- 8 그리드 레인 (일명 벽돌)
1 기초
성능을 개선하려면 가능한 경우 브라우저의 레이아웃 엔진을 사용하세요. 하드코딩된 크기나 복잡한 미디어 쿼리를 사용하기 전에 고유 크기, 논리적 속성, aspect-ratio를 사용하세요.
1.1 어떤 레이아웃 모드를 사용해야 하나요?
결정 트리를 위에서 아래로 탐색하고 첫 번째 일치 항목에서 중지합니다. 레이아웃은 서로 중첩될 수 있으며 각 결정은 해당 컨테이너의 사용 사례를 기반으로 합니다.
- 단순한 행 또는 열로 구성된 항목인가요? Flexbox 사용 — 1D, 콘텐츠 우선, 콘텐츠가 단일 축을 따라 분산됩니다.
- 중첩된 요소가 상위 요소의 상위 요소 그리드 트랙과 정렬되어야 하나요? 하위 그리드 사용 — 2D, 관계 우선, 상위 트랙을 상속하므로 하위 요소가 동위 요소 간에 정렬될 수 있습니다.
- 행과 열이 있는 복잡한 페이지 또는 구성요소 구조인가요? 그리드 사용 — 2D, 레이아웃 우선, 스켈레톤을 정의하고 콘텐츠가 이를 채웁니다.
- 콘텐츠가 균형 잡힌 열로 분할되어야 하는 긴 산문 흐름인가요? 다중 열(1D 흐름, 신문 스타일)을 사용합니다.
- 높이가 다양한 상품을 촘촘하게 포장해야 하나요? 지금은
grid-auto-flow: dense와 함께 grid를 사용하세요. 기준 타겟에 기본 masonry (일명 '그리드 레인')가 제공되는 경우에만 이를 사용하세요(§8 참고). - DOM 경계나 스택 컨텍스트를 가로지르더라도 요소가 페이지 위에 떠 있고 트리거에 공간적으로 연결되어 있어야 하나요? 앵커 위치 지정을 사용합니다. 트리거에는
anchor-name, 오버레이에는position-anchor를 사용합니다(§5 참고).
1.2 작동 원리
- 레이아웃 크기와 간격에 논리적 속성 (
inline-size,block-size,margin-inline,padding-block,inset-inline-start)을 사용합니다. 자세한 내용은 CSS: 현대적인 아키텍처를 참고하세요. - 콘텐츠 우선과 레이아웃 우선의 멘탈 모델을 적용합니다. 항목이 흐름을 결정할 때는 flexbox를, 스켈레톤을 먼저 정의할 때는 그리드를 사용합니다.
place-*약식 (place-content,place-items,place-self)을 사용하여 하나의 선언에서 두 축을 정렬합니다.- 고정된
width/height대신 고유 크기 조정 (min-content,max-content,fit-content())과 유연한 트랙 (fr,minmax())을 사용하세요. 미디어 쿼리가 줄어들고 레이아웃이 더 탄력적으로 조정됩니다. overflow: hidden를 사용하여 레이아웃 문제를 가리지 마세요. 소스에서 문제를 해결하세요. 예를 들어<canvas>,<img>,<svg>,<video>,<iframe>와 같은 특정 대체 요소는 기본적으로display: inline를 사용하므로 특정 레이아웃에 간격이 추가될 수 있습니다.overflow로 간격을 자르는 대신 블록 display-outside 값 (예:display: block,grid,flex등)을 적용하여 간격을 삭제하세요.aspect-ratio를 사용하여 미디어 공간을 예약하고 애셋이 로드되기 전에 레이아웃 변경을 방지합니다.
.sidebar { inline-size: max-content; } /* Size to longest unbreakable token. */
.main-content { inline-size: fit-content; } /* Grow to available space, no further. */
.media { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered { display: grid; place-content: center; min-block-size: 100dvb; }
calc-size()및 제약 조건 인식 고유 크기 조절은 고유 크기로 계산을 참고하세요.
2 Flexbox
1차원 레이아웃: 항목이 단일 기본 축을 따라 흐르며 교차 축에 정렬됩니다. 탐색 메뉴, 툴바, 항목 행, 단일 행 또는 열 분포에 사용하세요.
권장사항:
display: flex로 컨텍스트를 설정하고flex-direction(row기본값)로 기본 축을 설정합니다.- 오버플로가 발생할 수 있는 경우 항상
flex-wrap: wrap를 사용하세요.overflow: auto/hidden가 없는nowrap는 좁은 표시 영역에서 오버플로됩니다. - 압축된 항목 (예: 태그 클라우드, 필터 필, 버튼 그룹, 탐색 링크)의 래핑된 행에서
flex-wrap: wrap뒤에flex-wrap: balance를 레이어링하여 항목을 여러 줄에 균등하게 분산하고 마지막 행에 단일 분리 항목이 생기지 않도록 합니다. 지원되지 않는 브라우저는wrap로 대체됩니다. flex-grow/flex-shrink/flex-basis를 개별적으로 설정하는 대신 항목에flex약식<grow> <shrink> <basis>(예:flex: 1 1 250px)를 사용합니다.- 하위 여백 대신 항목 간 간격에
gap(또는row-gap/column-gap긴 형식)를 사용합니다. - 컨테이너가 콘텐츠보다 좁을 때 포커스 가능한 콘텐츠가 잘리지 않도록 위치 정렬에
safe(예:align-items: safe center)를 접두사로 추가합니다. margin-inline-start: auto(또는margin-block-start: auto)를 사용하여 단일 항목을 기본축의 맨 끝으로 밀어냅니다. 이것이 표준 이스케이프 해치입니다.align-self를 사용하여 항목별로 교차축 정렬을 재정의합니다.align-items를 사용하여 교차축에서 모든 항목을 중앙에 배치합니다. 단일 항목에margin: auto를 사용하여 두 축에서 독립적으로 중앙에 배치합니다. 컨테이너가 줄바꿈되고 행에 추가 공간이 있는 경우에만align-content를 사용합니다.- 끊을 수 없는 긴 콘텐츠(URL, 코드, 긴 문자열)가 포함된 flex 항목에
min-inline-size: 0(또는min-width: 0)를 설정합니다. flex 항목은 기본적으로 콘텐츠 크기보다 작게 축소되지 않으므로 오버플로가 발생합니다.
금지사항:
- flex 항목에는
justify-self를 사용하지 마세요. 그리드, 블록, 절대 위치 레이아웃에서만 작동합니다. 대신 자동 여백을 사용하세요. order또는flex-direction: *-reverse를 사용하여 대화형 콘텐츠의 순서를 변경하지 마세요. 시각적 순서만 변경됩니다. DOM 순서는 여전히 순차적 포커스를 유도하므로 키보드 탭 흐름이 사용자가 보는 것과 일치하지 않습니다.space-around(끝부분의 절반 간격)를space-evenly(앞, 중간, 뒤의 동일한 간격)와 혼동하지 마세요.- 축 뒤집기를 잊지 마세요.
flex-direction: column인 경우justify-content는 블록 축에 정렬되고align-items는 인라인 축에 정렬됩니다. 이는 기본값과 반대입니다. - 컨테이너와 하위 요소가 서로를 채우도록 크기를 지정하지 마세요. 이는 오버플로와 예상치 못한 결과의 일반적인 원인입니다. 한쪽에 명확한 크기를 지정합니다.
- 동일한 항목에
flex-basis와width/inline-size를 모두 설정하지 마세요. flex 컨텍스트에서는flex-basis가 우선 적용되고width는 무시됩니다.flex-basis(또는flex약식)를 가변 항목 크기 조정을 위한 단일 정보 소스로 사용합니다.
.card-grid { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item { flex: 1 1 250px; } /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; } /* Push to main-axis end. */
.toolbar { display: flex; align-items: safe center; }
.tag-list { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }
3 그리드 및 하위 그리드
2차원 레이아웃 — 행과 열을 명시적으로 정의하거나 엔진이 이를 도출하도록 합니다. 하위 그리드를 사용하면 중첩된 그리드가 상위 그리드의 트랙을 상속하므로 하위 요소가 동위 요소 간에 정렬됩니다.
그리드 기능 선택:
- 필요한 열의 수를 정확히 알고 있나요?
- 예 — 선정적인 트랙 (
grid-template-columns: 200px 1fr,repeat(3, 1fr)등)을 사용합니다. - 열마다 다른 크기가 필요한가요 (사이드바 + 기본, 모든 열에 걸쳐 있는 헤더)? → 이름이 지정된 읽기 가능한 리전에
grid-template-areas사용 - 모든 열이 균일한가요, 아니면 순전히 줄 번호로 배치되나요? →
repeat(N, ...)또는 이름이 지정된 선 사용 - 아니요 (반응형, 알 수 없는 항목 수) —
repeat(auto-fit, minmax(min, 1fr))사용 - 마지막 행의 항목이 남은 공간을 채우도록 늘어나야 하나요? →
auto-fit - 비어 있는 마지막 행 트랙이 최소 크기를 유지해야 하나요 (열 고스트 슬롯 유지)? →
auto-fill
- 예 — 선정적인 트랙 (
- 특정 위치에 항목을 배치해야 하나요?
- 예 —
grid-column: <start> / <end>또는grid-area: <name>사용 - 아니요 (여러 트랙에 걸쳐 있으며 흐름 위치는 중요하지 않음) —
grid-column: span <n>사용
- 예 —
- 하위 요소가 상위 그리드의 트랙 크기를 상속해야 하나요 (동위 요소 간의 불규칙한 가장자리 정렬)?
- 예 — 영향을 받는 축에 하위 그리드 사용
- 셀당 자녀 수가 가변적인가요? → 하위 그리드 한 축만, 다른 축에는
grid-auto-rows/grid-auto-columns사용 - 하위 요소 수가 고정되어 있나요? → 양쪽 축의 하위 그리드는 괜찮습니다.
- 아니요 — 표준 그리드, 하위 그리드 필요 없음
권장사항:
display: grid를 사용하여 컨텍스트를 설정합니다.- 복잡한 페이지 수준 레이아웃에는
grid-template-areas를 사용하세요. 영역 이름은 자체 문서화되며 선언을 행과 열로 정렬하여 한눈에 읽을 수 있습니다. repeat(auto-fit, minmax(200px, 1fr))를 사용하여 채워진 트랙을 늘려 행을 채우는 반응형 카드 그리드를 만들거나auto-fill를 사용하여 비어 있는 반복 트랙을 최소 크기로 유지합니다.- 비례 트랙 분포에는
fr를 사용하고 유연하지만 제한된 트랙에는minmax(min, max)를 사용합니다. grid-column: span <n>를 사용하여 트랙 간 크기를 조정하고,grid-column: <start> / <end>를 사용하여 특정 선에 배치하고,grid-area: <name>를 사용하여 명명된 영역에 배치합니다.- 하위 그리드 (
grid-template-columns: subgrid또는grid-template-rows: subgrid)를 사용하여 카드 목록의 '들쭉날쭉한 가장자리' 문제를 해결합니다. 제목, 메타데이터, CTA와 같은 내부 요소가 동위 요소에 걸쳐 정렬됩니다. - 이전 브라우저에서 동일한 캐스케이드 대체로 사용하기 위해 하위 그리드 선언을 이전의 명시적
grid-template-rows/-columns선언과 페어링합니다.
금지사항:
auto-fit/auto-fill트랙 크기가 항목 콘텐츠에서 가져올 것으로 예상하지 마세요.repeat()크기 인수에서 가져옵니다.- 양방향 콘텐츠에는
grid-auto-flow: dense를 사용하지 마세요. 항목을 효율적으로 패킹하지만 시각적으로 재정렬하여 DOM 순서 키보드 탭 흐름을 중단합니다. - 하위 요소 수가 가변적인 경우 두 축에 하위 그리드를 적용하지 마세요. 추가 항목은 마지막 트랙에 배치됩니다. 대신 암시적 축에
grid-auto-rows/grid-auto-columns를 사용하세요. justify-items/align-items(트랙 내에서 항목 콘텐츠 정렬)를justify-content/align-content(컨테이너 내에서 그리드 트랙 정렬)와 혼동하지 마세요. 잘못된 것을 사용하면 아무런 효과가 없습니다.- 컨테이너에 명확한
inline-size없이repeat(auto-fit/auto-fill, ...)를 사용하지 마세요.display: inline-grid또는 크기가 지정되지 않은 flex 항목 내에서 컨테이너에는 나눌 너비가 없으므로 트랙 수를 예측할 수 없습니다.
3.1 코드 예시: 그리드 및 하위 그리드
페이지 셸: <main class="page-layout">에는 <header>, <aside>, <div class="card"> 하위 요소가 있는 <section class="card-grid">, <footer>가 포함됩니다.
/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
gap: 1.5rem;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
footer { grid-area: footer; }
.card-grid {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-rows: auto 1fr; /* title block, body block */
gap: 1rem;
}
.card {
grid-row: span 2;
display: grid;
/* Same-cascade fallback: ignored when subgrid is supported. */
grid-template-rows: auto 1fr;
grid-template-rows: subgrid;
}
4 컨테이너 쿼리
표시 영역이 아닌 상위 컨테이너의 크기 (또는 계산된 스타일)를 쿼리합니다. 정신 모델: 컨테이너 쿼리 = 구성요소 컨텍스트, 미디어 쿼리 = 전역 페이지 레이아웃 및 사용자 환경설정 (prefers-color-scheme, prefers-reduced-motion)
권장사항:
- 하위 요소를 쿼리하기 전에 래퍼에서
container-type: inline-size(너비 전용 쿼리) 또는container-type: size(두 축 모두)를 사용하여 포함 컨텍스트를 설정합니다. - 중첩된 컨텍스트가 충돌할 수 있는 경우
container-name(또는container약식:container: card / inline-size)를 사용하여 컨테이너의 이름을 지정합니다. - 유동 유형 및 간격을 계산할 때 컨테이너 쿼리 단위를 포함합니다.
cqi/cqb(논리적 인라인/블록),cqw/cqh(물리적),cqmin/cqmax container-type: size를 사용할 때마다 컨테이너에 명확한block-size를 지정합니다. 지정하지 않으면 크기 포함으로 인해 컨테이너가 콘텐츠를 무시하므로 하위 요소가 축소됩니다.
금지사항:
block-size을container-type값으로 사용하지 마세요. 유효하지 않습니다. 두 축 모두에size를 사용합니다.container-type를 선언한 후에는 하위 요소의 고유 크기가 컨테이너에 영향을 미치지 않습니다. 포함이 활성화되면 컨테이너는 하위 요소가 없는 것처럼 계산됩니다.- 조건을 충족하지 않는 상위 요소의 하위 요소 내에 있는 컨테이너 쿼리 단위에 의존하지 마세요. 작은 표시 영역 (
svw/svh)으로 대체됩니다.
4.1 코드 예시: 컨테이너 쿼리 단위를 사용하는 유동적 서체
.card-wrapper {
container: card / inline-size; /* shorthand for container-name / container-type */
}
@container card (inline-size > 400px) {
.content {
display: flex;
gap: 2rem;
}
}
.title {
/* Fluid type bound to the container width, not the viewport. */
font-size: clamp(1rem, 4cqi, 2rem);
}
구성요소 기반 반응형 스타일 지정 패턴은 크기 인식 스타일 지정 및 유연한 확장을 참고하세요.
5 네이티브 오버레이, 앵커 위치 지정, 스태킹 컨텍스트
각 오버레이 프리미티브를 사용하는 경우:
- 일시적인 비모달 UI (플라이아웃, 토스트 메시지, 도움말)에는
popover를 사용하세요. 최상위 레이어에 있으며z-index관리가 필요하지 않습니다. - 포커스 트래핑과 비활성 배경이 필요한 모달 상호작용에는
.showModal()과 함께<dialog>을 사용합니다. - 동일한 요소에
popover와.showModal()를 결합하지 마세요. 이 두 가지는 상호 배타적인 런타임 상태입니다.
앵커 위치 지정 (오버레이의 공간 레이아웃):
position-area(또는 인셋의anchor()) 및anchor-size()를 사용하여 트리거를 기준으로 오버레이의 위치와 크기를 지정합니다.position-try-fallbacks: flip-block(또는flip-inline)를 사용하여 오버레이가 표시 영역을 오버플로할 때 브라우저가 위치를 변경하도록 합니다.- 단일
position-area값에 물리적 키워드와 논리적 키워드를 혼합하지 마세요. 하나의 좌표계를 선택하세요. @supports (anchor-name: --x)로 기능을 감지하고 절대 위치 대체 기능을 제공합니다.
전체 구현 세부정보, 폴리필 전략,
popover값 참조는 선언적 대화상자 및 팝오버 컨트롤 및 위치 인식 도움말을 참고하세요. 드롭다운, 플라이아웃, 탭 표시기에 적용된 앵커 위치 지정은 표시 영역 인식 드롭다운 및 앵커 위치 지정 탭 밑줄을 참고하세요.
6 오버플로 추적 및 레이아웃 안정성
레이아웃 변경, 스크롤바, 클리핑을 예측 가능하게 관리합니다.
권장사항:
overflow: auto를 사용하여 콘텐츠가 실제로 오버플로될 때만 스크롤바가 표시되도록 합니다.overflow: clip을 사용하여 스크롤 컨테이너를 설정하지 않고 콘텐츠를 클립합니다.overflow-clip-margin을 사용하여 오버플로를 선택합니다.scrollbar-gutter: stable를 사용하여 스크롤바를 위한 공간을 예약하고 콘텐츠가 늘어날 때 레이아웃 변경을 방지합니다.- 중첩된 스크롤 영역(예: 사이드바, 모달, 목록)에서
overscroll-behavior: contain(또는none)를 사용하여 스크롤 체인이 페이지로 버블링되지 않도록 하거나, 중간 스크롤 래퍼 (예: 스와이프하여 표시 트랙)에서overscroll-behavior: chain를 사용하여 초과 스크롤이 로컬 고무 밴드 바운스를 트리거하지 않고 상위 스크롤러로 전파되도록 합니다. 여러 줄을 자르려면
-webkit-line-clamp+display: -webkit-box+-webkit-box-orient: vertical조합을 사용하세요. 이 패턴은 접두사가 있지만 완전히 지정되어 있으며 지원 중단되지 않았습니다. 접두사가 없는line-clamp약칭을 함께 선언합니다. 아직 지원하지 않는 브라우저는 속성을 무해하게 무시합니다. 금지사항:auto로 충분한 경우overflow: scroll를 사용하지 마세요.scroll는 스크롤할 항목이 없는 경우에도 스크롤바를 강제로 표시합니다.클립만 하려는 경우
overflow: hidden를 사용하지 마세요.hidden는 프로그래매틱 방식으로 스크롤할 수 있는 스크롤 컨테이너를 설정합니다.
.scrollable-list {
max-block-size: 400px;
overflow-y: auto;
scrollbar-gutter: stable; /* Reserve scrollbar space. */
overscroll-behavior: contain; /* No scroll chaining into the page. */
}
.snippet {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
line-clamp: 3; /* Ignored where unsupported. */
overflow: clip;
}
overflow: clip및overflow-clip-margin에 관한 자세한 내용은 오버플로 클리핑 제어를 참고하세요. 스와이프하여 표시 UI에서overscroll-behavior: chain를 사용한 중첩 스크롤 핸드오프는 스와이프하여 표시를 참고하세요. 스크롤바 색상, 크기, 테마는 스크롤바의 색상 또는 두께 맞춤설정, 어두운 모드, 고대비 환경설정에 맞게 스크롤바 조정을 참고하세요.
7 표시 영역 메커니즘 및 트랙 배포
- 브라우저 UI 전환 (URL 표시줄 접기/펼치기)을 고려해야 하는 모바일 레이아웃 컨테이너에는
dvh/dvw를 사용합니다. - 전체 너비 레이아웃에는
100vw를 사용하지 마세요. 스크롤바 너비를 무시하고 가로 오버플로를 일으킵니다. 대신100%,100dvw또는100svw를 사용하세요.
반응형 디자인 진입점 (동적 표시 영역 단위, 미디어 공간 예약, 유동 타이포그래피)은 반응형 디자인을 참고하세요.
8 그리드 레인 (메이슨리라고도 함)
이 사양은 '그리드 레인' (예: display: grid-lanes)이라는 이름으로 개발 중이며, 이전 실험 프로토타입에서는 grid-template-rows: masonry를 사용했습니다.
권장사항:
- DOM 순서가 시각적 순서와 일치하지 않을 수 있다는 점을 감안하여, 오늘부터
grid-auto-flow: dense가 있는 그리드를 사용하여 조밀하게 배치하세요. - 항목이 동일한 가중치의 카드가 아닌 문서 프래그먼트인 경우 콘텐츠가 많은 벽돌과 같은 흐름에 다중 열 (
columns: 3; column-gap: 1rem)을 사용합니다. grid-template-rows: masonry는 점진적 개선으로만 처리합니다.@supports로 기능을 감지합니다.
금지사항:
- 기준 타겟이 따라잡을 때까지
grid-template-rows: masonry를 필수 요구사항으로 제공하지 마세요.
.gallery { columns: 3 200px; column-gap: 1rem; }
.gallery > * { break-inside: avoid; margin-block-end: 1rem; }
@supports (grid-template-rows: masonry) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry;
gap: 1rem;
columns: unset;
}
}