Share one CSS file between LTR and RTL pages by setting HTML direction explicitly, using logical properties for directional spacing, and using gap between layout items. This guide builds a local two-page example you can compare in a Mac browser.

The task is to make the same small component keep its intended spacing when its inline direction changes. You need a plain-text editor and a current desktop browser. The example uses synthetic labels and a decorative circle, with no framework, external assets, or JavaScript.

What should the shared stylesheet control?

Give the HTML responsibility for direction and the stylesheet responsibility for spacing. Both pages load the same CSS resource and use the same component classes; the page root supplies either dir="ltr" or dir="rtl". HTML defines rel="stylesheet" as an external resource link contributing to document styling. HTML Standard: stylesheet links.

Use HTML dir when the direction is known. Descendants generally inherit direction, and lang does not set it. Prefer explicit direction over dir="auto" for this controlled comparison: automatic direction depends on the content and would make the Latin sample labels choose LTR. MDN: the dir attribute.

The useful distinction is between a physical edge and a position in the content flow. In this example’s horizontal-tb writing mode, inline start means left in LTR and right in RTL. Block start remains the top. Logical properties express those relationships directly. MDN: logical margins, borders, and padding.

Intended spacing Shared CSS choice Result when direction changes
Space inside the component’s leading edge padding-inline-start Moves to the other physical side
Space inside both inline edges padding-inline Applies start and end values in the new direction
Space between Flexbox items gap Keeps the specified inter-item gap
Space above and below in this horizontal layout padding-block Remains above and below
A margin before an element in the inline flow margin-inline-start Maps to the new inline-start side

The margin and padding mappings follow the element’s writing mode and direction. gap instead creates gutters in supported layout containers. These are separate jobs: use padding around content and a gap between sibling items. MDN: logical spacing, MDN: gap.

Build two local pages that use one CSS file

Create a folder named spacing-demo on your Mac. Save the following files as plain text with their exact extensions. Avoid an editor that adds .txt after .html or .css.

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

Save this complete HTML document as ltr.html. Then save a second copy as rtl.html, changing only the root attribute from dir="ltr" to dir="rtl". Keep the stylesheet reference and component markup identical. The labels remain English because this is a synthetic layout comparison.

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

Now save the following complete stylesheet as components.css. The deliberately unequal inline padding makes direction changes easy to inspect: the larger inset should follow inline start.

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

Here, padding-inline: 24px 8px means start padding followed by end padding. It does not mean left followed by right. A single value would apply to both inline edges. MDN: padding-inline.

Keep flex-direction: row in both pages. A Flexbox row already follows its container’s direction: it runs left to right in LTR and right to left in RTL. Adding row-reverse to the RTL version would reverse that direction again. MDN: flex-direction.

The neutral circle has no directional meaning, so its drawing stays unchanged while its position follows the row. The Latin labels also keep their letter order; this example checks component layout rather than translated text.

How do you verify that spacing is actually shared?

Open both HTML files in your Mac browser and compare them at the same window width. The following values are expected from the declarations, not reported browser measurements. The card’s border is separate from its padding.

Check LTR page RTL page
Circle position First item on the left First item on the right
Card’s left padding 24px 8px
Card’s right padding 8px 24px
Gap between adjacent flex items 16px 16px
Card’s top and bottom padding 16px 16px
Space before the description 8px above 8px above

Test resource sharing separately from direction. Change --space-medium in components.css from 16px to 20px, save, and reload both pages. Both should gain the larger inter-item gap and larger block padding because those declarations reference the same variable. Restore 16px after the comparison.

Then narrow the browser window and replace the description with a longer synthetic label, such as Module A extended sample description. Check that the text wraps without covering the circle or tag. This adds a useful second test: a component can mirror correctly at one width while becoming cramped at another.

Do not judge every visible distance by gap alone. Item margins, internal padding, and distributed alignment can make the visible separation larger than the declared gutter. The sample resets the text margins and avoids distributed alignment so the comparison stays readable. MDN: gap description.

How do you migrate an existing component without spacing conflicts?

Start with the meaning of each existing declaration. A leading text inset usually belongs to inline start; an intentionally fixed physical edge may still need a physical property. Convert according to the design’s intent rather than replacing every occurrence of left mechanically.

For a component like this sample, use this sequence:

  1. Identify the ancestor that supplies direction.
  2. Replace directional interior spacing with logical padding.
  3. Replace directional exterior spacing with logical margins where needed.
  4. Use the parent’s gap for ordinary spacing between flex items, removing margins that previously supplied that same space.
  5. Repeat the unequal-padding and narrow-window comparisons in both directions.

Check for old physical declarations and later shorthands during the migration. Logical and physical properties that map to the same side participate in the cascade together; a logical property does not automatically win because it is logical. A later padding declaration can reset padding previously assigned through logical properties when the other cascade factors are equal. CSS Logical Properties and Values Level 1: box model properties.

Also keep the direction boundary in mind. A nested component with its own dir="ltr" should use LTR mappings even inside an RTL page. Conversely, margin-inline itself is not inherited, so give each component the spacing declarations it needs rather than expecting an ancestor’s margin to transfer to it. MDN: direction inheritance, MDN: margin-inline formal definition.

Which browser limits affect this approach?

MDN marks padding-inline and margin-inline as widely available across browsers since April 2021. That supports their use in current desktop browsers, but it is not a promise about every older browser or every newer syntax shown on those reference pages. This example uses ordinary lengths and auto, without newer sizing functions. MDN: padding-inline, MDN: margin-inline.

Flexbox gap has a separate history. Safari added it in version 14.1; Grid gap had already been supported since Safari 12. WebKit specifically notes that a property feature query could not distinguish those contexts, so @supports (gap: 16px) alone is insufficient to detect Flexbox gap support in older Safari. WebKit: New WebKit Features in Safari 14.1.

If your target browser supports logical margins but lacks Flexbox gap, append this replacement fragment after the main stylesheet rules. It supplies spacing for this specific single-row component. Use it instead of the active gap spacing, as shown; it is not a general solution for wrapping rows.

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

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

For browsers lacking logical properties too, this fallback is insufficient. Use a current browser for the local task, or author and test explicit physical LTR and RTL rules for the older target. Keep that browser-specific decision separate from the shared stylesheet’s main design.

The completed comparison should show one consistent contract: HTML supplies direction, the shared CSS supplies spacing, and the component’s leading inset follows inline start. Keep the two local pages as a compact regression fixture whenever you change the component’s spacing or layout.

Sources