HTML 방향을 명시적으로 설정하고, 방향에 따른 간격에는 논리 속성을 사용하며, 레이아웃 항목 사이에는 gap을 사용해 LTR 및 RTL 페이지에서 CSS 파일 하나를 공유하세요. 이 가이드에서는 Mac 브라우저에서 비교할 수 있는 로컬 2페이지 예제를 만듭니다.

목표는 인라인 방향이 바뀌어도 동일한 작은 컴포넌트가 의도한 간격을 유지하게 하는 것입니다. 일반 텍스트 편집기와 최신 데스크톱 브라우저가 필요합니다. 이 예제는 가상의 레이블과 장식용 원을 사용하며, 프레임워크, 외부 리소스 또는 JavaScript는 사용하지 않습니다.

공유 스타일시트는 무엇을 제어해야 하나요?

방향은 HTML이 담당하고 간격은 스타일시트가 담당하게 하세요. 두 페이지 모두 동일한 CSS 리소스를 불러오고 같은 컴포넌트 클래스를 사용합니다. 페이지 루트에는 dir="ltr" 또는 dir="rtl"을 지정합니다. HTML은 rel="stylesheet"을 문서 스타일에 기여하는 외부 리소스 링크로 정의합니다. HTML 표준: 스타일시트 링크.

방향을 알고 있다면 HTML dir을 사용하세요. 자손 요소는 일반적으로 방향을 상속하며, lang은 방향을 설정하지 않습니다. 이처럼 통제된 비교에서는 dir="auto"보다 명시적인 방향 지정을 우선하세요. 자동 방향은 콘텐츠에 따라 달라지므로 라틴 문자 샘플 레이블의 방향을 LTR로 결정하게 됩니다. MDN: dir 속성.

물리적인 가장자리와 콘텐츠 흐름에서의 위치를 구분하는 것이 중요합니다. 이 예제의 horizontal-tb 쓰기 모드에서 인라인 시작은 LTR에서는 왼쪽, RTL에서는 오른쪽을 뜻합니다. 블록 시작은 계속 위쪽입니다. 논리 속성은 이러한 관계를 직접 표현합니다. MDN: 논리 여백, 테두리 및 패딩.

의도한 간격 공유 CSS 선택 방향이 바뀔 때의 결과
컴포넌트의 시작 가장자리 안쪽 간격 padding-inline-start 반대쪽 물리적 면으로 이동
양쪽 인라인 가장자리 안쪽 간격 padding-inline 새 방향에 시작 및 끝 값을 적용
Flexbox 항목 사이 간격 gap 지정한 항목 간 간격을 유지
가로 레이아웃에서 위아래 간격 padding-block 위아래 위치를 유지
인라인 흐름에서 요소 앞의 여백 margin-inline-start 새 인라인 시작 쪽에 대응

여백 및 패딩의 대응 위치는 요소의 쓰기 모드와 방향을 따릅니다. 반면 gap은 지원되는 레이아웃 컨테이너에 거터를 만듭니다. 이 둘은 서로 다른 역할입니다. 콘텐츠 둘레에는 패딩을, 형제 항목 사이에는 gap을 사용하세요. MDN: 논리 간격, MDN: gap.

CSS 파일 하나를 사용하는 로컬 페이지 두 개 만들기

Mac에 spacing-demo이라는 폴더를 만드세요. 다음 파일을 정확한 확장자를 유지해 일반 텍스트로 저장하세요. .html 또는 .css 뒤에 .txt를 덧붙이는 편집기는 피하세요.

spacing-demo/
  ltr.html
  rtl.html
  components.css

다음 HTML 문서 전체를 ltr.html로 저장하세요. 그런 다음 두 번째 사본을 rtl.html로 저장하면서 루트 속성만 dir="ltr"에서 dir="rtl"로 바꾸세요. 스타일시트 참조와 컴포넌트 마크업은 동일하게 유지하세요. 이 예제는 가상의 레이아웃 비교이므로 레이블은 영어로 둡니다.

<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Component spacing sample</title>
  <link rel="stylesheet" href="components.css">
</head>
<body>
  <main class="demo">
    <h1>Spacing sample</h1>
    <article class="sample-card">
      <span class="sample-card__shape" aria-hidden="true"></span>
      <div class="sample-card__copy">
        <h2 class="sample-card__title">Module A</h2>
        <p class="sample-card__description">A compact sample tile</p>
      </div>
      <span class="sample-card__tag">Set B</span>
    </article>
  </main>
</body>
</html>

이제 다음 스타일시트 전체를 components.css로 저장하세요. 인라인 패딩 값에 일부러 차이를 둬 방향 변경을 쉽게 확인할 수 있습니다. 더 큰 안쪽 여백이 인라인 시작을 따라가야 합니다.

:root {
  --space-small: 8px;
  --space-medium: 16px;
  --space-large: 24px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 24px;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  color: #243447;
  background: #f2f5f8;
}

.demo {
  writing-mode: horizontal-tb;
  max-width: 640px;
  margin-inline: auto;
}

.sample-card {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-medium);
  padding-block: var(--space-medium);
  padding-inline: var(--space-large) var(--space-small);
  border: 1px solid #8594a5;
  border-radius: 12px;
  background: #ffffff;
}

.sample-card__shape {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #5477a3;
}

.sample-card__copy {
  flex: 1 1 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

.sample-card__title,
.sample-card__description {
  margin: 0;
}

.sample-card__title {
  font-size: 18px;
}

.sample-card__description {
  margin-block-start: var(--space-small);
}

.sample-card__tag {
  flex: 0 0 auto;
  padding-block: 4px;
  padding-inline: var(--space-small);
  border: 1px solid #8594a5;
  border-radius: 6px;
}

여기서 padding-inline: 24px 8px은 시작 패딩 다음에 끝 패딩을 의미합니다. 왼쪽 다음 오른쪽이라는 뜻이 아닙니다. 값을 하나만 지정하면 양쪽 인라인 가장자리에 모두 적용됩니다. MDN: padding-inline.

두 페이지 모두 flex-direction: row를 유지하세요. Flexbox의 행은 이미 컨테이너 방향을 따릅니다. LTR에서는 왼쪽에서 오른쪽으로, RTL에서는 오른쪽에서 왼쪽으로 진행합니다. RTL 버전에 row-reverse를 추가하면 방향이 다시 뒤집힙니다. MDN: flex-direction.

중립적인 원에는 방향성이 없으므로 원의 모양은 바뀌지 않고 위치만 행을 따라갑니다. 라틴 문자 레이블도 글자 순서를 유지합니다. 이 예제는 번역된 텍스트가 아니라 컴포넌트 레이아웃을 확인합니다.

간격이 실제로 공유되는지 어떻게 확인하나요?

Mac 브라우저에서 두 HTML 파일을 열고 같은 창 너비로 비교하세요. 다음 값은 실제 브라우저 측정값을 보고한 것이 아니라 선언에서 예상되는 값입니다. 카드의 테두리는 패딩과 별개입니다.

확인 항목 LTR 페이지 RTL 페이지
원 위치 왼쪽 첫 항목 오른쪽 첫 항목
카드 왼쪽 패딩 24px 8px
카드 오른쪽 패딩 8px 24px
인접한 Flexbox 항목 사이 간격 16px 16px
카드 위아래 패딩 16px 16px
설명 앞 간격 위쪽 8px 위쪽 8px

리소스 공유 여부는 방향 확인과 별도로 테스트하세요. components.css에서 --space-medium을 16px에서 20px로 바꾸고 저장한 다음 두 페이지를 새로고침하세요. 두 페이지 모두 항목 간 간격과 블록 패딩이 커져야 합니다. 두 선언 모두 같은 변수를 참조하기 때문입니다. 비교 후에는 16px로 되돌리세요.

그런 다음 브라우저 창을 좁히고 설명을 Module A extended sample description 같은 더 긴 가상 레이블로 바꾸세요. 텍스트가 원이나 태그를 덮지 않고 줄바꿈되는지 확인하세요. 이렇게 하면 두 번째 유용한 테스트도 할 수 있습니다. 컴포넌트는 한 너비에서는 올바르게 대칭을 이루면서 다른 너비에서는 답답하게 보일 수 있습니다.

눈에 보이는 모든 간격을 gap만으로 판단하지 마세요. 항목 여백, 내부 패딩, 분산 정렬 때문에 실제로 보이는 간격이 선언된 거터보다 커질 수 있습니다. 샘플은 텍스트 여백을 초기화하고 분산 정렬을 피하므로 비교를 쉽게 볼 수 있습니다. MDN: gap 설명.

간격 충돌 없이 기존 컴포넌트를 어떻게 옮기나요?

먼저 기존 선언의 의미를 파악하세요. 앞쪽 텍스트 안쪽 여백은 보통 인라인 시작에 해당합니다. 의도적으로 고정한 물리적 가장자리에는 물리 속성이 여전히 필요할 수 있습니다. 모든 left 항목을 기계적으로 바꾸지 말고 디자인의 의도에 따라 변환하세요.

이 샘플과 같은 컴포넌트라면 다음 순서로 진행하세요.

  1. 방향을 제공하는 상위 요소를 찾습니다.
  2. 방향에 따른 내부 간격을 논리 패딩으로 바꿉니다.
  3. 필요한 경우 방향에 따른 외부 간격을 논리 여백으로 바꿉니다.
  4. Flexbox 항목 사이의 일반적인 간격에는 부모의 gap을 사용하고, 같은 간격을 제공하던 기존 여백은 제거합니다.
  5. 양쪽 방향에서 값이 다른 패딩과 좁은 창 너비 비교를 반복합니다.

마이그레이션 중에는 기존 물리 선언과 뒤에 오는 단축 속성을 확인하세요. 같은 면에 대응하는 논리 속성과 물리 속성은 함께 캐스케이드에 참여합니다. 논리 속성이라고 해서 자동으로 우선 적용되지는 않습니다. 다른 캐스케이드 조건이 같다면 뒤에 오는 padding 선언이 논리 속성으로 지정한 패딩을 초기화할 수 있습니다. CSS 논리 속성 및 값 레벨 1: 박스 모델 속성.

방향 경계도 고려하세요. 자체 dir="ltr"이 있는 중첩 컴포넌트는 RTL 페이지 안에 있어도 LTR 대응 규칙을 사용해야 합니다. 반대로 margin-inline 자체는 상속되지 않으므로 상위 요소의 여백이 컴포넌트에 전달될 것으로 기대하지 말고 각 컴포넌트에 필요한 간격 선언을 지정하세요. MDN: 방향 상속, MDN: margin-inline 공식 정의.

이 방식에 영향을 주는 브라우저 지원 한계는 무엇인가요?

MDN에 따르면 padding-inline 및 margin-inline은 2021년 4월부터 여러 브라우저에서 널리 지원됩니다. 따라서 최신 데스크톱 브라우저에서 사용할 수 있지만, 참조 페이지에 표시된 모든 구형 브라우저나 최신 문법까지 지원한다는 보장은 아닙니다. 이 예제는 일반 길이 단위와 auto을 사용하며, 최신 크기 지정 함수는 사용하지 않습니다. MDN: padding-inline, MDN: margin-inline.

Flexbox gap은 지원 이력이 다릅니다. Safari 14.1에서 추가됐으며, Grid gap은 그보다 앞선 Safari 12부터 지원됐습니다. WebKit은 속성 기능 쿼리로는 두 문맥을 구분할 수 없다고 명시했습니다. 따라서 @supports (gap: 16px)만으로는 구형 Safari의 Flexbox gap 지원 여부를 감지할 수 없습니다. WebKit: Safari 14.1의 새로운 WebKit 기능.

대상 브라우저가 논리 여백은 지원하지만 Flexbox gap은 지원하지 않는다면, 기본 스타일시트 규칙 뒤에 다음 대체 구문을 추가하세요. 이 구문은 이 특정 단일 행 컴포넌트의 간격을 제공합니다. 표시된 방식대로 활성 gap 간격 대신 사용하세요. 여러 행으로 줄바꿈되는 경우를 위한 일반적인 해결책은 아닙니다.

/* Optional single-row replacement for Flexbox gap. */
.sample-card {
  gap: 0;
}

.sample-card > * + * {
  margin-inline-start: var(--space-medium);
}

논리 속성도 지원하지 않는 브라우저에는 이 대체 방법으로 충분하지 않습니다. 로컬 작업에는 최신 브라우저를 사용하거나 구형 대상 브라우저용으로 명시적인 물리 LTR 및 RTL 규칙을 작성하고 테스트하세요. 브라우저별 결정은 공유 스타일시트의 기본 설계와 분리해 두세요.

완성된 비교에서 일관된 규칙을 확인할 수 있어야 합니다. HTML은 방향을 제공하고, 공유 CSS는 간격을 제공하며, 컴포넌트의 시작 안쪽 여백은 인라인 시작을 따릅니다. 컴포넌트의 간격이나 레이아웃을 변경할 때마다 회귀 테스트용으로 이 로컬 페이지 두 개를 간단한 테스트 자료로 사용하세요.

출처