Condividere un file CSS tra pagine LTR e RTL impostando esplicitamente la direzione nell'HTML, usando proprietà logiche per la spaziatura direzionale e usando gap tra gli elementi di layout. Questa guida crea un esempio locale di due pagine che potete confrontare in un browser su Mac.

L'obiettivo è far sì che lo stesso piccolo componente mantenga la spaziatura prevista quando la sua direzione inline cambia. Servono un editor di testo semplice e un browser desktop aggiornato. L'esempio usa etichette sintetiche e un cerchio decorativo, senza framework, risorse esterne o JavaScript.

Cosa dovrebbe controllare il foglio di stile condiviso?

Affidate all'HTML la responsabilità della direzione e al foglio di stile la responsabilità della spaziatura. Entrambe le pagine caricano la stessa risorsa CSS e usano le stesse classi del componente; la radice della pagina fornisce dir="ltr" o dir="rtl". L'HTML definisce rel="stylesheet" come link a una risorsa esterna che contribuisce allo stile del documento. HTML Standard: link a fogli di stile.

Usate l'attributo HTML dir quando la direzione è nota. I discendenti generalmente ereditano la direzione, e lang non la imposta. Preferite una direzione esplicita a dir="auto" per questo confronto controllato: la direzione automatica dipende dal contenuto e farebbe scegliere LTR alle etichette di esempio latine. MDN: l'attributo dir.

La distinzione utile è tra un bordo fisico e una posizione nel flusso del contenuto. Nella horizontal-tb modalità di scrittura di questo esempio, l'inizio inline significa sinistra in LTR e destra in RTL. L'inizio del blocco rimane in alto. Le proprietà logiche esprimono direttamente queste relazioni. MDN: margini, bordi e padding logici.

Spaziatura prevista Scelta CSS condivisa Risultato quando la direzione cambia
Spazio all'interno del bordo iniziale del componente padding-inline-start Si sposta sull'altro lato fisico
Spazio all'interno di entrambi i bordi inline padding-inline Applica i valori di inizio e fine nella nuova direzione
Spazio tra elementi Flexbox gap Mantiene l'interspazio specificato tra gli elementi
Spazio sopra e sotto in questo layout orizzontale padding-block Rimane sopra e sotto
Un margine prima di un elemento nel flusso inline margin-inline-start Mappa sul nuovo lato inline-start

I mapping di margin e padding seguono la modalità di scrittura e la direzione dell'elemento. gap crea invece griglie nei contenitori di layout supportati. Sono compiti separati: usate il padding intorno al contenuto e un gap tra elementi fratelli. MDN: spaziatura logica, MDN: gap.

Costruire due pagine locali che usano un file CSS

Create una cartella chiamata spacing-demo sul vostro Mac. Salvate i seguenti file come testo semplice con le loro esatte estensioni. Evitate un editor che aggiunge .txt dopo .html o .css.

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

Salvate questo documento HTML completo come ltr.html. Poi salvate una seconda copia come rtl.html, cambiando solo l'attributo radice da dir="ltr" a dir="rtl". Mantenete identici il riferimento al foglio di stile e il markup del componente. Le etichette rimangono in inglese perché questo è un confronto di layout sintetico.

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

Ora salvate il seguente foglio di stile completo come components.css. Il padding inline deliberatamente diseguale rende facili da ispezionare i cambi di direzione: l'inset maggiore dovrebbe seguire l'inizio inline.

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

Qui, padding-inline: 24px 8px significa padding d'inizio seguito da padding di fine. Non significa sinistra seguita da destra. Un singolo valore si applicherebbe a entrambi i bordi inline. MDN: padding-inline.

Mantenete flex-direction: row in entrambe le pagine. Una riga Flexbox segue già la direzione del suo contenitore: va da sinistra a destra in LTR e da destra a sinistra in RTL. Aggiungere row-reverse alla versione RTL invertirebbe di nuovo quella direzione. MDN: flex-direction.

Il cerchio neutro non ha significato direzionale, quindi il suo disegno rimane invariato mentre la sua posizione segue la riga. Le etichette latine mantengono anche loro l'ordine delle lettere; questo esempio controlla il layout del componente piuttosto che il testo tradotto.

Come verificare che la spaziatura sia effettivamente condivisa?

Aprite entrambi i file HTML nel vostro browser Mac e confrontateli alla stessa larghezza della finestra. I seguenti valori sono attesi dalle dichiarazioni, non dalle misurazioni riportate dal browser. Il bordo della card è separato dal suo padding.

Controllo Pagina LTR Pagina RTL
Posizione del cerchio Primo elemento a sinistra Primo elemento a destra
Padding sinistro della card 24px 8px
Padding destro della card 8px 24px
Gap tra elementi flex adiacenti 16px 16px
Padding superiore e inferiore della card 16px 16px
Spazio prima della descrizione 8px sopra 8px sopra

Testate la condivisione delle risorse separatamente dalla direzione. Cambiate --space-medium in components.css da 16px a 20px, salvate e ricaricate entrambe le pagine. Entrambe dovrebbero acquisire il gap inter-elemento maggiore e il padding di blocco maggiore perché quelle dichiarazioni fanno riferimento alla stessa variabile. Ripristinate 16px dopo il confronto.

Poi restringete la finestra del browser e sostituite la descrizione con un'etichetta sintetica più lunga, come Module A extended sample description. Verificate che il testo vada a capo senza coprire il cerchio o il tag. Questo aggiunge un secondo test utile: un componente può rispecchiarsi correttamente a una larghezza mentre diventa stretto a un'altra.

Non giudicate ogni distanza visibile basandovi solo su gap. I margin degli elementi, il padding interno e l'allineamento distribuito possono rendere la separazione visibile maggiore del gutter dichiarato. L'esempio azzera i margin del testo ed evita l'allineamento distribuito per mantenere leggibile il confronto. MDN: descrizione di gap.

Come migrare un componente esistente senza conflitti di spaziatura?

Partite dal significato di ogni dichiarazione esistente. Un inset di testo iniziale di solito appartiene all'inizio inline; un bordo fisico intenzionalmente fisso potrebbe ancora aver bisogno di una proprietà fisica. Convertite secondo l'intento del design piuttosto che sostituire meccanicamente ogni occorrenza di left.

Per un componente come questo esempio, usate questa sequenza:

  1. Identificate l'antenato che fornisce la direzione.
  2. Sostituite la spaziatura interna direzionale con padding logico.
  3. Sostituite la spaziatura esterna direzionale con margin logici dove necessario.
  4. Usate il gap del genitore per la spaziatura ordinaria tra elementi flex, rimuovendo i margin che fornivano precedentemente quello stesso spazio.
  5. Ripetete i confronti con padding diseguale e finestra stretta in entrambe le direzioni.

Controllate le vecchie dichiarazioni fisiche e gli shorthand successivi durante la migrazione. Le proprietà logiche e fisiche che mappano allo stesso lato partecipano insieme alla cascata; una proprietà logica non vince automaticamente perché è logica. Una dichiarazione padding successiva può resettare un padding assegnato precedentemente tramite proprietà logiche quando gli altri fattori della cascata sono uguali. CSS Logical Properties and Values Level 1: proprietà del box model.

Tenete anche presente il confine di direzione. Un componente nidificato con il suo dir="ltr" dovrebbe usare mapping LTR anche dentro una pagina RTL. Al contrario, margin-inline non è ereditato, quindi date a ogni componente le dichiarazioni di spaziatura di cui ha bisogno invece di aspettarvi che il margin di un antenato si trasferisca a esso. MDN: ereditarietà della direzione, MDN: definizione formale di margin-inline.

Quali limiti dei browser influiscono su questo approccio?

MDN segna padding-inline e margin-inline come ampiamente disponibili nei browser da aprile 2021. Questo ne supporta l'uso nei browser desktop attuali, ma non è una promessa su ogni browser più vecchio o su ogni sintassi più recente mostrata in quelle pagine di riferimento. Questo esempio usa lunghezze ordinarie e auto, senza funzioni di dimensionamento più recenti. MDN: padding-inline, MDN: margin-inline.

Il gap Flexbox ha una storia separata. Safari l'ha aggiunto nella versione 14.1; il gap Grid era già supportato da Safari 12. WebKit nota specificamente che una feature query per proprietà non poteva distinguere quei contesti, quindi @supports (gap: 16px) da solo è insufficiente per rilevare il supporto del gap Flexbox in vecchi Safari. WebKit: New WebKit Features in Safari 14.1.

Se il vostro browser target supporta i margin logici ma manca del gap Flexbox, aggiungete questo frammento di sostituzione dopo le regole principali del foglio di stile. Fornisce la spaziatura per questo specifico componente a riga singola. Usatelo al posto della spaziatura gap attiva, come mostrato; non è una soluzione generale per righe che vanno a capo.

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

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

Per i browser che mancano anche delle proprietà logiche, questo fallback è insufficiente. Usate un browser attuale per l'attività locale, o create e testate regole LTR e RTL fisiche esplicite per il target più vecchio. Tenete quella decisione specifica del browser separata dal design principale del foglio di stile condiviso.

Il confronto completato dovrebbe mostrare un contratto coerente: l'HTML fornisce la direzione, il CSS condiviso fornisce la spaziatura, e l'inset iniziale del componente segue l'inizio inline. Mantenete le due pagine locali come un compatto fixture di regressione ogni volta che cambiate la spaziatura o il layout del componente.

Fonti