Skip to main content

Chapitre 44 : Gestion de la directionnalité avec la propriété direction et la pseudo-classe :dir()

Support du RTL (Right-to-Left) et LTR (Left-to-Right) via CSS natif

L'internationalisation (i18n) d'une interface ne se limite pas à la traduction des textes. Pour des langues comme l'arabe, l'hébreu ou le persan, l'intégralité de la mise en page doit être inversée. C'est ce qu'on appelle le support du RTL (Right-to-Left).

Historiquement, les développeurs ajoutaient des classes comme .rtl sur la balise <body> et réécrivaient manuellement toutes les marges et positions. Aujourd'hui, le CSS moderne propose des outils natifs pour gérer cela de manière dynamique et accessible.

La propriété direction

1. Quoi

La propriété direction définit la direction de base du texte et l'alignement des éléments de bloc à l'intérieur d'un élément.

Elle accepte deux valeurs principales :

  • ltr (Left-to-Right) : Le texte s'écrit de gauche à droite (valeur par défaut pour la plupart des langues).
  • rtl (Right-to-Left) : Le texte s'écrit de droite à gauche.

Il est crucial de comprendre que direction est une propriété héritée. Si vous définissez direction: rtl sur l'élément <html> ou <body>, tous les enfants adopteront ce comportement, sauf s'ils redéfinissent leur propre direction.

2. Pourquoi

L'utilisation de direction est indispensable pour l'accessibilité (a11y). Un utilisateur dont la langue maternelle est le RTL s'attend à ce que :

  • La barre de navigation soit inversée.
  • Les icônes de "retour" pointent vers la droite au lieu de la gauche.
  • Le flux de lecture commence en haut à droite.

Ignorer la directionnalité crée une expérience utilisateur frustrante et peut rendre l'interface totalement illisible pour des millions d'utilisateurs.

3. Comment

A. Syntaxe de base

La propriété peut être appliquée directement en CSS, mais la bonne pratique recommande de définir la direction dans le HTML via l'attribut dir, le CSS ne faisant que compléter le style.


<html dir="rtl">
...
</html>
/* CSS pour forcer ou modifier la direction d'un bloc spécifique */
.arabic-text {
direction: rtl;
}

B. Cas concret : Mise en page bidirectionnelle

Pour qu'un site soit réellement compatible RTL, direction doit être couplée aux propriétés logiques (étudiées précédemment).

/* ❌ Mauvaise pratique : Valeurs physiques */
.card {
margin-left: 20px;
text-align: left;
border-left: 5px solid blue;
}

/* ✅ Bonne pratique : Valeurs logiques */
.card {
/* S'adapte automatiquement selon la valeur de 'direction' */
margin-inline-start: 20px;
text-align: start;
border-inline-start: 5px solid blue;
}

Dans ce second exemple, si direction est ltr, margin-inline-start devient margin-left. Si direction est rtl, il devient margin-right.

C. Limitations

La propriété direction ne change pas la direction des caractères eux-mêmes (c'est le rôle du moteur de rendu du navigateur et de l'algorithme Unicode), mais elle change la disposition des blocs.

Un piège courant est l'utilisation de direction: rtl sur un élément contenant un mélange de langues (ex: un texte arabe contenant un nom de marque en anglais). Dans ce cas, il faut utiliser la propriété unicode-bidi: bidi-override ou isolate pour éviter que les signes de ponctuation ne sautent d'un côté à l'autre.

4. Zone de Danger

Erreur commune : Utiliser text-align: right pour simuler du RTL. text-align ne fait que pousser le texte vers la droite, il ne change pas la direction du flux, ni la position des éléments de bloc, ni l'ordre des colonnes.

Bonne pratique : Utiliser l'attribut HTML dir="rtl" et les propriétés logiques CSS. Cela garantit que le navigateur traite correctement la sémantique et l'accessibilité.

Flux de détermination de la directionnalité CSS

La pseudo-classe :dir()

1. Quoi

La pseudo-classe :dir() est un sélecteur CSS moderne qui permet d'appliquer des styles spécifiquement lorsque la directionnalité d'un élément est ltr ou rtl.

Contrairement au sélecteur d'attribut [dir="rtl"], :dir(rtl) cible l'élément même si la direction est héritée du parent et non explicitement définie sur l'élément lui-même.

2. Pourquoi

Dans un projet complexe, certaines propriétés ne sont pas "logiques" par nature. Par exemple, une icône de flèche doit être physiquement inversée en dans un contexte RTL.

Comme il n'existe pas de transform-inline-start: rotate(180deg), nous avons besoin d'un moyen de détecter la direction pour appliquer une transformation spécifique.

3. Comment

A. Syntaxe de base

/* Cible tous les éléments en mode RTL, hérités ou non */
:dir(rtl) .icon-arrow {
transform: scaleX(-1);
}

/* Cible tous les éléments en mode LTR */
:dir(ltr) .icon-arrow {
transform: scaleX(1);
}

B. Cas concret : Interface de messagerie

Imaginons une bulle de chat où l'avatar doit changer de côté et l'ombre portée doit s'inverser.

.chat-bubble {
display: flex;
gap: 1rem;
padding: 1rem;
border-radius: 10px;
}

/* Style par défaut (LTR) */
.chat-bubble {
box-shadow: 5px 5px 10px rgba(0,0,0,0.1);
}

/* Adaptation spécifique pour le RTL */
:dir(rtl) .chat-bubble {
/* On inverse l'ombre car le flux visuel change */
box-shadow: -5px 5px 10px rgba(0,0,0,0.1);
}

/* Gestion d'une icône de validation */
.check-icon {
width: 20px;
height: 20px;
}

:dir(rtl) .check-icon {
/* On retourne l'icône pour qu'elle pointe dans le bon sens */
transform: rotate(180deg);
}

C. Limitations

Le support de :dir() est excellent dans les navigateurs modernes (Chrome, Edge, Safari), mais peut varier sur d'anciennes versions de Firefox. Pour une compatibilité maximale, certains développeurs utilisent encore un combo :

/* Fallback pour les anciens navigateurs */
[dir="rtl"] .element,
:dir(rtl) .element {
/* styles */
}

4. Zone de Danger

Erreur commune : Confondre :dir(rtl) et [dir="rtl"]. Si vous avez <body dir="rtl"><div class="content">...</div></body>, le sélecteur [dir="rtl"] .content fonctionnera, mais [dir="rtl"] appliqué directement sur .content échouera car la div n'a pas l'attribut explicitement. :dir(rtl) .content fonctionnera toujours.

Bonne pratique : Utiliser :dir() pour les ajustements visuels "physiques" (rotations, ombres, images miroirs) et les propriétés logiques pour tout le reste (marges, paddings, alignements).

Questions clés

1. Quelle est la différence fondamentale entre direction: rtl et text-align: right ?

Découvrir la réponse

direction: rtl change le flux global du document : le point de départ du texte, la position des barres de défilement, l'ordre des éléments de bloc et le comportement des propriétés logiques. text-align: right ne fait que justifier le texte vers la droite sans modifier la structure du document.

2. Pourquoi préférer :dir(rtl) au sélecteur d'attribut [dir="rtl"] ?

Découvrir la réponse

Parce que :dir() prend en compte l'héritage. Si la direction est définie sur la balise <html>, tous les éléments enfants sont considérés comme étant dans cette direction par :dir(), alors que [dir="rtl"] ne ciblerait que l'élément possédant physiquement l'attribut.

3. Comment direction influence-t-elle les propriétés logiques comme margin-inline-start ?

Découvrir la réponse

Les propriétés logiques sont des abstractions. margin-inline-start signifie "la marge au début de la ligne". En ltr, le début est à gauche. En rtl, le début est à droite. Le navigateur traduit donc automatiquement la propriété physique correspondante en fonction de la valeur de direction.

4. Est-il possible de mélanger LTR et RTL sur une même page ?

Découvrir la réponse

Oui. C'est fréquent dans les sites multilingues. On peut définir dir="rtl" sur un paragraphe spécifique ou une citation tout en gardant le reste de la page en dir="ltr".

Mise en pratique

Exercice 1 : Reproduction guidée

Niveau : Facile

Créez une carte de profil utilisateur simple.

  • Elle doit contenir un avatar et un texte.
  • Utilisez des propriétés logiques pour l'espacement.
  • Assurez-vous que si l'on change l'attribut dir de ltr à rtl sur le conteneur, l'avatar et le texte s'inversent naturellement.
Découvrir la solution commentée
<div class="profile-card" dir="ltr"> 
<img src="avatar.jpg" alt="Avatar" class="avatar" />
<div class="info">
<h2>Jean Dupont</h2>
<p>Développeur Web</p>
</div>
</div>
.profile-card {
display: flex;
align-items: center;
/* Utilisation de gap qui est nativement directionnel */
gap: 1rem;
padding: 1rem;
border: 1px solid #ccc;
/* On définit la direction de base, mais l'attribut HTML prime */
direction: ltr;
}

.avatar {
width: 50px;
height: 50px;
border-radius: 50%;
}

.info {
/* On utilise start pour que l'alignement suive la direction */
text-align: start;
}

Exercice 2 : Adaptation avec :dir()

Niveau : Intermédiaire

Vous avez un bouton "Suivant" avec une icône de flèche .

  • Le bouton doit être stylisé normalement en LTR.
  • En mode RTL, l'icône doit être inversée (effet miroir) et le bouton doit avoir une bordure colorée à droite au lieu de gauche.

HTML et CSS de départ :

<!-- Version LTR -->
<div class="container" dir="ltr">
<button class="btn-next">
<span>Suivant</span>
<span class="arrow"></span>
</button>
</div>

<!-- Version RTL — le bouton doit s'adapter -->
<div class="container" dir="rtl">
<button class="btn-next">
<span>Suivant</span>
<span class="arrow"></span>
</button>
</div>
/* CSS de départ — propriété physique qui ne s'adapte pas au RTL */
.btn-next {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-left: 4px solid blue; /* ❌ physique : ne bascule pas en RTL */
background: #f0f0f0;
cursor: pointer;
}

Ce que vous devez corriger :

  1. Remplacer border-left par la propriété logique équivalente pour que la bordure change automatiquement de côté en RTL.
  2. Ajouter une règle :dir(rtl) .arrow pour retourner l'icône (scaleX(-1)).
Découvrir la solution commentée
<div class="container" dir="rtl">
<button class="btn-next">
<span>Suivant</span>
<span class="arrow"></span>
</button>
</div>
.btn-next {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
/* Bordure logique : start = gauche en LTR, droite en RTL */
border-inline-start: 4px solid blue;
background: #f0f0f0;
cursor: pointer;
}

/* On utilise :dir(rtl) pour l'effet miroir de l'icône */
:dir(rtl) .arrow {
display: inline-block;
transform: scaleX(-1);
}

Exercice 3 : Conception de Dashboard i18n

Niveau : Avancé

Concevez la structure CSS d'une barre latérale (sidebar) de dashboard qui s'adapte automatiquement au LTR et au RTL.

Comportement attendu :

  • En LTR (dir="ltr") : la sidebar est collée à gauche, les icônes sont à gauche du texte, l'indicateur de sélection (bordure colorée) est sur le bord gauche de l'item.
  • En RTL (dir="rtl") : la sidebar bascule à droite, les icônes passent à droite du texte, l'indicateur de sélection est sur le bord droit.

Spécifications visuelles :

ÉlémentPropriétéValeur
bodydisplayflex
.sidebarwidth250px
.sidebarheight100vh
.sidebarbackground#2c3e50
.sidebarcolorwhite
.nav-itemdisplayflex
.nav-itemalign-itemscenter
.nav-itempadding1rem
.nav-itemgap10px
.nav-item:hoverbackground#34495e
.nav-item:hoverbordure logique de sélection4px solid #3498db sur inline-start
:dir(rtl) bodyflex-directionrow-reverse

HTML de départ :

<body dir="ltr">
<aside class="sidebar">
<nav>
<a href="#" class="nav-item">
<span class="icon">🏠</span>
<span class="label">Accueil</span>
</a>
<a href="#" class="nav-item">
<span class="icon">⚙️</span>
<span class="label">Paramètres</span>
</a>
</nav>
</aside>
<main class="content">
<h1>Contenu Principal</h1>
</main>
</body>
Tester les deux modes

Changez dir="ltr" en dir="rtl" sur <body> pour observer le basculement automatique. L'icône et l'indicateur de sélection devraient changer de côté sans aucune modification du CSS.

Découvrir la solution commentée
body {
display: flex;
margin: 0;
}

.sidebar {
width: 250px;
height: 100vh;
background: #2c3e50;
color: white;
/* Positionnement logique : inline-start place la sidebar
à gauche en LTR et à droite en RTL */
position: sticky;
left: 0; /* Note: 'left' est physique, mais en flexbox body,
l'ordre des éléments suit la direction */
}

/* En RTL, on doit s'assurer que le flux flex inverse l'ordre */
:dir(rtl) body {
flex-direction: row-reverse;
}

.nav-item {
display: flex;
align-items: center;
padding: 1rem;
text-decoration: none;
color: white;
gap: 10px;
}

/* L'ordre des éléments dans le flex-container (nav-item)
est automatiquement géré par la propriété 'direction'.
L'icône sera à gauche en LTR et à droite en RTL sans code additionnel. */

.nav-item:hover {
background: #34495e;
/* Bordure logique pour l'indicateur de sélection */
border-inline-start: 4px solid #3498db;
}