🧩 Design System3. Typography & Spacing02 — Spacing Scale (4 / 8 Grid)

02 — Spacing Scale (4 / 8 Grid)

이 문서가 답하는 질문: 모든 디자인 시스템이 4px 또는 8px를 기준 단위로 쓰는 이유는 무엇인가? space.1=4 vs space.xs=4 — 둘 중 어느 네이밍이 더 나은가? Tailwind의 0/0.5/1/1.5/…/96 스케일은 어떻게 만들어졌는가? 한 줄 답 (Pyramid Top): spacing은 “4 또는 8의 배수만 허용하는 닫힌 집합” 이다 — 4px는 세밀함(아이콘·캡션), 8px는 *정렬 확실성(레이아웃)*의 trade-off다. 이름은 숫자 기반(space.1, space.2, …)이 T-shirt(xs/sm/md/…)보다 확장성이 좋다 — 6개를 넘는 순간 T-shirt는 무너진다.


Why — 왜 4 또는 8의 배수인가

1) 그리드 정렬

  • 8의 배수: 16, 24, 32, 40 … 가 모두 짝수의 짝수. 두 요소를 가운데 정렬하거나 절반 띄울 때 반올림 오차가 없다.
  • 4의 배수: 8의 배수보다 2배 촘촘. 작은 아이콘(16×16) 안의 패딩처럼 8로 표현이 안 되는 영역을 채운다.

2) 디바이스 픽셀 정합

  • 모든 모던 스크린은 짝수 픽셀로 동작 (1x·2x·3x 모두 4의 배수가 정수 픽셀로 떨어짐).
  • padding: 5px 같은 홀수는 1x 화면에서 정수, 2x 화면에서 10px, 3x에서 15px제어가 어려운 분수가 됨.

3) 인지 — 디자이너 관점

  • 디자이너가 Figma에서 8단위로 스냅하면, 개발자가 눈으로 그 값을 추측할 수 있다.
  • padding: 13px처럼 임의의 값은 디자이너의 즉흥이지 시스템이 아니다.
풀려는 문제4-grid의 해8-grid의 해
픽셀 정합4px = 모든 dpr에서 정수8px = 더 안전
정렬 확실성약함강함 (절반·1/4·1/8 분할 모두 정수)
세밀함강함 (아이콘 내부 패딩 4px)약함 (8px는 큰 단위)
디자인 부채적음가장 적음

How — 4 vs 8의 trade-off와 hybrid

순수 8px grid

0 / 8 / 16 / 24 / 32 / 40 / 48 / 56 / 64 / 72 / 80 / 96 / 128
  • 가장 강한 그리드. 하지만 아이콘 내부의 4px padding 같은 케이스가 불가능.

순수 4px grid

0 / 4 / 8 / 12 / 16 / 20 / 24 / 28 / 32 / 36 / 40 / 48 / 56 / 64 / 80 / 96
  • 세밀하지만, 디자이너가 16 vs 20 vs 24를 매번 고민해야 함.

Hybrid (사실상 표준): 4-grid 기반 + 8에 우선

[0] · 2 · 4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64 · 80 · 96 · 128
  • 작은 값(2·4·12)은 예외적으로 허용.
  • 대부분의 값은 8의 배수.
  • 24 이후는 8 그리드가 default.

Tailwind/Panda 모두 이 hybrid를 채택한다.


What — Tailwind 스케일 (전수)

Tailwind v4 default spacing scale — 단위는 rem (1rem = 16px 가정):

토큰rempx
000
px1px1
0.50.125rem2
10.25rem4
1.50.375rem6
20.5rem8
2.50.625rem10
30.75rem12
3.50.875rem14
41rem16
51.25rem20
61.5rem24
71.75rem28
82rem32
92.25rem36
102.5rem40
112.75rem44
123rem48
143.5rem56
164rem64
205rem80
246rem96
328rem128
4010rem160
4812rem192
5614rem224
6416rem256
8020rem320
9624rem384

Tailwind는 숫자 = 4 × n / 16 (rem) 공식 — 41rem, 82rem. 즉 숫자 키는 4px 단위의 multiplier다.

Tailwind v4의 단순화

Tailwind v4는 --spacing 단일 변수로 모든 값 생성한다:

@theme {
  --spacing: 0.25rem;  /* 4px = 1 unit */
}
 
/* p-4 → padding: calc(var(--spacing) * 4) = 16px */
/* p-7 → padding: calc(var(--spacing) * 7) = 28px */
/* p-13 → padding: calc(var(--spacing) * 13) = 52px */
  • 이전 v3는 spacing 객체에 30여 개 키를 직접 나열.
  • v4는 계산식으로 무한 확장. p-100도 동작 (= 400px).

What — Panda CSS 스케일

Panda는 token-first이므로 명시적인 키를 둔다:

// panda.config.ts
export default defineConfig({
  theme: {
    tokens: {
      spacing: {
        0:    { value: "0" },
        px:   { value: "1px" },
        0.5:  { value: "0.125rem" },  // 2px
        1:    { value: "0.25rem"  },  // 4px
        1.5:  { value: "0.375rem" },  // 6px
        2:    { value: "0.5rem"   },  // 8px
        3:    { value: "0.75rem"  },  // 12px
        4:    { value: "1rem"     },  // 16px
        5:    { value: "1.25rem"  },  // 20px
        6:    { value: "1.5rem"   },  // 24px
        8:    { value: "2rem"     },  // 32px
        10:   { value: "2.5rem"   },  // 40px
        12:   { value: "3rem"     },  // 48px
        16:   { value: "4rem"     },  // 64px
        20:   { value: "5rem"     },  // 80px
        24:   { value: "6rem"     },  // 96px
        32:   { value: "8rem"     },  // 128px
        40:   { value: "10rem"    },  // 160px
        48:   { value: "12rem"    },  // 192px
        64:   { value: "16rem"    }   // 256px
      }
    }
  }
});
  • 사용: <div className={css({ padding: "4", gap: "2" })}>padding: 1rem; gap: 0.5rem.
  • 타입 안전: padding: "5px" 같은 임의값은 IDE가 경고.

Tailwind vs Panda 비교

항목Tailwind v4Panda CSS
정의단일 --spacing 변수 + 계산명시적 키 나열
임의값p-[13px] 허용 (escape hatch)padding: '13px'는 token이 아닌 raw
타입 안전className 문자열TS literal union
확장무한 (p-100도 OK)config에 추가해야 함
닫힌 집합 강제약함강함

What — T-shirt vs Numeric naming

T-shirt (의미적·짧음)

xs · sm · md · lg · xl · 2xl · 3xl
  • 장점: 짧고 상대적 위계가 직관적 (xl > md).
  • 단점: 6개 넘어가면 무너진다5xl, 6xl, 7xl상대 크기를 디자이너가 못 외움.
  • 적합: 색·spacing처럼 6단계 이하의 의미 tier.

Numeric (확장성·중립)

0 · 1 · 2 · 3 · 4 · 5 · 6 · 8 · 10 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64 · 80 · 96
  • 장점: 무한 확장. 수학적 명료성 (4 = 16px).
  • 단점: 절대 의미가 없음 (space.624px인지 외워야 함).
  • 적합: 30개 이상의 spacing scale.

결론: 카테고리별로 다르게

카테고리추천이유
font-sizeT-shirt보통 8~10단계, 위계가 의미적
spacingNumeric30+ 단계, 수치가 중요
radiusT-shirt5~7단계
shadowT-shirt5~7단계
z-indexsemantic (modal, toast)tier가 의미적
color scaleNumeric (50~950)12단계 정밀도

What-if — 잘못 쓰면 어떻게 깨지는가

  • 함정 1: padding: 13px 허용 — 디자이너가 그 자리에서만 작동하는 값을 만듦. 다른 곳에 재사용 불가. 대응: linter로 token이 아닌 spacing을 금지. Panda는 기본 강제, Tailwind는 tailwindcss-no-arbitrary-values 같은 ESLint 룰로 강제.
  • 함정 2: T-shirt 사이즈 7개 초과5xl / 6xl / 7xl / 8xl … 디자이너가 어느 게 큰지 못 외움. 대응: spacing은 numeric으로 전환, font-size는 role 도입 (display.xl).
  • 함정 3: spacing 토큰을 component-level로 정의button.padding-x = 12px. 한 컴포넌트가 깨지면 그 토큰만 수정해서 해결 → 시스템 전체의 spacing이 깨짐을 그 컴포넌트가 가림. 대응: component-level은 primitive를 alias만 해야 함 (button.padding-x = {spacing.3}).
  • 함정 4: 음수 spacing 토큰화 시도margin-top: -8px 같은 음수는 사용 빈도가 너무 낮아 토큰화 가치가 없다. 대응: 필요한 곳에서만 음수 값을 직접 쓰고, 가능하면 flex/grid의 gap으로 해결.
  • 함정 5: margingap을 같은 토큰 풀에서 — 의미는 다르지만 OK. 다만 컴포넌트 외부 margin 은 토큰 사용을 최소화 (Stack, Cluster 같은 레이아웃 컴포넌트로 대체).

Insight — 8px 그리드의 기원

Bootstrap 1.0(2011)은 gutter 20px / 30px 같은 임의의 값을 썼다. 시각적으로 자연스럽지만 왜 20인지는 누구도 답하지 못했다.

2014년 Google Material Design이 발표되며 “8dp baseline grid” 를 명시적으로 천명했다 — 모든 spacing은 8dp의 배수, 아이콘·텍스트는 4dp 보조 grid. 이게 사실상 표준이 됐다.

흥미로운 점: 8은 디자이너의 직관 + 개발자의 수학이 일치하는 유일한 작은 정수라는 것. 4는 너무 촘촘하고, 12는 너무 거칠다. 8은 :

  • 절반(4), 1/4(2), 1/8(1)이 모두 정수
  • 1x/2x/3x DPI에서 반올림 없는 정수 픽셀
  • 8 × n 으로 16/24/32/40/48 같은 흔한 크기들이 자연스럽게 나옴

Tailwind의 4px base (rem 기준 0.25rem)는 8의 절반을 채택한 세밀한 버전이다 — 즉 4-grid를 깔되 디자이너에게는 8을 권장하는 hybrid.


요약

  • spacing은 4 또는 8의 배수 닫힌 집합이다.
  • 4는 세밀함, 8은 정렬 확실성. 실무는 hybrid.
  • 이름: numeric(space.1=4px)이 30+ 단계엔 정답. T-shirt는 6단계 이하.
  • Tailwind v4는 --spacing 단일 변수, Panda는 명시적 token 키.
  • 임의값(p-[13px])을 ESLint로 막아야 시스템이 산다.