07 — OpenType Features
폰트는 이미 가지고 있다, 우리가 켜지 않을 뿐이다. OpenType은 1996년부터 폰트 안에 연결 글자, 표 숫자, 스타일 세트, 작은 대문자 같은 미세 조판 규칙을 담아 왔다 — CSS 한 줄로 그 보물을 열 수 있다.
한 문장 답 (Pyramid Top)
OpenType feature는 폰트 제작자가 폰트 안에 미리 넣어둔 “조판 규칙” —
font-feature-settings: "tnum"한 줄로 표의 숫자를 가지런하게,"ss01"한 줄로 대체 글자 스타일을,"liga"로 연결 글자를 켤 수 있다. 그리고 High-level CSS 속성font-variant-numeric,font-variant-ligatures,font-variant-caps는 그 위에 의미 기반의 추상화를 제공한다.
Why — 왜 이게 필요한가
1) 표의 숫자가 가지런하지 않다
$ 123.45
$ 98.20
$1,234.56기본 폰트의 프로포셔널 숫자는 1과 8의 폭이 다르다. 표가 지그재그로 보임.
→ font-variant-numeric: tabular-nums 한 줄로 모든 숫자 폭이 동일 → 가지런.
2) 디자인 시스템의 작은 차이
- 헤더에서 G의 모양이 달랐으면 좋겠다. a는 둥근 형태가 더 좋겠다.
- Inter는 Stylistic Set 01 (
ss01)로 대체 G/R/a 를 제공한다 — 폰트 디자이너가 대안 글자를 미리 만들어 둠.
3) 합자 (ligature)
fi,fl같은 영문 글자 조합이 어색하게 겹친다 → 폰트는fi,fl을 한 글자처럼 디자인해 둠 (ligafeature).- 코드 폰트(
FiraCode,JetBrains Mono)는===,=>,<=등을 시각적 기호로 변환 (calt).
How — 두 가지 레벨의 CSS
Low-level: font-feature-settings
.heading {
font-feature-settings: "ss01", "tnum", "liga" off;
}- 4자리 OpenType 태그.
1/on(켜기, 기본),0/off(끄기).- 여러 feature 동시: 쉼표로.
High-level: font-variant-*
.heading {
font-variant-numeric: tabular-nums;
font-variant-ligatures: no-common-ligatures;
font-variant-alternates: styleset(alt-1);
font-variant-caps: small-caps;
}- 의미 기반의 추상화 — 폰트가 어떻게 구현했든 같은 결과.
- 폰트가 지원하지 않으면 조용히 무시 (browser fallback).
우선순위
font-variant-* 와 font-feature-settings 가 같이 있으면 → font-feature-settings가 이긴다 (low-level 우선).
→ 일반은 font-variant-*, 커스텀 feature는 font-feature-settings.
What — 자주 쓰는 OpenType feature 목록
1) 숫자 관련
| Feature | CSS 속성 | 효과 |
|---|---|---|
tnum | tabular-nums | 표 숫자 — 모든 숫자 폭 동일 |
pnum | proportional-nums | 프로포셔널 — 글자별 자연 폭 |
onum | oldstyle-nums | 옛 스타일 — descender 있는 숫자 (3, 4, 5, 7, 9) |
lnum | lining-nums | 라이닝 — 모든 숫자 같은 높이 |
frac | diagonal-fractions | 분수 — 1/2를 ½로 |
zero | slashed-zero | 0과 O 구분 — 슬래시 있는 0 |
.table-cell {
font-variant-numeric: tabular-nums slashed-zero;
}
.book-body {
font-variant-numeric: oldstyle-nums; /* 본문엔 옛 스타일이 어울림 */
}2) 합자 (Ligatures)
| Feature | CSS 속성 | 효과 |
|---|---|---|
liga | common-ligatures | 일반 합자 (fi, fl) — 기본 ON |
dlig | discretionary-ligatures | 선택적 합자 (ct, st) — 기본 OFF |
hlig | historical-ligatures | 역사 합자 — 거의 OFF |
calt | contextual | 문맥 합자 — 코드 폰트에서 ===를 ≡로 |
.brand {
font-variant-ligatures: discretionary-ligatures; /* fancy 브랜드 */
}
pre, code {
font-variant-ligatures: contextual; /* FiraCode === → ≡ */
}3) 대체 글자 (Stylistic Sets, Alternates)
| Feature | CSS 속성 | 효과 |
|---|---|---|
ss01-ss20 | styleset(alt-N) | 폰트 제작자가 정의한 스타일 세트 (최대 20개) |
salt | styleset() | 일반 대체 글자 |
cv01-cv99 | character-variant() | 개별 글자 대체 |
/* Inter의 ss01 = 단순한 G, ss02 = 다른 a 모양 */
.heading {
font-feature-settings: "ss01", "ss02";
}
/* 또는 의미 기반 */
.heading {
font-variant-alternates: styleset(alt-1, alt-2);
}4) 대문자
| Feature | CSS 속성 | 효과 |
|---|---|---|
smcp | small-caps | 소형 대문자 (소문자를 대문자처럼) |
c2sc | all-small-caps | 모든 글자를 소형 대문자 |
case | case-sensitive | 대문자에 맞게 구두점 정렬 |
.label {
font-variant-caps: small-caps;
letter-spacing: 0.05em;
}What — 한글에 유용한 feature
영문 위주의 feature 외에도 한글·CJK 용 feature가 있다.
| Feature | 효과 |
|---|---|
palt | Proportional alternate widths — 한글 폭을 프로포셔널하게 |
vert | 세로쓰기 글자 (writing-mode: vertical-rl) |
vkrn | 세로쓰기 커닝 |
halt | Alternate half widths |
.korean-display {
font-feature-settings: "palt"; /* 한글 글자 폭 자동 조정 */
}→ 큰 제목에서 한글의 여백을 줄여 더 밀집된 인상.
What — Code 폰트의 contextual alternates
FiraCode, JetBrains Mono, Cascadia Code 같은 폰트는 코드 기호를 합자로 표현.
pre, code {
font-family: "FiraCode", monospace;
font-feature-settings: "calt", "ss01", "ss02", "ss03";
/* 또는 */
font-variant-ligatures: contextual;
}===→≡=>→⇒!=→≠<=→≤->→→||→∨
호불호 강함 — 디버깅 시 진짜 글자 수가 헷갈릴 수 있어 시니어 개발자는 끄는 경우도 많다.
What — 완전한 BP
:root {
/* 1. 본문은 자연스러운 옛 스타일 숫자 */
font-variant-numeric: oldstyle-nums proportional-nums;
}
/* 2. 표·UI 숫자는 가지런하게 */
.table,
.metric,
.timestamp {
font-variant-numeric: tabular-nums slashed-zero;
}
/* 3. 헤더는 스타일 세트 활용 */
h1, h2 {
font-feature-settings: "ss01", "case";
}
/* 4. 작은 라벨은 소형 대문자 */
.label {
font-variant-caps: small-caps;
letter-spacing: 0.08em;
}
/* 5. 코드 폰트는 contextual */
pre, code {
font-feature-settings: "calt";
}What — 폰트가 어떤 feature를 지원하는가
폰트의 feature 목록은 폰트마다 다르다. 확인 방법:
- 폰트 제작자 사이트 — Inter, Recursive, JetBrains Mono의 공식 문서.
- wakamaifondue.com — 폰트 파일을 올리면 모든 feature 목록 출력.
- Figma·Sketch의 OpenType 패널 — UI에서 직접 확인.
예: Inter의 feature 목록
- 숫자:
tnum,pnum,onum,lnum,zero,frac - 합자:
liga,dlig,calt - 스타일 세트:
ss01(단순 G),ss02(단순 ą),ss03(대체 ł), …,ss08 case(대문자 인접 구두점 조정)cv01-cv11(개별 글자 대체)
What-if — 잘못 쓰면
1) font-feature-settings의 누적 함정
.a { font-feature-settings: "tnum"; }
.b { font-feature-settings: "ss01"; /* tnum 사라짐 */ }→ 모든 feature를 항상 다시 적어야. 또는 변수로 합성:
:root {
--feat: "tnum", "ss01";
}
.text { font-feature-settings: var(--feat); }2) font-variant-numeric: tabular-nums만 (slashed-zero 누락)
.code-input { font-variant-numeric: tabular-nums; }- 표 숫자는 OK.
- 하지만
0과O가 구분 안 됨 → 한 자리 ID·비밀번호에서 읽기 어려움.
→ tabular-nums slashed-zero 함께.
3) 폰트가 지원 안 하는 feature
.text { font-feature-settings: "tnum"; }
/* 폰트에 tnum이 없으면? 조용히 무시됨 (에러 없음) */- 침묵 실패. → 디자인 시스템에서 지원하는 폰트 명시.
4) 코드 합자가 디버깅 방해
== !== ===
↓
= ≠ ≡- 코드 리뷰에서
===가≡로 보여 문법 오해 가능. - → 시니어가
font-variant-ligatures: none으로 끄는 경우 빈번.
5) 본문에 tabular-nums
body { font-variant-numeric: tabular-nums; }→ 본문 안의 자연스러운 숫자 흐름이 지나치게 기계적. tabular는 표·UI 메트릭 전용.
Insight — 흥미로운 이야기
“OpenType은 Adobe + Microsoft의 정전 협정”
1990년대 폰트 포맷 전쟁 — Adobe의 PostScript Type 1 vs Apple/Microsoft의 TrueType. 1996년 Adobe와 Microsoft가 공동 표준에 합의 — OpenType (1996년 발표, 2007년 ISO 표준). OpenType의 두 핵심: ① Unicode 풀 지원 (한 폰트가 수만 자 글리프 보유 가능), ② feature tables (조판 규칙을 폰트 안에 박음). 이게 없었다면 같은 폰트로 영문, 한글, 아랍어, 수학 기호를 모두 그릴 수 없었을 것이다. OpenType feature는 30년 전 협정의 산물이고, CSS는 그걸 2010년대에야 노출했다.
“FiraCode는 Mozilla의 사이드 프로젝트였다”
2014년 Mozilla의 Nikita Prokopov가 “코드를 더 잘 읽을 수 없을까” 라는 개인 프로젝트로 시작. Mozilla의 Fira Sans Mono에 코드 기호 합자를 추가한 게 FiraCode. GitHub에 올렸더니 45,000 스타 — 가장 인기 있는 폰트 오픈소스 프로젝트 중 하나가 됐다. 이후 JetBrains Mono, Cascadia Code (Microsoft), Monaspace (GitHub) 가 같은 컨셉으로 출시. 2024년 모든 IDE의 기본 폰트가 contextual ligature 폰트 — 한 사람의 사이드 프로젝트가 개발자 도구의 표준 시각 언어를 바꿨다.
요약 + Mermaid
- OpenType feature = 폰트 안에 박혀 있는 조판 규칙.
- High-level
font-variant-*(의미 기반) + Low-levelfont-feature-settings(직접 태그). - 자주 쓰는 4가지:
tnum(표 숫자),zero(슬래시 0),ss01-20(스타일 세트),calt(코드 합자). - 한글에는
palt(프로포셔널 폭),vert(세로쓰기). - 항상 함께 적기 (덮어쓰기 함정) 또는 CSS 변수로 합성.
이로써 04-typography 챕터가 끝났다 — 폰트 자원부터 미세 조판까지 7층 스택을 모두 살폈다.
다음 챕터 → 05-color-visual: 이 글자 위에 색과 시각 효과가 어떻게 얹히는가.