MYMONDAY.ai
서비스스튜디오도구데이터·포맷73CSS 생성기19색상·이미지10인코딩·보안10웹·SEO11개발·데브옵스10파일·내보내기18계산기6가이드

9분 읽기 · 마지막 업데이트

CSS clamp() 유동 타이포그래피: 공식을 처음부터 유도하기

유동 타입은 여러 개의 브레이크포인트를 CSS 한 줄로 대신합니다. 그래프 위의 두 점에서 그 한 줄을 직접 유도해 보면, 생성기가 만든 값도 스스로 검산할 수 있습니다.

clamp()가 실제로 하는 일

clamp(최소값, 선호값, 최대값)은 선호값을 돌려주되 최소값보다 작아지지 않고 최대값보다 커지지 않게 막습니다. max(최소값, min(선호값, 최대값))의 줄임 표기입니다. 글자 크기에서 핵심은 선호값입니다. 뷰포트 너비에 비례해 커지는 식이어서, 브레이크포인트에서 툭 바뀌는 대신 최소와 최대 사이를 부드럽게 오갑니다.

선호값은 직선입니다. 모든 직선은 절편 + 기울기 * x로 쓸 수 있고, CSS에서 x는 100vw로 표현하는 뷰포트 너비입니다. 그래서 선호값은 항상 A + Bvw 꼴이 됩니다. A는 길이(절편), B는 뷰포트 너비에 대한 백분율(기울기)입니다. 이 글의 나머지는 네 개의 숫자로 A와 B를 계산하는 과정입니다.

두 점에서 공식 유도하기

가장 작은 글자 크기와 가장 큰 글자 크기(minSize, maxSize), 그리고 각각이 적용될 뷰포트 너비(minVw, maxVw)를 정합니다. 그래프 위의 두 점 (minVw, minSize)와 (maxVw, maxSize)를 정하는 셈이고, 두 점을 지나는 직선에는 기울기와 절편이 있습니다.

기울기는 (maxSize - minSize) / (maxVw - minVw)입니다. 뷰포트 1px당 글자가 몇 px 커지는지를 뜻합니다. CSS에서는 1vw가 너비의 1%이므로 여기에 100을 곱해 vw로 바꿉니다. 절편은 뷰포트 너비가 0일 때 직선이 놓일 값으로 minSize - 기울기 * minVw입니다. rem으로 쓰려면 16으로 나눕니다.

직선이 두 점을 정확히 지나므로 minVw와 maxVw 사이에서는 보간된 값이 그대로 나오고, 그 바깥에서만 clamp가 작동합니다.

기울기      = (maxSize - minSize) / (maxVw - minVw)
기울기(vw)  = 기울기 * 100                /* A + Bvw 의 B */
절편(px)    = minSize - 기울기 * minVw    /* A */
절편(rem)   = 절편(px) / 16

font-size: clamp(minSize, 절편(rem) + 기울기(vw)vw, maxSize);

예시 1: 본문을 16px에서 24px까지

목표는 320px 폰에서 16px, 1280px 데스크톱에서 24px인 본문입니다. 기울기는 (24 - 16) / (1280 - 320) = 8 / 960 = 0.008333, 즉 0.8333vw이고, 절편은 16 - 0.008333 * 320 = 13.3333px, 즉 0.8333rem입니다. 완성된 선언은 아래와 같습니다.

검산해 봅시다. 320과 1280의 한가운데인 800px 뷰포트에서는 글자 크기도 16과 24의 한가운데인 20px이어야 합니다. 식에 넣으면 13.3333 + 0.008333 * 800 = 13.3333 + 6.6667 = 20px로 맞습니다. 375px 폰에서는 16.46px이라, 가장 좁은 기준을 벗어나는 즉시 조금씩 커집니다.

font-size: clamp(1rem, 0.8333rem + 0.8333vw, 1.5rem);

예시 2: 제목을 32px에서 64px까지

제목은 보통 범위가 더 넓고, 둥근 숫자보다 실제 기기 폭에 맞춰 기준을 잡는 경우가 많습니다. 375px(흔한 폰 폭)에서 32px, 1440px(흔한 노트북 폭)에서 64px로 해 봅니다. 기울기는 32 / 1065 = 0.030047이므로 3.0047vw, 절편은 32 - 0.030047 * 375 = 20.7324px = 1.2958rem입니다.

아래 표는 이 선언 하나가 뷰포트 폭 여섯 곳에서 만들어 내는 값입니다. 마지막 행이 clamp의 효과입니다. 1440px를 넘으면 창이 아무리 넓어져도 64px에서 멈춥니다.

뷰포트 너비계산된 글자 크기이유
320px32pxminVw 미만: 2rem 하한에 걸림
375px32px정확히 minVw
768px43.81px20.7324 + 0.030047 * 768
1000px50.78px20.7324 + 0.030047 * 1000
1440px64px정확히 maxVw
1920px64pxmaxVw 초과: 4rem 상한에 걸림
font-size: clamp(2rem, 1.2958rem + 3.0047vw, 4rem);

최소·최대와 뷰포트 범위 고르기

눈으로 확인할 수 있는 양 끝에서 시작하세요. 지원할 가장 좁은 화면에서 적당해 보이는 크기와, 콘텐츠 컬럼이 더 이상 넓어지지 않는 화면에서 적당해 보이는 크기를 재면 그것이 minSize와 maxSize입니다. 뷰포트 범위도 임의의 숫자가 아니라 그 크기가 의미를 갖는 너비여야 합니다. 레이아웃이 1200px에서 더 커지지 않는데 maxVw를 1920으로 잡으면, 흔한 화면에서는 제목이 최댓값에 영영 도달하지 못합니다.

유동으로 만드는 속성은 적을수록 좋습니다. 제목과 섹션 간격은 폰과 데스크톱 사이에서 많이 달라지므로 유동으로 만들 가치가 큽니다. 본문은 범위가 2~4px이면 충분하고, 많은 팀이 1rem으로 고정해 둡니다. 라벨·캡션 같은 작은 UI 글자는 컨트롤이 흔들리지 않도록 대체로 고정하는 편이 낫습니다.

간격도 같은 산수입니다. 320px에서 16px, 1280px에서 48px로 변하는 섹션 패딩은 기울기가 32 / 960 = 3.3333vw, 절편이 16 - 0.033333 * 320 = 5.3333px = 0.3333rem이라 clamp(1rem, 0.3333rem + 3.3333vw, 3rem)이 됩니다. 800px에서 32px로, 예상대로 중간값입니다.

경계값은 rem, px 중 무엇으로

최소·최대는 rem으로 쓰세요. rem은 루트 글자 크기를 기준으로 하며 기본은 16px이지만 사용자가 브라우저 설정에서 바꿀 수 있습니다. 기본값을 20px로 올려 둔 독자는 경계가 rem일 때만 그에 비례해 큰 글자를 얻고, px 경계는 이 설정을 무시합니다. 글자 크기에 rem을 권하는 가장 큰 이유입니다.

절편도 같은 이유로 rem이 맞습니다. 뷰포트와 무관한 부분이므로 사용자의 기본 크기를 따라야 합니다. 뷰포트 단위가 허용되는 곳은 vw 항 하나뿐이고, 이 항은 글자만 키우는 설정으로는 커지지 않습니다. 이것이 다음에 볼 함정으로 이어집니다.

변환할 때 rem당 16px이라는 가정은 숫자를 계산하는 데만 쓰고, 결과물은 상대값으로 남습니다. 사용자 기본값이 20px이면 같은 선언이 그만큼 커집니다. html { font-size: 62.5% }로 1rem을 10px로 만들었다면 16 대신 10으로 다시 계산해야 합니다. 그렇지 않으면 크기가 1.6배 어긋납니다.

px 값rem 값(루트 16px)rem 값(루트 10px)
1611.6
241.52.4
3223.2
6446.4

WCAG 1.4.4 확대 함정

WCAG 2.2 성공 기준 1.4.4(텍스트 크기 조절, AA)는 보조 기술 없이도 텍스트를 200%까지 키울 수 있어야 하고, 그때 콘텐츠나 기능이 손실되지 않아야 한다고 요구합니다. 보통 브라우저 확대로 시험합니다. 유동 타입의 문제는 확대하면 CSS 픽셀로 잰 뷰포트가 줄어든다는 점입니다. 200% 확대에서 1280px 창은 640px 창처럼 동작합니다. vw에 의존하는 크기는 사용자가 확대할수록 오히려 줄어들어 확대 효과를 일부 상쇄합니다.

앞의 16px~24px 예시를 1280px 창에서 보면, 100%에서는 24px입니다. 200% 확대에서는 CSS 뷰포트가 640px이므로 clamp가 13.3333 + 0.008333 * 640 = 18.67px로 계산되고, 브라우저는 이를 두 배인 37.33 디바이스 픽셀로 그립니다. 원래 24px의 2배가 아니라 1.56배입니다. 고정값 1.5rem이었다면 48 디바이스 픽셀까지 커졌을 것입니다.

32px~64px 제목은 같은 방식으로 1.33배, 16px~40px 범위는 1.2배에 그칩니다. 최소와 최대 사이의 범위가 넓을수록 확대했을 때 덜 커집니다. 통과 여부는 맥락에 따른 판단이므로 직접 시험해야 합니다. 브라우저를 200%로 놓고 제목과 본문이 눈에 띄게 두 배로 커졌는지, 잘리는 곳이 없는지 확인하세요.

  • 본문은 rem 고정으로 두거나 1rem~1.125rem 같은 좁은 범위만 줍니다.
  • 유동 크기는 제목·히어로 텍스트·간격에 주로 쓰고, 최소와 최대의 비율은 적당히 유지합니다.
  • 최소·최대는 rem으로 써서 사용자의 기본 크기를 존중합니다.
  • 출시 전에 200%와 400%로 확대해 보고, 400%에서도 가로 스크롤 없이 재배치되는지(WCAG 1.4.10) 확인합니다.

디자인 시스템에 적용하기

유동 단계는 커스텀 속성으로 한 번만 정의해 재사용하세요. 흔한 구성은 5~7단계의 타입 스케일과 비슷한 길이의 간격 스케일이며, 단계마다 자기 clamp() 값을 갖습니다. 도구로 계산하면 옮겨 적다 생기는 실수를 피할 수 있고, :root에 모아 두면 한 곳만 고쳐 사이트 전체를 바꿀 수 있습니다.

글자 크기에는 알맞은 줄 간격과 줄 길이를 함께 정합니다. 단위 없는 line-height(본문 1.5, 큰 제목 1.1~1.25)는 clamp()가 만든 크기를 그대로 따라 커집니다. max-width: 65ch로 한 줄을 대략 45~75자로 제한하면 글자가 커져도 줄이 지나치게 길어지지 않습니다.

현재 주요 브라우저는 모두 clamp()를 지원하므로 일반적인 환경에서는 대체 선언이 필요 없습니다. 아주 오래된 엔진까지 지원해야 한다면 clamp() 선언 앞에 font-size: 1.25rem 같은 일반 선언을 두세요. clamp()를 해석하지 못하는 브라우저는 두 번째 줄을 무시하고 첫 줄을 씁니다.

:root {
  --step-0: clamp(1rem, 0.9583rem + 0.2083vw, 1.125rem);
  --step-3: clamp(2rem, 1.2958rem + 3.0047vw, 4rem);
  --space-section: clamp(1rem, 0.3333rem + 3.3333vw, 3rem);
}
h1 { font-size: var(--step-3); line-height: 1.1; }
section { padding-block: var(--space-section); }

완성된 유동 타입 스케일 계산하기

전체 스케일은 같은 산수를 단계마다 반복한 것일 뿐입니다. 320px과 1280px 뷰포트에서의 크기로 정의한 다섯 단계를 잡고 단계별로 직선을 계산합니다. 아래 표는 앞 절의 공식으로 만들었고, 마지막 열은 중간 지점인 800px 뷰포트에서 각 단계가 도달하는 크기이므로 최소와 최대의 평균과 비교해 검산할 수 있습니다.

기울기가 크기 범위에 비례해 커진다는 점에 주목하세요. 본문은 960px의 뷰포트 구간에서 2px만 바뀌지만, 가장 큰 제목은 17px이 바뀝니다. 유동 타입의 핵심이 이것입니다. 큰 글자는 키울 여유도, 작은 화면에서 잃을 것도 많아서 조정 폭의 대부분을 가져가고, 본문은 편안한 기본 크기 근처에 머뭅니다.

단계최소 / 최대(px)clamp() 값800px에서
본문16 / 18clamp(1rem, 0.9583rem + 0.2083vw, 1.125rem)17px
제목 420 / 24clamp(1.25rem, 1.1667rem + 0.4167vw, 1.5rem)22px
제목 325 / 32clamp(1.5625rem, 1.4167rem + 0.7292vw, 2rem)28.5px
제목 231 / 42clamp(1.9375rem, 1.7083rem + 1.1458vw, 2.625rem)36.5px
제목 139 / 56clamp(2.4375rem, 2.0833rem + 1.7708vw, 3.5rem)47.5px

clamp()가 이상하게 동작할 때 점검할 실수

깨진 유동 타입의 대부분은 몇 가지 실수에서 나옵니다. 공식이 틀렸다고 단정하기 전에 이 순서로 확인하세요.

  • 기울기에 100을 곱하지 않은 경우. 기울기 0.008333을 0.008333vw로 쓰면 100배 작아서 크기가 최솟값을 벗어나지 못합니다.
  • 최소와 최대를 뒤바꾼 경우. clamp(MIN, x, MAX)는 max(MIN, min(x, MAX))이므로 MIN이 MAX보다 크면 결과는 항상 MIN이고 선호값은 무시됩니다.
  • 기준이 섞인 경우. 루트 크기가 16px인 사이트에서 절편은 px로 계산해 놓고 rem 변환은 10으로 나누면 크기가 1.6배 어긋납니다.
  • 식 안의 +, - 양옆 공백을 빼먹은 경우. CSS는 이 두 연산자 양옆에 공백을 요구하므로 0.8333rem+0.8333vw는 올바르지 않고 선언 전체가 버려집니다.
  • 레이아웃과 맞지 않는 뷰포트 범위. 콘텐츠 컬럼이 1100px에서 멈추는데 maxVw가 1920이면, 더 커지지 않는 컬럼 안에서 제목만 계속 커집니다.
  • 좁은 컬럼 안의 컴포넌트에 vw를 쓰는 경우. cqi 같은 컨테이너 쿼리 길이 단위는 창이 아니라 컨테이너 너비를 따르므로 최신 브라우저에서 카드나 사이드바에 알맞습니다.

직접 해보기