Que fait Layout ?
Ce skill décide où placer les éléments, à quelle distance les séparer et quelle taille leur donner. Il prend les mêmes décisions dans le même ordre, et vérifie le résultat par rapport aux pixels rendus.
Avant toute décision, appliquez product-ui §1 (le journal de goût et la priorité) et §5 (quel skill possède quoi).
Décider dans cet ordre
- Shell. Nommez les régions de l'écran : barre latérale, barre d'outils, contenu, inspecteur. Chaque région est un propriétaire de défilement ou aucun. Détails et largeurs : shell-and-responsive.md. Pourquoi : le shell fixe toutes les largeurs dans lesquelles la mise en page intérieure doit s'inscrire.
- Largeur et mesure. Définissez la colonne de contenu en fonction de ce qu'elle contient : formulaire 440px, lecture 720px avec texte plafonné à la mesure
type(66ch), données 1200px. Une colonne ne s'étire jamais parce que l'écran est large. Pourquoi : au-delà de 75 caractères, une ligne perd le lecteur, et un formulaire étiré envoie les yeux traverser l'écran. - Espace. Utilisez l'échelle ci-dessous et rien d'autre. Groupez d'abord, avec de l'espace ; ajoutez un fond, puis une ligne, seulement là où l'espace ne peut pas porter. spacing-and-density.md.
- Aligner. Choisissez les bords partagés et placez tout sur eux. Un écart de 1 à 4px est un bug. Corrigez optiquement là où la géométrie semble fausse. spacing-and-density.md.
- Taille. Choisissez la densité pour la région, puis les zones de clic. spacing-and-density.md.
- Données dans le pire cas. Redimensionnez la boîte pour le contenu le plus long, le plus court, manquant, traduit et encore en chargement avant de dire que c'est fini. stability-and-text.md.
- Adapter. Faites réagir la structure à son conteneur, pas à l'appareil. shell-and-responsive.md.
- Mesurer. Rendez à 2×, mesurez les écarts et les bords, corrigez ce que les pixels montrent. measure.md.
L'échelle
Une seule échelle, base 4px. N'écrivez jamais une valeur d'espacement qui n'est pas un pas. Pourquoi : des valeurs uniques (10px, 14px, 18px) rendent le rythme impossible à voir et impossible à maintenir.
| Token | px | Utilisation typique |
|---|---|---|
--space-xs | 4 | étiquette par rapport à sa description ; icône par rapport à l'étiquette dans un chip |
--space-sm | 8 | éléments dans un groupe ; remplissage bloc du contrôle |
--space-md | 12 | entre contrôles bordés ; remplissage en ligne du contrôle |
--space-lg | 16 | entre champs ; remplissage carte compacte |
--space-20 | 20 | entre blocs dans une carte |
--space-xl | 24 | entre groupes ; remplissage carte confortable |
--space-2xl | 32 | entre sections |
--space-3xl | 48 | au-dessus du premier titre d'une page |
--space-4xl | 64 | en dessous du dernier bloc d'une page |
Les ajustements de 1 à 2px pour l'alignement optique sont les seules valeurs hors échelle. Nommez-les (--play-nudge: 2px).
Règles qui n'ont besoin d'aucune référence
- Grouper avec de l'espace. La rupture entre les groupes est d'au moins 2× l'écart à l'intérieur d'un groupe : 8px à l'intérieur signifie 16px ou plus entre. Un espacement égal partout rend le regroupement ambigu, et cette ambiguïté est un bug fonctionnel.
- Supprimer les lignes avant d'en ajouter. D'abord l'espace, ensuite un fond, enfin un séparateur, et seulement pour les données denses. Pourquoi : chaque ligne coûte de l'attention pour la même information que l'espace donne gratuitement.
- L'espace au-dessus est égal à l'espace en dessous pour tout ce qui se trouve entre deux choses. Pourquoi : une chose plus proche d'un voisin donne l'impression de lui appartenir.
- Les contrôles doivent ressembler à des contrôles. Un bouton, un lien ou un interrupteur a une forme, un soulignement ou une position dans la barre d'outils. Le texte statique ne partage jamais ces caractéristiques. Pourquoi : un contrôle stylisé comme du texte se cache, et du texte stylisé comme un contrôle collecte des clics morts.
- Les actions se trouvent au-dessus des données sur lesquelles elles agissent, jamais en dessous. Un contrôle qui définit quelque chose se trouve à côté.
- Le contenu principal passe en premier et en tête. L'ordre visuel est égal à l'ordre DOM : jamais
order,row-reverseou placement par grille pour réorganiser le contenu. Pourquoi : l'ordre du clavier et du lecteur d'écran suit le DOM. - Propriétés logiques (
margin-inline-start,padding-block,inset-inline-end,text-align: start) pour tout ce qui se reflète. Pourquoi : le RTL fonctionne alors sans une deuxième feuille de style. - Ne jamais placer de contrôles critiques dans le bord inférieur d'une fenêtre ou d'une barre latérale. Pourquoi : les gens poussent les fenêtres partiellement hors de l'écran.
- Le défilement montre ce qui est caché. Le contenu passé un bord reçoit un indice visible : un aperçu de 16 à 32px ou un dégradé.
- Rien ne bouge. Le survol, la sélection et le focus ne changent jamais la taille d'un élément. Les données chargées prennent la boîte que le squelette détenait.
Paires
| Cassé | Corrigé | Pourquoi |
|---|---|---|
gap: 12px dans le formulaire et gap: 12px entre ses fieldset | gap: 8px à l'intérieur, gap: 24px entre | Les groupes ont besoin d'une rupture d'au moins 2× |
padding: 10px 14px | padding: var(--space-sm) var(--space-md) | Pas uniquement |
| Titre à x=24, description à x=26 | Les deux à x=24 | Un écart de 1 à 4px est perçu comme une erreur |
Bouton icône 16×16 avec padding: 0 | Glyphe 16px, ::before { inset: -12px } pour une cible de 40px | La cible de la souris est de 40px |
.row + .row { margin-top: 8px } avec survol et clic sur les lignes | .row { padding-block: 4px }, pas de marge | Un écart entre les lignes est une zone morte et fait vaciller le survol |
.menu { width: 200px } | width: max-content; min-width: 160px; max-width: 320px | Le menu épouse son étiquette la plus longue |
@media (max-width: 768px) sur une carte | @container (width < 400px) sur le parent de la carte | Une carte dans une barre latérale étroite a besoin de sa propre largeur, pas de celle de l'écran |
<img src> sans taille, puis une carte qui saute | width, height ou aspect-ratio définis | La boîte existe avant les octets |
| En-tête collant, ancre arrive en dessous | scroll-padding-block-start: 56px sur le défileur | Le focus et les ancres ne se retrouvent jamais sous le chrome |
Avant de terminer
Recherchez ces motifs dans le diff, puis vérifiez.
| Motif | Correction |
|---|---|
margin, padding ou gap avec une valeur hors de l'échelle | Pas le plus proche |
Une seule valeur gap utilisée à l'intérieur et entre les groupes | Intérieur ≤ moitié de entre |
Bouton uniquement icône de moins de 24px, ou de moins de 40px sans ::before | Étendre avec un pseudo-élément |
gap ou margin entre des lignes cliquables ou des éléments de menu | Padding à l'intérieur de l'élément |
width fixe sur un menu, un bouton ou une zone de texte | max-content, min-width, max-width ; min-height pour le texte |
@media (max-width à l'intérieur d'un composant réutilisable | @container sur son parent |
height: 100vh pour un shell | 100dvh |
Piste 1fr ou enfant flex contenant du texte long | minmax(0, 1fr) ou min-width: 0 |
margin-left, padding-right, left: 0, text-align: left dans une UI en miroir | Forme logique |
En-tête ou pied de page collant sans scroll-padding-block | Définissez-le à la hauteur du chrome |
| Hauteur de ligne squelette ou largeurs de colonne différentes du chargé | Faites-les correspondre |
<img> sans dimensions ni aspect-ratio | Ajoutez-en un |
Vérifiez avec measure.md : rendez à 2× à 390px et à la largeur la plus large du shell, exécutez le script, recadrez chaque candidat et comptez les pixels. Ensuite, tabulez à travers l'écran avec le chrome collant en place, et chargez les chaînes les plus longues. Signalez ce que vous n'avez pas pu vérifier comme non vérifié.