간격을 그때그때 눈대중으로 주면 어떤 카드는 14px, 어떤 카드는 15px로 벌어져 화면이 미묘하게 들쑥날쑥해집니다. 간격 시스템은 4px 또는 8px를 기준 걸음으로 삼고 모든 여백을 그 배수로만 쓰게 만듭니다. 아래 코드는 간격 토큰을 세우고 CSS 그리드로 반응형 배치까지 잇는 흐름입니다.
38.1 임의의 px를 쓰던 나쁜 예
배수 규칙이 없으면 여백 값이 아무 근거 없이 흩어집니다. 나중에 맞추려 해도 어디에 맞춰야 할지 기준이 없습니다.
.card { padding: 18px; }
.card + .card { margin-top: 14px; }
.list li { margin-bottom: 11px; }
.toolbar { gap: 7px; }
18px와 14px와 11px 사이엔 아무 관계가 없습니다. 이런 값이 쌓이면 요소들이 보이지 않게 조금씩 어긋나 화면이 정돈되지 못합니다.
38.2 4px 베이스 간격 토큰
기준 걸음을 4px로 두고 그 배수만 토큰으로 만듭니다. 숫자 이름은 곧 몇 걸음인지를 뜻하니 값이 머릿속에 그려집니다.
:root {
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 24px;
--space-6: 32px;
--space-7: 48px;
--space-8: 64px;
}
작은 쪽은 4px씩 촘촘하게, 큰 쪽은 성글게 벌어집니다. 이 사다리 밖의 어중간한 값을 쓰지 않겠다는 약속이 화면의 눈금을 하나로 맞춥니다.
38.3 padding과 gap에 토큰 쓰기
안쪽 여백과 요소 사이 간격을 전부 토큰으로 바꿉니다. 가까운 것은 좁은 토큰으로 붙이고 다른 무리끼리는 넓은 토큰으로 벌립니다.
.card {
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.card + .card {
margin-top: var(--space-4);
}
.toolbar {
display: flex;
gap: var(--space-3);
}
제목과 설명은 space-2로 바짝 묶고 카드끼리는 space-4로 벌려 두면, 선을 긋지 않아도 간격만으로 무엇이 한 덩어리인지 읽힙니다.
38.4 둥근 모서리와 컨테이너 토큰
간격뿐 아니라 모서리 반지름과 본문 최대 폭도 토큰으로 잡아 둡니다. 넓은 화면에서 글이 끝에서 끝까지 늘어지지 않도록 폭을 가둡니다.
:root {
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 16px;
--container-width: 1120px;
}
.container {
max-width: var(--container-width);
margin-inline: auto;
padding-inline: var(--space-4);
}
.card {
border-radius: var(--radius-md);
}
margin-inline: auto가 컨테이너를 가운데로 세우고, 남는 폭은 양옆에 고르게 비워집니다. 화면이 아무리 넓어도 내용은 1120px 안에서 읽기 편하게 유지됩니다.
38.5 12컬럼 그리드
화면을 12개의 세로 기둥으로 나누면 요소들이 그 기둥에 기대어 가지런히 놓입니다. 기둥 사이 틈도 간격 토큰으로 맞춥니다.
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--space-4);
}
.col-4 {
grid-column: span 4;
}
.col-6 {
grid-column: span 6;
}
.col-8 {
grid-column: span 8;
}
col-4를 셋 놓으면 12칸을 정확히 나눠 세 덩어리가 나란히 섭니다. 요소마다 폭을 px로 계산할 필요 없이 몇 칸을 차지할지만 정하면 됩니다.
38.6 auto-fit으로 알아서 접기
기둥 수를 직접 정하는 대신 최소 폭만 정해 두고 나머지는 그리드에 맡기는 방법도 있습니다. 카드 목록처럼 개수가 유동적일 때 편합니다.
.auto-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: var(--space-5);
}
카드 하나의 최소 폭을 240px로 정해 두면, 화면이 넓으면 한 줄에 넷, 좁아지면 셋, 둘, 하나로 알아서 접힙니다. 미디어쿼리 한 줄 없이 반응형 배치가 완성됩니다.
38.7 좁은 화면에서 한 줄로 쌓기
12컬럼 그리드는 좁은 화면에서 그대로 두면 답답합니다. 미디어쿼리로 기둥 수를 줄여 요소들이 세로로 차곡차곡 쌓이게 합니다.
@media (max-width: 640px) {
.grid {
grid-template-columns: 1fr;
gap: var(--space-3);
}
.col-4,
.col-6,
.col-8 {
grid-column: span 1;
}
}
640px보다 좁아지면 기둥이 하나로 줄고 모든 칼럼이 한 줄을 통째로 차지합니다. 좁은 화면에서 손가락으로 눌러야 하니 간격도 space-3으로 조금 좁혀 화면을 알뜰하게 씁니다.
38.8 세로 리듬을 토큰으로
가로만 그리드에 맞추고 세로 간격을 눈대중으로 주면 아래로 훑을 때 박자가 흔들립니다. 제목과 본문, 섹션 사이의 세로 여백도 같은 사다리에서 골라 씁니다.
.section {
margin-bottom: var(--space-7);
}
.section > h2 {
margin-bottom: var(--space-4);
}
.section > h2 + p {
margin-top: 0;
}
.stack > * + * {
margin-top: var(--space-3);
}
stack 안에서는 형제 요소 사이마다 space-3이 균일하게 들어갑니다. 첫 요소 위에는 여백이 붙지 않으니, 위쪽 간격이 이중으로 겹치는 사고도 막힙니다.
38.9 마크업에 얹어 완성하기
지금까지의 토큰을 실제 마크업에 얹으면 이렇게 됩니다. 컨테이너가 폭을 잡고, 그리드가 카드를 늘어놓고, 카드 안은 간격 토큰이 정돈합니다.
<div class="container">
<div class="auto-grid">
<article class="card">
<h3>요금제</h3>
<p>월 9900원부터 시작합니다.</p>
</article>
<article class="card">
<h3>지원</h3>
<p>평일 상담을 제공합니다.</p>
</article>
</div>
</div>
클래스 이름만 붙였을 뿐인데 폭 제한, 반응형 접힘, 안쪽 간격이 전부 토큰에서 흘러나옵니다. px 하나 손대지 않고도 어느 화면에서나 같은 리듬으로 정돈됩니다.
38.10 정리
간격과 그리드 시스템은 4px 기준 걸음의 배수로 여백을 묶고, radius와 컨테이너 폭을 토큰으로 세우고, 그리드로 정렬과 반응형 배치를 떠받치는 일입니다. 임의의 px를 흩뿌리던 화면이 배수의 눈금 위에 올라서면, 요소들이 보이지 않는 선에 맞춰 정돈되고 화면들 사이에 같은 리듬이 흐릅니다. 빈틈을 눈대중이 아니라 규칙으로 다루는 것이 그 시작입니다.