05 — Logical Properties (block-size · inline-start · inset)
한 줄 답: 물리축(
top/left/width/height)을 논리축(block-start/inline-start/inline-size/block-size)으로 추상화하면, RTL(아랍어·히브리어) · 세로쓰기(일본어·중국어 수직) 환경에서 스타일시트를 한 벌만 짜도 자동으로 뒤집힌다.
Why — 왜 logical properties인가
CSS는 1996년부터 영어 기준 LTR 가로쓰기를 가정해서 설계됐다. margin-left, padding-top 같은 이름은 물리적 방향에 박혀 있다. 문제는:
- RTL (Right-to-Left, 아랍어·히브리어): UI가 통째로 좌우 반전된다.
margin-left는 시작 마진이 아니라 끝 마진이 된다. - 세로쓰기 (일본어·중국어 전통 조판): 텍스트가 위→아래로 흐르고, 줄은 오른쪽에서 왼쪽으로.
width는 글의 길이가 아니라 글의 두께가 된다. - 인쇄 매체 / 페이지 회전: 90도 회전된 인쇄에서
width/height의 의미가 뒤집힌다.
/* 영어 LTR 가정의 옛 코드 */
.menu-item { padding-left: 16px; }
/* → 아랍어 RTL에서 메뉴 항목 패딩이 *오른쪽*에 와야 하는데 여전히 왼쪽 */
/* logical: 자동 대응 */
.menu-item { padding-inline-start: 16px; }
/* → LTR에서 left, RTL에서 right */결론: i18n(국제화)을 진지하게 하는 모든 제품은 logical properties로 작성해야 한다. 영어 전용이라도 옛 습관을 끊는 의미에서 모던 코드의 기본이다.
How — 어떻게 동작하나
1) 두 축 — Block axis와 Inline axis
- Block axis: 한 줄에서 다음 줄로 쌓이는 방향. 영어에서는 세로, 일본어 세로쓰기에서는 가로.
- Inline axis: 한 줄 안에서 글자가 흐르는 방향. 영어에서는 가로, 일본어 세로쓰기에서는 세로.
2) 물리축 ↔ 논리축 매핑표
| 물리축 | 논리축 (block-axis) | 논리축 (inline-axis) |
|---|---|---|
top | block-start (inset-block-start) | — |
bottom | block-end (inset-block-end) | — |
left | — | inline-start* (inset-inline-start) |
right | — | inline-end* (inset-inline-end) |
width | — | inline-size |
height | block-size | — |
margin-top | margin-block-start | — |
margin-left | — | margin-inline-start* |
padding-top | padding-block-start | — |
padding-left | — | padding-inline-start* |
border-top | border-block-start | — |
border-left | — | border-inline-start* |
*RTL이나 세로쓰기에서 inline-start는 자동으로 반대편으로 간다.
3) 단축 속성들
/* Margin */
.x { margin-block: 1rem; } /* top + bottom (logical) */
.x { margin-inline: 1rem; } /* left + right (logical) */
.x { margin-block: 1rem 2rem; } /* start=1, end=2 */
.x { margin-inline: 1rem auto; } /* start=1, end=auto */
/* Padding */
.x { padding-block: 1rem; padding-inline: 2rem; }
/* Inset (position offsets) */
.x { inset: 0; } /* top right bottom left = 0 */
.x { inset-block: 0; } /* top + bottom = 0 */
.x { inset-inline: 0 auto; } /* left=0 right=auto (LTR) */
/* Size */
.x { inline-size: 100%; } /* width */
.x { block-size: auto; } /* height */
.x { min-inline-size: 0; } /* min-width */
.x { max-block-size: 80vh; } /* max-height */4) writing-mode와 direction
:root { writing-mode: horizontal-tb; } /* 기본 — 영어 */
[lang="ar"] { direction: rtl; } /* 아랍어 — LTR 반전 */
[lang="ja"].vertical { writing-mode: vertical-rl; } /* 일본어 세로 */
[lang="zh"].vertical { writing-mode: vertical-rl; } /* 중국어 세로 */
[lang="mn"] { writing-mode: vertical-lr; } /* 몽골어 */writing-mode 값:
horizontal-tb(기본): 가로쓰기, 줄은 위→아래vertical-rl: 세로쓰기, 줄은 오른쪽→왼쪽 (일본어 전통)vertical-lr: 세로쓰기, 줄은 왼쪽→오른쪽 (몽골어)sideways-rl/sideways-lr(실험적): 글자도 90도 회전
direction 값:
ltr(기본)rtl
5) :dir() 가상클래스
.icon-arrow { transform: scaleX(1); }
.icon-arrow:dir(rtl) { transform: scaleX(-1); }
/* → RTL에서 화살표 아이콘 좌우 반전 */→ Logical properties로 안 잡히는 시각적 자산(아이콘, 차트)은 :dir()로 분기.
What — 구체 사양
Logical properties 호환성 (2026 baseline)
| 그룹 | 호환성 |
|---|---|
margin/padding-block/inline-* | 모든 브라우저 (2019+) |
inline-size, block-size | 모든 브라우저 (2019+) |
inset-*, inset 단축 | 모든 브라우저 (2021+) |
border-block/inline-* | 모든 브라우저 (2019+) |
text-align: start/end | 모든 브라우저 |
float: inline-start/inline-end | 모든 브라우저 (2021+) |
:dir() 가상클래스 | Chrome 89, Firefox 49, Safari 16.4 (2023 baseline) |
caption-side: inline-start/end | 모든 브라우저 |
→ 2026년 모든 신규 코드는 logical properties로 작성 가능.
text-align도 logical로
.x { text-align: start; } /* LTR에서 left, RTL에서 right */
.x { text-align: end; } /* 반대 */
/* left/right는 *실제 물리축*이므로 디자인 의도와 다를 수 있음 */border-radius의 logical 버전
.card {
border-start-start-radius: 8px; /* 시작-시작 모서리 */
border-start-end-radius: 8px;
border-end-start-radius: 0;
border-end-end-radius: 0;
}
/* LTR에서 위쪽 두 모서리만 둥글게.
RTL이어도 같은 시각적 의미 유지 */→ border-top-left-radius는 RTL에서 의미가 뒤집힌다.
inset 매트릭스 예
/* 박스를 오른쪽 위에 붙이기 */
.tooltip { position: absolute; inset-block-start: 0; inset-inline-end: 0; }
/* 물리축으로 표현하면: top: 0; right: 0 (LTR) / top: 0; left: 0 (RTL) */→ 시작점(*-start)과 끝점(*-end)으로 표현하면 언어 무관하다.
Flow-relative 단위들
| 단위 | 의미 |
|---|---|
1lh | line-height 1 (block direction) |
1rlh | root element의 line-height |
1vi | viewport inline size의 1% (= LTR에서 1vw) |
1vb | viewport block size의 1% (= LTR에서 1vh) |
→ 세로쓰기 환경에서 1vw가 글의 두께인지 글의 흐름인지 헷갈리는 문제를 1vi/1vb가 푼다.
What-if — 잘못 쓰면 / 적용하면
1) 물리축과 논리축을 섞어서 쓰면
.card {
margin-left: 1rem; /* 물리 */
padding-inline-start: 2rem; /* 논리 */
}
/* → LTR에서는 둘 다 왼쪽이라 OK
RTL에서는 margin-left는 그대로 왼쪽, padding-inline-start만 오른쪽 → 깨짐 */→ 컴포넌트 단위로 일관되게 한쪽만 쓸 것.
2) 디자인 토큰에 margin-left 변수
/* 안티패턴 */
:root { --gap-left: 16px; }
.x { margin-left: var(--gap-left); }
/* BP */
:root { --gap-inline-start: 16px; }
.x { margin-inline-start: var(--gap-inline-start); }→ 변수 이름부터 논리축으로.
3) RTL 대응 시 transform: scaleX(-1)을 통째로 거는 옛 패턴
/* 옛날 */
html[dir="rtl"] body { transform: scaleX(-1); }
html[dir="rtl"] .text { transform: scaleX(-1); } /* 글자는 다시 뒤집기 */→ 최악의 패턴. 클릭 좌표·스크롤·이미지 모두 깨진다. logical properties로 다시 짜라.
4) width: 100%가 세로쓰기에서 의미 헷갈림
/* 영어 가로쓰기 */
.x { width: 100%; } /* 글이 흐르는 방향(가로) 100% */
/* 일본어 세로쓰기 (vertical-rl) */
.x { width: 100%; } /* width는 *물리* 가로 → 글의 두께 100%? */
.x { inline-size: 100%; } /* inline은 *글이 흐르는 방향(세로)* 100% → 더 의도 명확 */→ 세로쓰기 지원이 필요하면 inline-size/block-size로 강제.
5) :dir()을 모르고 미디어 쿼리 분기
/* 안티패턴 */
[dir="rtl"] .x { ... } /* 정확하지만 길고 매번 써야 함 */
/* 더 깔끔 */
.x:dir(rtl) { ... }→ :dir()은 DOM 트리의 dir 속성을 따라간다 — 더 정확하다.
6) RTL 아이콘 모두 뒤집기
.icon { transform: scaleX(1); }
.icon:dir(rtl) { transform: scaleX(-1); }→ 방향성 있는 아이콘만 뒤집어야 한다 — 화살표, 진행 표시, 캘린더 등. 방향성 없는 아이콘 (가위, 카메라)은 안 뒤집어야 한다.
.icon-directional:dir(rtl) { transform: scaleX(-1); }
/* 또는 클래스 분리: */
.icon[data-flip-rtl]:dir(rtl) { transform: scaleX(-1); }Insight — 흥미로운 이야기
”1999년 W3C가 RTL을 거의 잊을 뻔했다”
CSS2 명세 작성 당시 RTL 지원은 후속 모듈로 미루자는 분위기였다. 다행히 W3C i18n WG가 “웹은 영어만의 매체가 아니다” 라며 강하게 반대했고, direction 속성과 unicode-bidi 알고리즘이 CSS2에 들어갔다.
하지만 logical properties는 무려 20년 뒤인 2018년에야 명세화됐다 (CSS Logical Properties Level 1). 그 사이 RTL 지원은 통째로 미러링 + 수많은 예외 처리라는 거대한 부담을 졌다.
한 페르시아 개발자의 회고: “우리는 영어 사이트를 RTL로 옮기는데 매번 2~3배의 시간을 썼다. logical properties가 그 비용을 1.1배로 줄였다."
"일본 신문 사이트는 왜 세로쓰기를 다시 도입했나”
2010년대 일본 디지털 미디어는 모바일 가로쓰기로 통일됐다. 하지만 2020년대 들어 “전통 인쇄와 같은 독서 경험” 을 원하는 독자층이 늘며, 아사히신문 디지털, NHK, 小説サイト(소설 사이트) 들이 writing-mode: vertical-rl로 세로쓰기 옵션을 부활시켰다.
이 흐름은 세로쓰기 + 한자 강조 + ルビ(루비/후리가나) 같은 동아시아 조판 패턴을 CSS WG가 다시 챙기게 만든 계기였다. CSS Ruby Module, CSS Writing Modes 3, CSS Text 4 등이 모두 이 시기에 나왔다.
”왜 inline-size라는 이름인가”
width 대신 inline-size를 쓰는 이유는 “이 박스의 글이 흐르는 방향의 크기” 라는 의미를 살리기 위해서다. 영어 가로쓰기에서는 가로 너비이지만, 일본어 세로쓰기에서는 세로 너비다.
이 이름 짓기는 “물리축은 매체의 우연이고, 논리축은 텍스트의 본질” 이라는 철학을 담는다 — 잘 지은 이름은 추상화를 가르친다.
”Mozilla의 -moz-margin-start가 표준의 씨앗이었다”
logical properties의 초안은 사실 Firefox가 2010년대 초부터 벤더 프리픽스(-moz-margin-start)로 실험하던 것이다. 그게 너무 유용해서 다른 브라우저도 따라했고, 결국 표준이 됐다. 벤더 프리픽스가 표준의 prototype 역할을 한 성공 사례.
요약 + 다이어그램
물리축(top/left/width)을 논리축(block-start/inline-start/inline-size)으로 갈아끼우면 RTL · 세로쓰기 자동 대응이 공짜로 따라온다. 2026년 baseline은 모든 logical properties와
:dir()을 포함한다. 디자인 토큰부터--gap-inline-start같은 논리 이름으로 작성하라.
다음 문서:
06-anchor-positioning.md— 위치 지정의 미래. CSS만으로 floating-ui를 대체한다.