Accueil / Articles / Longueur de ligne, échelles d’espacement, surfaces sombres, ombres et anneaux de mise au point en CSS

Longueur de ligne, échelles d’espacement, surfaces sombres, ombres et anneaux de mise au point en CSS

Apprenez les principes fondamentaux du CSS qui sous-tendent des interfaces soignées : longueurs de ligne basées sur ch, une échelle d’espacement de 4 px, des surfaces sombres en couches, des ombres multicouches et des anneaux visibles lors du focus.

1903 mots

Contrôler la longueur des lignes avec les unités ch

Un texte qui occupe toute la largeur d’un écran de 1440 px est l’un des signes les plus évidents d’une interface utilisateur non affinée. Les lignes trop longues sont difficiles à lire : dès qu’une ligne dépasse environ 80 caractères, l’œil doit revenir jusqu’au bord gauche, et il atterrit souvent sur la mauvaise ligne lorsqu’il redescend. Sur une page longue, ce réajustement constant est épuisant.

Généralement, le problème se présente ainsi : un conteneur de largeur totale sans aucun élément limitant le paragraphe :

{/* BAD: Unbounded text stretches across the whole viewport */}
<div className="w-full p-8">
  <h1 className="text-3xl font-bold">API Documentation</h1>
  <p className="text-slate-300 mt-4 text-base">
    Our platform enables developers to authenticate and stream webhook events in real-time... (stretches 1200px wide)
  </p>
</div>

La solution : ajuster la taille des blocs de texte en fonction du nombre de caractères

CSS dispose d’une unité conçue pour ce problème. Une ch a la même largeur que le caractère « 0 » de la police, ce qui permet d’ajuster la taille en fonction du nombre de caractères par ligne, indépendamment de la taille de police. Une lecture confortable correspond généralement à un nombre de caractères par ligne compris entre 45 et 75. Une classe de prose réutilisable peut combiner une limite basée sur ch, une hauteur de ligne suffisante et un espacement entre les lettres légèrement réduit :

/* Clean readable prose container */
.prose-container {
  max-width: 68ch; /* Optimal line length regardless of font size */
  line-height: 1.65;
  letter-spacing: -0.01em;
}

Au sein de Tailwind, l’outil max-w-prose offre une limite similaire (65ch), et mx-auto centre la colonne :

{/* Enterprise Grade: Beautiful, focused reading experience */}
<div className="max-w-prose mx-auto px-6 py-12">
  <h1 className="text-3xl font-bold tracking-tight text-white">
    API Documentation
  </h1>
  <p className="mt-4 text-slate-300 text-base leading-relaxed">
    Our platform enables developers to authenticate and stream webhook events in real-time...
  </p>
</div>

En limitant la documentation, les articles de blog et les textes des paramètres descriptifs à environ 65 à 70ch, ces pages paraissent immédiatement équilibrées et accueillantes. Appliquez cette règle aux blocs de texte, et non à l’ensemble du layout.

Utilisez une échelle de 4px/8px pour tous les espaces

Examinez les classes utilitaires dans une base de code qui semble désordonnée et vous trouverez souvent des valeurs comme celles-ci :

  • Épaisseur d’espacement des cartes : p-[18px]
  • Marge d’un modal : mt-5 (20px)
  • Épaisseur d’espacement des boutons : px-3.5 py-[7px]
  • Espacement entre les sections : gap-7 (28px)

Chacune de ces valeurs a été choisie parce qu’elle semblait appropriée sur un écran à un moment donné, mais ensemble elles détruisent le rythme spatial. Les gens perçoivent des intervalles cohérents même s’ils ne les remarquent pas, et lorsque les valeurs d’espacement n’ont aucun rapport entre elles, le layout paraît encombré et désorganisé.

Une échelle de tokens à adopter

Les systèmes de conception établis limitent les espacements à des multiples de 4 ou 8 pixels et attribuent un nom ainsi qu’une fonction à chaque étape. Une échelle pratique ressemble à ceci :

  • space-1 (4px) : espace micro, comme l’écart entre une icône et son étiquette
  • space-2 (8px) : marge compacte pour les badges et les petites étiquettes
  • space-3 (12px) : marge interne des champs de formulaire
  • space-4 (16px) : marge standard pour les cartes et les boutons
  • space-6 (24px) : espaces entre les cartes
  • space-8 (32px) : séparation entre les sections
  • space-12 (48px) : séparation entre les grands blocs du tableau de bord

Tout ce qui dépasse cette échelle, comme margin-top: 19px, doit être considéré comme un défaut plutôt que comme un choix de style. L’échelle par défaut de Tailwind utilise déjà des pas de 4px, il est donc conseillé en pratique d’éviter les valeurs arbitraires.

Évitez les arrière-plans entièrement noirs en mode sombre

Une première approche courante pour une interface SaaS sombre consiste à mettre la page en noir pur avec du texte blanc pur :

/* The Harsh Dark Mode Trap */
body {
  background-color: #000000;
  color: #ffffff;
}

Le blanc sur noir offre le rapport de contraste maximal possible, soit 21:1. Cela dépasse facilement les exigences minimales en matière d’accessibilité, mais à un degré aussi extrême, le texte brillant peut sembler s’imprimer ou briller sur l’arrière-plan (halo), ce qui fatigue de nombreux lecteurs, en particulier ceux atteints d’asthigmatisme, surtout sur les écrans OLED et à fort contraste.

Le problème majeur est la profondeur. Les surfaces plus proches du spectateur ou de la source lumineuse ont naturellement un aspect légèrement plus brillant. Si la couche de base est déjà #000000, il n’y a plus de marge pour distinguer une carte d’un menu déroulant ou d’une fenêtre modale, car le noir ne peut pas devenir plus sombre en dessous d’elles.

Créer une échelle de profondeur pour les surfaces

Préférez plutôt des tons sombres profonds et légèrement teintés, en augmentant la luminosité à mesure que les éléments s’élevent. La première partie de l’ensemble de tokens définit quatre niveaux de surface, allant du canevas de la page aux éléments superposés tels que les modaux et les tooltips :

:root {
  /* Slate / Charcoal Depth Stack */
  --bg-canvas: #090d16;   /* Deepest background */
  --bg-surface: #0f172a;  /* Cards, tables, sidebar */
  --bg-elevated: #1e293b; /* Dropdowns, popovers, active tabs */
  --bg-overlay: #334155;  /* Modals, tooltips */

Le reste du même bloc :root ajoute deux degrés d’opacité pour les bordures translucides et trois niveaux de texte, allant des titres jusqu’aux timestamps et aux icônes inactives. Notez que, dans l’extrait présenté, les déclarations --border-active et --text-primary se trouvent sur la même ligne ; c’est un CSS valide mais qui mériterait d’être reformatté, et #f8fafc correspond simplement à une couleur presque blanche plutôt qu’à du blanc à 95 % d’opacité :

  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-active: rgba(255, 255, 255, 0.16);  --text-primary: #f8fafc;   /* 95% opacity white for headings */
  --text-secondary: #94a3b8; /* Muted slate for body text */
  --text-tertiary: #64748b;  /* Inactive icons, timestamps */
}

Lorsqu’il est appliqué au markup, le canevas se trouve en bas, la carte utilise la couleur de surface avec un bordure subtile, tandis que les titres et le texte principal utilisent les tokens de texte primaire et secondaire :

{/* Clean, layered elevation */}
<div className="bg-[var(--bg-canvas)] min-h-screen p-8">
  <div className="bg-[var(--bg-surface)] border border-[var(--border-subtle)] rounded-xl p-6 shadow-sm">
    <h2 className="text-[var(--text-primary)] font-semibold">
      Workspace Overview
    </h2>
    <p className="text-[var(--text-secondary)] text-sm mt-1">
      Manage team roles and API keys.
    </p>
  </div>
</div>

La hiérarchie s’appuie désormais uniquement sur la luminosité, sans besoin de ombres denses.

Remplacer une ombre dense par plusieurs ombres claires

Les interfaces amateurs ont tendance à utiliser une seule ombre sombre et floue :

/* BAD: One thick, dark, muddy shadow */
.card-bad {
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
}

Le résultat ressemble à un effet de retouche photo dépassé. Les objets réels ne projettent pas une flouité uniforme. Une ombre physique combine deux composantes :

  • une ombre nette et à fort contraste juste à côté de l’objet, produite par la source lumineuse principale
  • une ombre large et douce qui s’estompe progressivement dans l’environnement, appelée occlusion ambiante

Superposer des ombres avec de faibles valeurs alpha

box-shadow accepte une liste séparée par des virgules, ce qui vous permet de superposer deux ou trois ombres, chacune ayant une faible valeur alpha. Ici, une ombre de 1px ancre l’élément, un flou moyen ajoute une profondeur ambiante, et une couche large et très douce étend l’effet :

/* Polished Enterprise Shadow */
.card-elevation-high {
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.06),   /* Crisp grounding line */
    0 8px 16px rgba(0, 0, 0, 0.08),  /* Middle ambient blur */
    0 24px 48px rgba(0, 0, 0, 0.12); /* Soft dispersed glow */
}

Sur des arrière-plans sombres, les ombres sont difficiles à voir, il leur faut donc une opacité plus élevée, et un fin bord lumineux permet de bien séparer l’élément. Les valeurs négatives d’étalement empêchent que l’ombre sombre ne s’étende au-delà des bords :

/* In dark mode, pair subtle shadow with a crisp top border */
.card-dark-elevation {
  box-shadow:
    0 20px 25px -5px rgba(0, 0, 0, 0.5),
    0 8px 10px -6px rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

Plutôt que d’avoir un motif découpé posé sur du noir, l’élément semble flotter juste au-dessus de la toile.

N’effacez jamais les contours de focus sans en proposer un remplaçant

Une seule ligne peut causer plus de dommages que n’importe quelle autre dans les feuilles de style frontend :

/* DO NOT DO THIS */
*:focus {
  outline: none;
}

Les équipes l’ajoutent parce que le cercle de focus par défaut du navigateur entre en conflit avec les contrôles personnalisés. L’effacer sans proposer une alternative empêche les utilisateurs de clavier de savoir quel élément est en focus, ce qui rend l’interface pratiquement inutilisable pour eux et viole les exigences d’accessibilité.

Préférez plutôt :focus-visible

Les navigateurs modernes prennent en charge :focus-visible, qui s’active uniquement lorsque le navigateur juge qu’un indicateur de focus est nécessaire, généralement lors de la navigation au clavier et non après un clic de souris. Cela vous permet d’ôter cet indicateur pour les utilisateurs de souris tout en le conservant pour ceux qui utilisent le clavier. La première étape consiste à supprimer le contour par défaut des boutons :

/* Remove ugly mouse clicks, preserve crystal-clear keyboard rings */
button:focus {
  outline: none;
}

La deuxième étape définit un indicateur personnalisé clair pour le focus au clavier :

button:focus-visible {
  outline: 2px solid #6366f1; /* Crisp indigo ring */
  outline-offset: 2px;
  border-radius: 6px;
}

Deux améliorations méritent d’être connues. Premièrement, une version plus sûre de la première règle est button:focus:not(:focus-visible), qui supprime le contour uniquement lorsque l’indicateur visible n’est pas nécessaire, permettant ainsi aux navigateurs ne prenant pas en charge :focus-visible de conserver leur contour par défaut. Deuxièmement, la propriété border-radius dans la règle de focus modifie la forme du bouton lorsqu’il est en état de focus ; si le bouton possède déjà des coins arrondis, il suffit d’omettre cette ligne, car les navigateurs modernes dessinent des contours suivant le rayon de l’élément.

Au sein de Tailwind, la même idée est exprimée à l’aide des variantes focus-visible:, qui ajoutent un contour, un décalage ainsi qu’une couleur de décalage adaptée au fond sombre :

<button className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 rounded-lg text-white font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-900 transition-all">
  Save Changes
</button>

Le comportement de outline-none chez Tailwind a changé entre les versions majeures (les nouvelles versions ajoutent outline-hidden), il convient donc de vérifier la version que vous utilisez. Dans tous les cas, les utilisateurs de la souris obtiennent des contrôles propres et ceux qui utilisent Tab voient toujours où se trouve le focus.

Liste de contrôle avant fusion

Vérifiez ces points avant de déployer une modification frontend :

  1. Largeur de lecture : les blocs de texte sont-ils limités à environ 50 à 75ch ?
  2. Espacement : tous les paddings, marges et espaces proviennent-ils d’une échelle stricte de 4px/8px ?
  3. Couches en mode sombre : les surfaces sont-elles composées de tons de charbon ou de slate superposés plutôt que du code brut #000000 ?
  4. Ombres : les ombres sont-elles douces, superposées et diffuses plutôt qu’un simple flou sombre ?
  • États de focus : est-il possible pour quelqu’un d’utiliser la touche Tab pour contrôler tout l’écran et de voir en permanence où se trouve le focus ?
  • Conclusion

    Le polish provient moins d’une intuition artistique que de contraintes cohérentes : des longueurs de ligne basées sur ch, une échelle de marge fixe, l’utilisation de la luminosité comme indicateur de profondeur dans les thèmes sombres, des ombres imitant la lumière réelle, ainsi que des anneaux de focus restant visibles. Chacune de ces règles est simple et facile à vérifier, ce qui permet de les appliquer au moyen de tokens, de règles de linting et d’une liste de contrôle, plutôt que de compter sur le goût personnel à chaque demande de fusion.

    Lectures complémentaires

  • Neuf techniques de mode sombre comparées, des hacks de filtre aux cookies de serveur — Comparez neuf méthodes pour ajouter un mode sombre à une application web, allant des filtres inversés aux jetons, en passant par light-dark() et les cookies de serveur, et découvrez quels bugs chacune d’elles introduit discrètement.