Używaj tego samego pliku CSS na stronach LTR i RTL, jawnie ustawiając kierunek w HTML, stosując właściwości logiczne do odstępów zależnych od kierunku oraz używając gap między elementami układu. W tym przewodniku utworzysz lokalny przykład z dwiema stronami, które możesz porównać w przeglądarce na Macu.

Celem jest zapewnienie temu samemu niewielkiemu komponentowi zamierzonych odstępów po zmianie kierunku przepływu w tekście. Potrzebujesz edytora zwykłego tekstu i aktualnej przeglądarki na komputer. W przykładzie użyto wymyślonych etykiet i ozdobnego koła, bez frameworka, zewnętrznych zasobów ani JavaScriptu.

Co powinien kontrolować współdzielony arkusz stylów?

Przypisz HTML-owi odpowiedzialność za kierunek, a arkuszowi stylów za odstępy. Obie strony wczytują ten sam zasób CSS i używają tych samych klas komponentu; element główny strony określa dir="ltr" albo dir="rtl". HTML definiuje rel="stylesheet" jako odnośnik do zewnętrznego zasobu, który wpływa na stylizację dokumentu. Standard HTML: odnośniki do arkuszy stylów.

Gdy kierunek jest znany, używaj HTML-owego atrybutu dir. Elementy potomne zazwyczaj dziedziczą kierunek, a lang go nie ustawia. W tym kontrolowanym porównaniu wybierz jawne określenie kierunku zamiast dir="auto": automatyczny kierunek zależy od treści, więc łacińskie etykiety przykładowe spowodowałyby wybór LTR. MDN: atrybut dir.

Warto rozróżnić fizyczną krawędź od położenia w przepływie treści. W trybie pisma horizontal-tb z tego przykładu początek wiersza oznacza lewą stronę w LTR i prawą w RTL. Początek bloku pozostaje u góry. Właściwości logiczne wyrażają te zależności bezpośrednio. MDN: logiczne marginesy, obramowania i dopełnienia.

Zamierzony odstęp Wybór we współdzielonym CSS Wynik po zmianie kierunku
Odstęp wewnątrz krawędzi początkowej komponentu padding-inline-start Przenosi się na drugą fizyczną stronę
Odstęp wewnątrz obu krawędzi wiersza padding-inline Stosuje wartości początku i końca w nowym kierunku
Odstęp między elementami Flexboxa gap Zachowuje określony odstęp między elementami
Odstęp u góry i u dołu w tym układzie poziomym padding-block Pozostaje u góry i u dołu
Margines przed elementem w przepływie wiersza margin-inline-start Przypada na nową stronę początku wiersza

Odwzorowanie marginesów i dopełnień wynika z trybu pisma i kierunku elementu. Natomiast gap tworzy odstępy w obsługiwanych kontenerach układu. To odrębne zadania: używaj dopełnienia wokół zawartości, a odstępu między elementami potomnymi tego samego poziomu. MDN: logiczne odstępy, MDN: gap.

Utwórz dwie lokalne strony korzystające z jednego pliku CSS

Utwórz na Macu folder o nazwie spacing-demo. Zapisz poniższe pliki jako zwykły tekst, zachowując dokładnie podane rozszerzenia. Nie używaj edytora, który dopisuje .txt po .html lub .css.

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

Zapisz ten kompletny dokument HTML jako ltr.html. Następnie zapisz jego drugą kopię jako rtl.html, zmieniając tylko atrybut elementu głównego z dir="ltr" na dir="rtl". Pozostaw odwołanie do arkusza stylów i znaczniki komponentu bez zmian. Etykiety pozostają po angielsku, ponieważ to syntetyczne porównanie układu.

<!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>

Teraz zapisz poniższy kompletny arkusz stylów jako components.css. Celowo nierówne dopełnienie w wierszu ułatwia sprawdzenie zmiany kierunku: większe wcięcie powinno podążać za początkiem wiersza.

: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;
}

Tutaj padding-inline: 24px 8px oznacza dopełnienie od początku, a następnie od końca. Nie oznacza lewej, a potem prawej strony. Jedna wartość dotyczyłaby obu krawędzi wiersza. MDN: padding-inline.

Pozostaw flex-direction: row na obu stronach. Wiersz Flexboxa już podąża za kierunkiem kontenera: w LTR biegnie od lewej do prawej, a w RTL od prawej do lewej. Dodanie row-reverse do wersji RTL ponownie odwróciłoby ten kierunek. MDN: flex-direction.

Neutralne koło nie ma znaczenia kierunkowego, więc jego wygląd pozostaje bez zmian, a jego położenie podąża za wierszem. Etykiety zapisane alfabetem łacińskim również zachowują kolejność liter; ten przykład sprawdza układ komponentu, a nie przetłumaczony tekst.

Jak sprawdzić, czy odstępy są rzeczywiście współdzielone?

Otwórz oba pliki HTML w przeglądarce na Macu i porównaj je przy tej samej szerokości okna. Poniższe wartości wynikają z deklaracji — nie są pomiarami zgłoszonymi przez przeglądarkę. Obramowanie karty jest niezależne od jej dopełnienia.

Sprawdzenie Strona LTR Strona RTL
Położenie koła Pierwszy element po lewej Pierwszy element po prawej
Lewe dopełnienie karty 24px 8px
Prawe dopełnienie karty 8px 24px
Odstęp między sąsiednimi elementami Flexboxa 16px 16px
Górne i dolne dopełnienie karty 16px 16px
Odstęp przed opisem 8px u góry 8px u góry

Sprawdź współdzielenie zasobów niezależnie od kierunku. Zmień --space-medium w components.css z 16px na 20px, zapisz plik i wczytaj ponownie obie strony. Na obu powinien zwiększyć się odstęp między elementami oraz dopełnienie w kierunku bloku, ponieważ te deklaracje odwołują się do tej samej zmiennej. Po porównaniu przywróć wartość 16px.

Następnie zwęź okno przeglądarki i zastąp opis dłuższą, wymyśloną etykietą, na przykład Module A extended sample description. Sprawdź, czy tekst zawija się i nie zasłania koła ani znacznika. To dodatkowy przydatny test: komponent może poprawnie odbijać układ przy jednej szerokości, a przy innej być zbyt ciasny.

Nie oceniaj każdej widocznej odległości wyłącznie na podstawie gap. Marginesy elementów, wewnętrzne dopełnienie i wyrównanie rozdzielające mogą sprawić, że widoczny odstęp będzie większy niż zadeklarowana szczelina. W przykładzie wyzerowano marginesy tekstu i pominięto wyrównanie rozdzielające, by ułatwić porównanie. MDN: opis właściwości gap.

Jak przenieść istniejący komponent bez konfliktów odstępów?

Zacznij od ustalenia znaczenia każdej istniejącej deklaracji. Wcięcie tekstu od strony początkowej zwykle odpowiada początkowi wiersza; krawędź celowo przypisana do konkretnej fizycznej strony może nadal wymagać właściwości fizycznej. Konwertuj deklaracje zgodnie z zamierzeniem projektu, zamiast mechanicznie zastępować każde wystąpienie left.

W przypadku komponentu podobnego do tego przykładu wykonaj kolejno następujące kroki:

  1. Wskaż element nadrzędny określający kierunek.
  2. Zastąp zależne od kierunku odstępy wewnętrzne logicznym dopełnieniem.
  3. W razie potrzeby zastąp zależne od kierunku odstępy zewnętrzne logicznymi marginesami.
  4. Użyj właściwości gap elementu nadrzędnego do zwykłych odstępów między elementami Flexboxa i usuń marginesy, które wcześniej zapewniały ten sam odstęp.
  5. Powtórz porównania z nierównym dopełnieniem i zwężonym oknem dla obu kierunków.

W trakcie migracji sprawdź starsze deklaracje fizyczne i późniejsze skrócone zapisy. Właściwości logiczne i fizyczne odwzorowujące tę samą stronę uczestniczą razem w kaskadzie; właściwość logiczna nie wygrywa automatycznie tylko dlatego, że jest logiczna. Późniejsza deklaracja padding może zresetować dopełnienie przypisane wcześniej przez właściwości logiczne, jeśli pozostałe czynniki kaskady są równe. Właściwości i wartości logiczne CSS, poziom 1: właściwości modelu pudełkowego.

Pamiętaj też o granicy kierunku. Zagnieżdżony komponent z własnym dir="ltr" powinien korzystać z odwzorowania LTR nawet na stronie RTL. Z kolei samo margin-inline nie jest dziedziczone, więc przypisz każdemu komponentowi potrzebne deklaracje odstępów, zamiast oczekiwać, że margines elementu nadrzędnego przeniesie się na niego. MDN: dziedziczenie kierunku, MDN: formalna definicja margin-inline.

Jakie ograniczenia przeglądarek wpływają na to podejście?

Według MDN właściwości padding-inline i margin-inline są szeroko obsługiwane w przeglądarkach od kwietnia 2021 r. Pozwala to używać ich w aktualnych przeglądarkach na komputer, ale nie gwarantuje obsługi w każdej starszej przeglądarce ani każdej nowszej składni pokazanej na tych stronach referencyjnych. W tym przykładzie użyto zwykłych długości i auto, bez nowszych funkcji określania rozmiaru. MDN: padding-inline, MDN: margin-inline.

Odstęp Flexboxa ma odrębną historię. Safari dodało tę obsługę w wersji 14.1; obsługa odstępów w Grid była dostępna już od Safari 12. WebKit zwraca szczególną uwagę na to, że zapytanie o obsługę właściwości nie potrafiło rozróżnić tych kontekstów, więc samo @supports (gap: 16px) nie wystarcza do wykrycia obsługi odstępu Flexboxa w starszym Safari. WebKit: nowe funkcje WebKit w Safari 14.1.

Jeśli docelowa przeglądarka obsługuje logiczne marginesy, ale nie obsługuje odstępu Flexboxa, dodaj poniższy fragment zastępczy na końcu głównych reguł arkusza stylów. Zapewnia odstępy temu konkretnemu komponentowi w jednym wierszu. Użyj go zamiast aktywnego odstępu, jak pokazano; nie jest to ogólne rozwiązanie dla zawijanych wierszy.

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

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

Jeśli przeglądarka nie obsługuje również właściwości logicznych, ten fragment zastępczy nie wystarczy. Do lokalnego zadania użyj aktualnej przeglądarki albo utwórz i przetestuj jawne reguły fizyczne LTR i RTL dla starszej wersji docelowej. Decyzję dotyczącą konkretnej przeglądarki oddziel od głównego projektu współdzielonego arkusza stylów.

Końcowe porównanie powinno potwierdzić spójne założenie: HTML określa kierunek, współdzielony CSS określa odstępy, a wcięcie początkowe komponentu podąża za początkiem wiersza. Zachowaj obie lokalne strony jako niewielki zestaw regresyjny, który możesz wykorzystać przy każdej zmianie odstępów lub układu komponentu.

Źródła