06 — Fluid Typography (clamp·cqi)
“이 글자가 모바일에선 14px, 데스크톱에선 18px이어야 한다” — 더 이상 미디어 쿼리 5개를 쓰지 않는다.
clamp(min, ideal, max)한 줄이 연속적 스케일을 만든다.
한 문장 답 (Pyramid Top)
유체 타이포는
clamp(최소값, viewport·container에 비례한 이상값, 최대값)의 3-인자 공식으로 표현한다. 핵심은 비례 기준을vw(뷰포트)에서cqi(컨테이너 인라인)로 옮기는 모던 패러다임 — 같은 컴포넌트가 어느 컨테이너에 들어가든 그 컨테이너 폭에 맞게 스케일된다.
Why — 왜 유체 타이포인가
1) 미디어 쿼리 폭발
/* 전통 방식 */
h1 { font-size: 24px; }
@media (min-width: 480px) { h1 { font-size: 28px; } }
@media (min-width: 768px) { h1 { font-size: 36px; } }
@media (min-width: 1024px) { h1 { font-size: 44px; } }
@media (min-width: 1440px) { h1 { font-size: 56px; } }- 5개의 불연속 점프. 481px에서 갑자기 28→36.
- 사이 사이즈는 외면.
- 폰트 타입마다 이걸 반복 → 디자인 시스템이 불어남.
2) 디자인 시스템의 연속 스케일
디자이너는 사실 “화면이 커지면 글자도 점진적으로 커진다” 라는 직관을 가진다. CSS도 그 직관 그대로 표현되어야 한다.
3) 접근성: 사용자의 확대
px 고정 → 사용자가 브라우저 줌을 해도 폰트가 안 커짐. rem + clamp → 접근성을 깨지 않으면서 유체.
How — clamp() 의 3-인자 공식
font-size: clamp(MIN, IDEAL, MAX);- MIN — 절대 이 아래로는 안 내려감.
- IDEAL — 이상적인 값 (보통
vw/cqi로 표현되는 동적 값). - MAX — 절대 이 위로는 안 올라감.
가장 흔한 패턴
:root {
font-size: clamp(1rem, 0.5rem + 1vw, 1.25rem);
}- 모바일 (320px 뷰포트):
0.5rem + 1vw = 0.5×16 + 320×0.01 = 8 + 3.2 = 11.2px→ MIN 16px 적용. - 데스크톱 (1280px):
0.5rem + 1vw = 8 + 12.8 = 20.8px→ MAX 20px 적용. - 사이 (768px):
8 + 7.68 = 15.68px→ MIN 16px 적용.
공식 유도
원하는 결과: 320px 화면에서 16px, 1280px 화면에서 20px.
font-size = a × vw + b × rem
320×a + b×16 = 16
1280×a + b×16 = 20
a × (1280 - 320) = 4
a = 4/960 = 0.00417 vw 단위 — 즉 0.417vw
b×16 = 16 - 320×0.00417 = 16 - 1.333 = 14.67
b = 0.917remfont-size: clamp(1rem, 0.917rem + 0.417vw, 1.25rem);유틸리티: Utopia.fyi
utopia.fyi에서 (min screen, min size, max screen, max size) 4개 입력하면 clamp 공식을 자동 생성.
How — vw vs cqi (모던 패러다임 전환)
vw (Viewport Width)
h1 { font-size: clamp(2rem, 1rem + 3vw, 4rem); }- 뷰포트 폭에 비례. 사이드바·콘텐츠 영역 폭과 무관.
- 사이드바 안에 들어간
h1이 전체 뷰포트 기준으로 커짐 → 사이드바를 뚫고 나갈 수 있음.
cqi (Container Query Inline)
.card {
container-type: inline-size;
}
.card h1 {
font-size: clamp(1.5rem, 1rem + 3cqi, 3rem);
}- 컨테이너의 인라인 폭(보통 가로) 에 비례.
- 카드가 300px이면 h1이 작고, 600px이면 큼.
- 같은 카드 컴포넌트가 어디 들어가도 적응.
단위 변환
1vw= 뷰포트 폭의 1%.1cqi= 컨테이너 인라인 폭의 1%.1cqw= 컨테이너 폭 (containers query width).1cqh= 컨테이너 높이 (vertical writing mode 시 inline).1cqmin,1cqmax= 컨테이너 인/블록 중 최소/최대.
언제 vw, 언제 cqi
- 사이트 전체 본문, 헤더 →
vw(브라우저 폭에 따라 일관). - 카드, 사이드바 안의 텍스트, 재사용 컴포넌트 →
cqi(컨테이너에 따라).
2024년 BP는 “기본 cqi, 특수한 경우만 vw”. Container query 지원이 모든 모던 브라우저에 도착했기 때문.
How — Modular Scale
유체 타이포의 개별 사이즈들을 공통 비율로 묶는다 — modular scale.
비율 (Type Scale)
| 이름 | 비율 | 특징 |
|---|---|---|
| Minor Second | 1.067 | 좁은 차이 |
| Major Second | 1.125 | 본문 위주 |
| Minor Third | 1.2 | 디폴트 (편안) |
| Major Third | 1.25 | 흔히 사용 |
| Perfect Fourth | 1.333 | 표제 강조 |
| Golden Ratio | 1.618 | 극적 |
적용
:root {
--ratio: 1.25;
--step-0: 1rem;
--step-1: calc(var(--step-0) * var(--ratio)); /* 1.25rem */
--step-2: calc(var(--step-1) * var(--ratio)); /* 1.5625rem */
--step-3: calc(var(--step-2) * var(--ratio));
--step-4: calc(var(--step-3) * var(--ratio));
--step--1: calc(var(--step-0) / var(--ratio)); /* 0.8rem */
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
body { font-size: var(--step-0); }
small { font-size: var(--step--1); }Fluid Modular Scale (조합)
각 step을 clamp으로 감싼다:
:root {
--step-0: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--step-1: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
--step-2: clamp(1.5rem, 1.3rem + 1vw, 2rem);
--step-3: clamp(2rem, 1.7rem + 1.5vw, 3rem);
--step-4: clamp(2.5rem, 2rem + 2.5vw, 4rem);
}→ 모든 스케일이 연속적·비율적·접근성 보장.
What — 완전한 BP 토큰
:root {
/* 1. 기준 크기 (모바일·데스크톱) */
--fs-min: 1rem;
--fs-max: 1.125rem;
/* 2. 비율 */
--ratio: 1.25;
/* 3. fluid scale */
--fs-0: clamp(var(--fs-min), 0.9rem + 0.5vw, var(--fs-max));
--fs-1: clamp(calc(var(--fs-min) * var(--ratio)), 1.1rem + 0.6vw, calc(var(--fs-max) * var(--ratio)));
--fs-2: clamp(calc(var(--fs-min) * pow(var(--ratio), 2)), 1.4rem + 0.8vw, calc(var(--fs-max) * pow(var(--ratio), 2)));
--fs-3: clamp(calc(var(--fs-min) * pow(var(--ratio), 3)), 1.8rem + 1.2vw, calc(var(--fs-max) * pow(var(--ratio), 3)));
--fs-4: clamp(calc(var(--fs-min) * pow(var(--ratio), 4)), 2.2rem + 2vw, calc(var(--fs-max) * pow(var(--ratio), 4)));
}
body { font-size: var(--fs-0); }
h3 { font-size: var(--fs-2); }
h2 { font-size: var(--fs-3); }
h1 { font-size: var(--fs-4); }pow()은 CSS Values 4에서 등장 — Chrome 112+, Safari 16.4+ 지원. 미지원 시 직접 곱.
What — 접근성: 사용자 줌과 rem
px 고정의 문제
body { font-size: 16px; } /* 사용자가 브라우저에서 폰트 키워도 변하지 않음 */rem + clamp의 해결
body { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); }- 사용자가 브라우저 기본 폰트를 18px로 키우면 →
rem = 18px이 되어 모든 사이즈가 함께 커짐. - 시각 장애 사용자, 노인, 고해상도 모니터 사용자에게 핵심 접근성.
WCAG 2.1 권장
- 본문 텍스트는 최소 16px (모바일).
- 사용자가 200%까지 줌해도 콘텐츠 손실 없음 →
rem+clamp로 자동.
What — cqi 실전 예시
카드 컴포넌트
.card {
container-type: inline-size;
container-name: card;
}
.card__title {
font-size: clamp(1rem, 4cqi, 2rem);
}
.card__body {
font-size: clamp(0.875rem, 2.5cqi, 1rem);
}- 카드가 그리드의 한 셀로 200px에 들어가도, 전체폭 hero로 1200px에 들어가도 각각 적절한 폰트 크기.
- 미디어 쿼리 없이.
Grid + cqi의 시너지
.layout {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 30ch), 1fr));
gap: 1rem;
}
.card {
container-type: inline-size;
}- 그리드가 컨테이너 폭을 자동 분배.
- 카드가 자기 container query로 그 폭에 맞게 폰트 스케일.
- 반응형이 컴포넌트의 책임으로 캡슐화.
What-if — 잘못 쓰면
1) vw만 사용 (clamp 없이)
h1 { font-size: 5vw; }- 1920px 화면:
96px(너무 큼). - 320px 화면:
16px(제목치고 너무 작음). - 사용자 줌 무시.
→ clamp(1.5rem, 5vw, 4rem)처럼 경계를 잡는다.
2) MIN/MAX에 vw 단위
font-size: clamp(2vw, 3vw, 5vw); /* 의미 없음 */- MIN/MAX 자체가 vw → 작은 화면에선 MIN도 작아진다.
→ MIN/MAX는 절대 단위(rem/px), IDEAL만 vw/cqi.
3) Container 설정 없이 cqi
.card h1 { font-size: clamp(1rem, 4cqi, 2rem); }
/* .card에 container-type 없음 */cqi가 가장 가까운 viewport로 fallback (브라우저별 다름).
→ 반드시 부모에 container-type: inline-size.
4) em 누적
:root { font-size: 1.25em; }
.large { font-size: 1.25em; /* 부모의 1.25em = 1.5625em */ }
.large .larger { font-size: 1.25em; /* 1.95em */ }→ 누적 폭발. 항상 rem.
5) Fluid한 line-height
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
line-height: 4vw; /* 망함 — 폰트보다 작아질 수도 */
}→ line-height는 단위 없는 숫자. 폰트와 함께 자동 비례.
Insight — 흥미로운 이야기
“clamp는 2020년 도착, 4년 만에 미디어 쿼리를 절반으로 줄였다”
2020년 Chrome 79, Safari 13.1, Firefox 75에
clamp()가 동시에 들어갔다. 같은 해 4월 CSS Tricks에 “Linearly Scale font-size with CSS clamp() Based on the Viewport”라는 글이 올라왔고, 곧 utopia.fyi가 등장하며 clamp 계산기가 디자이너의 표준 도구가 됐다. 2023년 통계로 상위 1000개 사이트의 60%가clamp()사용 — 가장 빠르게 채택된 CSS 함수 중 하나. 미디어 쿼리는 죽지 않았지만, 폰트 크기에서만은 멸종에 가까워졌다.
“container query는 1999년부터 외친 꿈이었다”
CSS-Tricks의 Chris Coyier가 “Element Queries” (지금의 container query)를 2010년부터 주장해 왔다. 답답한 이유는 브라우저 구현이 무한 루프 위험을 안고 있었기 때문 — 컨테이너가 자식의 크기에 영향받고 자식이 다시 컨테이너에 의존하면 순환 의존. 2021년 Miriam Suzanne(CSSWG 멤버)이
container-type: inline-size라는 해결책을 설계 — 컨테이너의 인라인 폭만 쿼리 대상으로 한정해 사이클을 차단. 2022년 Chrome 105, 2023년 Safari 16에 도착. 12년 만에 등장한 표준이 미디어 쿼리의 패러다임을 컴포넌트 단위로 바꿨다.
요약 + Mermaid
- 유체 타이포 공식:
clamp(MIN, IDEAL = a*rem + b*vw|cqi, MAX). - MIN/MAX는 절대 단위, IDEAL만 동적 단위.
vw(사이트 전체) →cqi(컴포넌트) 로 패러다임 이동.- Modular scale + clamp = fluid modular scale (연속 + 비율).
- 반드시
rem기반 — 접근성 줌 보장.
다음 문서 → 07-opentype-features: 폰트가 이미 가진 미세 조판 기능들.