RTL

RTL

Bootstrap에서 오른쪽에서 왼쪽(RTL) 방향을 지원하도록 설정하는 방법을 설명하는 문서예요. 필수 HTML, 접근 방식, 소스 커스터마이즈까지 다뤄요.

출처: 문서

본문

익숙해지기 (Get familiar)

먼저 Getting Started Introduction 페이지를 읽어서 Bootstrap에 익숙해지는 걸 권장해요. 그것을 살펴본 뒤, 여기로 돌아와서 RTL을 활성화하는 방법을 계속 읽어 보세요.

RTL 접근 방식을 뒷받침하는 RTLCSS 프로젝트에 대해서도 읽어 보면 좋아요.

Bootstrap의 RTL 기능은 아직 실험적이며 사용자 피드백에 따라 진화할 거예요. 뭔가 발견했거나 개선할 점이 있다면 이슈를 열어 주세요. 여러분의 의견을 듣고 싶어요.

필수 HTML (Required HTML)

Bootstrap 기반 페이지에서 RTL을 활성화하려면 두 가지 엄격한 요구사항이 있어요.

  • <html> 요소에 dir="rtl"을 설정하세요.
  • <html> 요소에 lang="ar" 같은 적절한 lang 속성을 추가하세요.

그리고 나서 RTL 버전의 CSS를 포함해야 해요. 예를 들어 RTL이 활성화된 컴파일·minify CSS의 스타일시트는 다음과 같아요.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.rtl.min.css" integrity="sha384-CfCrinSRH2IR6a4e6fy2q6ioOX7O6Mtm1L9vRvFZ1trBncWmMePhzvafv7oIcWiW" crossorigin="anonymous">

스타터 템플릿 (Starter template)

위 요구사항이 반영된 수정된 RTL 스타터 템플릿을 볼 수 있어요.

<!doctype html>
<html lang="ar" dir="rtl">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.rtl.min.css" integrity="sha384-CfCrinSRH2IR6a4e6fy2q6ioOX7O6Mtm1L9vRvFZ1trBncWmMePhzvafv7oIcWiW" crossorigin="anonymous">

    <title>مرحبًا بالعالم!</title>
  </head>
  <body>
    <h1>مرحبًا بالعالم!</h1>

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: Bootstrap Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>

    <!-- Option 2: Separate Popper and Bootstrap JS -->
    <!--
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-G/EV+4j2dNv+tEPo3++6LCgdCROaejBqfUeNjuKAiuXbjrxilcCdDz6ZAVfHWe1Y" crossorigin="anonymous"></script>
    -->
  </body>
</html>

RTL 예시 (RTL examples)

여러 RTL 예시 중 하나로 시작해 보세요.

접근 방식 (Approach)

Bootstrap에 RTL 지원을 구축하는 우리의 접근 방식에는 CSS 작성·사용 방식에 영향을 주는 두 가지 중요한 결정이 있어요.

  1. 먼저 RTLCSS 프로젝트로 구축하기로 했어요. 이는 LTR에서 RTL로 전환할 때 변경과 재정의를 관리할 수 있는 강력한 기능을 제공해요. 또한 하나의 코드베이스에서 두 가지 버전의 Bootstrap을 만들 수 있게 해 줘요.

  2. 둘째로, 논리 속성(logical properties) 접근 방식을 채택하도록 방향 관련 클래스 몇 개를 이름을 바꿨어요. 대부분 이미 flex 유틸리티 덕분에 논리 속성을 접해 봤을 거예요. left와 right 같은 방향 속성을 start와 end로 대체하죠. 이렇게 하면 추가 오버헤드 없이 클래스 이름과 값이 LTR과 RTL 모두에 적합해져요.

예를 들어 margin-left를 위한 .ml-3 대신 .ms-3을 사용하세요.

RTL 사용은 소스 Sass를 통해서든 컴파일된 CSS를 통해서든 기본 LTR과 크게 다르지 않을 거예요.

소스에서 커스터마이즈 (Customize from source)

커스터마이즈할 때 가장 선호하는 방법은 변수, 맵, 믹스인을 활용하는 거예요. 이 접근 방식은 RTLCSS의 동작 방식 덕분에 컴파일된 파일에서 후처리되더라도 RTL에서도 동일하게 동작해요.

커스텀 RTL 값 (Custom RTL values)

RTLCSS 값 지시어(value directives)를 사용하면 변수가 RTL에서는 다른 값을 출력하도록 할 수 있어요. 예를 들어 코드베이스 전체에서 $font-weight-bold의 굵기를 낮추려면 /*rtl: {value}*/ 구문을 사용할 수 있어요.

$font-weight-bold: 700 #{/* rtl:600 */} !default;

이렇게 하면 기본 CSS와 RTL CSS에 다음과 같이 출력돼요.

/* bootstrap.css */
dt {
  font-weight: 700 /* rtl:600 */;
}

/* bootstrap.rtl.css */
dt {
  font-weight: 600;
}

대체 폰트 스택 (Alternative font stack)

커스텀 폰트를 사용한다면 모든 폰트가 비라틴 알파벳을 지원하지 않는다는 점을 알아 두세요. 범유럽(Pan-European) 계열에서 아랍어 계열로 바꾸려면 폰트 스택에서 /*rtl:insert: {value}*/을 사용해서 폰트 패밀리 이름을 수정해야 할 수 있어요.

예를 들어 LTR에서는 Helvetica Neue 폰트를, RTL에서는 Helvetica Neue Arabic을 사용하도록 바꾸려면 Sass 코드가 이렇게 될 수 있어요.

$font-family-sans-serif:
  Helvetica Neue #{"/* rtl:insert:Arabic */"},
  // Cross-platform generic font family (default user interface font)
  system-ui,
  // Safari for macOS and iOS (San Francisco)
  -apple-system,
  // Chrome < 56 for macOS (San Francisco)
  BlinkMacSystemFont,
  // Windows
  "Segoe UI",
  // Android
  Roboto,
  // Basic web fallback
  Arial,
  // Linux
  "Noto Sans",
  // Sans serif fallback
  sans-serif,
  // Emoji fonts
  "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !default;

LTR과 RTL을 동시에 (LTR and RTL at the same time)

같은 페이지에서 LTR과 RTL을 모두 필요로 한다면 어떻게 할까요? RTLCSS String Maps 덕분에 이건 아주 간단해요. @import를 클래스로 감싸고 RTLCSS의 커스텀 rename 규칙을 설정하면 돼요.

/* rtl:begin:options: {
  "autoRename": true,
  "stringMap":[ {
    "name": "ltr-rtl",
    "priority": 100,
    "search": ["ltr"],
    "replace": ["rtl"],
    "options": {
      "scope": "*",
      "ignoreCase": false
    }
  } ]
} */
.ltr {
  @import "../node_modules/bootstrap/scss/bootstrap";
}
/*rtl:end:options*/

Sass 다음에 RTLCSS를 실행하면 CSS 파일의 각 선택자 앞에 .ltr이 붙고, RTL 파일에는 .rtl이 붙어요. 이제 같은 페이지에서 두 파일을 모두 사용할 수 있고, 컴포넌트 래퍼에 .ltr이나 .rtl을 사용하기만 하면 방향을 선택할 수 있어요.

LTR과 RTL을 결합해서 구현할 때 고려해야 할 엣지 케이스와 알려진 제약 사항은 다음과 같아요.

  • .ltr과 .rtl을 전환할 때는 그에 맞게 dir과 lang 속성을 추가해야 해요.
  • 두 파일을 모두 로드하는 것은 실제 성능 병목이 될 수 있어요. 최적화를 고려하고, 그중 하나를 비동기로 로드해 보세요.
  • 이렇게 스타일을 중첩하면 form-validation-state() 믹스인이 의도대로 동작하지 않아서 직접 조금 수정해야 할 거예요. #31223을 참고하세요.

한 스타일시트 안에서 두 방향을 다루는 여러 엣지 케이스를 처리하는 이 과정을 자동화하고 싶다면, PostCSS RTLCSS를 PostCSS 플러그인으로 사용해서 소스 파일을 처리하는 걸 고려해 보세요. PostCSS RTLCSS는 내부적으로 RTLCSS를 사용해서 방향 뒤집기 과정을 관리하지만, 뒤집힌 선언을 LTR과 RTL에 대해 서로 다른 접두사를 가진 규칙으로 분리해요. 그래서 같은 스타일시트 파일 안에서 두 방향을 모두 가질 수 있게 해 줘요. 이렇게 하면 페이지의 dir만 바꾸면(또는 플러그인을 그에 맞게 구성했다면 특정 클래스를 수정하기만 해도) LTR과 RTL 방향을 전환할 수 있어요.

PostCSS RTLCSS로 LTR과 RTL 결합 구현을 만들 때 유의해야 할 중요한 점은 다음과 같아요.

  • html 요소에 dir 속성을 추가하는 걸 권장해요. 이렇게 하면 방향을 바꿀 때 전체 페이지가 영향을 받아요. 또한 그에 맞게 lang 속성도 추가하세요.
  • 두 방향이 모두 들어 있는 단일 번들은 최종 스타일시트 크기를 늘려요(평균 20%~30%). 최적화를 고려해 보세요.
  • PostCSS RTLCSS는 어떤 CSS 규칙도 제거하지 않기 때문에 /* rtl:remove */ 지시어와 호환되지 않아요. /* rtl:remove */, /* rtl:begin:remove */, /* rtl:end:remove */ 지시어를 각각 /* rtl:freeze */, /* rtl:begin:freeze */, /* rtl:end:freeze */ 지시어로 교체해야 해요. 이 지시어들은 대상 규칙이나 선언에 현재 방향을 접두사로 붙이지만 RTL 대응 버전은 만들지 않아요(RTLCSS의 remove 지시어와 같은 결과예요).

breadcrumb 구분자는 $breadcrumb-divider를 기본값으로 하는 $breadcrumb-divider-flipped라는 완전히 새로운 전용 변수가 필요한 유일한 사례예요.

추가 리소스 (Additional resources)

더 알아보기 (Learn more)