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한 글자처럼 디자인해 둠 (liga feature).
  • 코드 폰트(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-*, 커스텀 featurefont-feature-settings.


What — 자주 쓰는 OpenType feature 목록

1) 숫자 관련

FeatureCSS 속성효과
tnumtabular-nums표 숫자 — 모든 숫자 폭 동일
pnumproportional-nums프로포셔널 — 글자별 자연 폭
onumoldstyle-nums옛 스타일 — descender 있는 숫자 (3, 4, 5, 7, 9)
lnumlining-nums라이닝 — 모든 숫자 같은 높이
fracdiagonal-fractions분수 — 1/2를 ½로
zeroslashed-zero0과 O 구분 — 슬래시 있는 0
.table-cell {
  font-variant-numeric: tabular-nums slashed-zero;
}
 
.book-body {
  font-variant-numeric: oldstyle-nums;  /* 본문엔 옛 스타일이 어울림 */
}

2) 합자 (Ligatures)

FeatureCSS 속성효과
ligacommon-ligatures일반 합자 (fi, fl) — 기본 ON
dligdiscretionary-ligatures선택적 합자 (ct, st) — 기본 OFF
hlighistorical-ligatures역사 합자 — 거의 OFF
caltcontextual문맥 합자 — 코드 폰트에서 ===를 ≡로
.brand {
  font-variant-ligatures: discretionary-ligatures;  /* fancy 브랜드 */
}
 
pre, code {
  font-variant-ligatures: contextual;  /* FiraCode === → ≡ */
}

3) 대체 글자 (Stylistic Sets, Alternates)

FeatureCSS 속성효과
ss01-ss20styleset(alt-N)폰트 제작자가 정의한 스타일 세트 (최대 20개)
saltstyleset()일반 대체 글자
cv01-cv99character-variant()개별 글자 대체
/* Inter의 ss01 = 단순한 G, ss02 = 다른 a 모양 */
.heading {
  font-feature-settings: "ss01", "ss02";
}
 
/* 또는 의미 기반 */
.heading {
  font-variant-alternates: styleset(alt-1, alt-2);
}

4) 대문자

FeatureCSS 속성효과
smcpsmall-caps소형 대문자 (소문자를 대문자처럼)
c2scall-small-caps모든 글자를 소형 대문자
casecase-sensitive대문자에 맞게 구두점 정렬
.label {
  font-variant-caps: small-caps;
  letter-spacing: 0.05em;
}

What — 한글에 유용한 feature

영문 위주의 feature 외에도 한글·CJK 용 feature가 있다.

Feature효과
paltProportional alternate widths — 한글 폭을 프로포셔널하게
vert세로쓰기 글자 (writing-mode: vertical-rl)
vkrn세로쓰기 커닝
haltAlternate 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 목록은 폰트마다 다르다. 확인 방법:

  1. 폰트 제작자 사이트 — Inter, Recursive, JetBrains Mono의 공식 문서.
  2. wakamaifondue.com — 폰트 파일을 올리면 모든 feature 목록 출력.
  3. 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.
  • 하지만 0O가 구분 안 됨 → 한 자리 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-level font-feature-settings (직접 태그).
  • 자주 쓰는 4가지: tnum (표 숫자), zero (슬래시 0), ss01-20 (스타일 세트), calt (코드 합자).
  • 한글에는 palt (프로포셔널 폭), vert (세로쓰기).
  • 항상 함께 적기 (덮어쓰기 함정) 또는 CSS 변수로 합성.

이로써 04-typography 챕터가 끝났다 — 폰트 자원부터 미세 조판까지 7층 스택을 모두 살폈다.

다음 챕터 → 05-color-visual: 이 글자 위에 색과 시각 효과가 어떻게 얹히는가.