ADR-023 ActifActive contract

Typographic scale: rem Minor Third + line-height rules

DateDate
2026-05-29
DécideursDecision makers
Guilherme Negreiros — Design System Lead

Context

The previous typographic scale had 3 sizes (sm/md/xl in px) with no formalized scale logic, no line-height management by context, and no distinction between the needs of marketing, e-commerce, and SaaS contexts. The goal is an accessible, coherent scale, adapted to usage profiles.

Situation before

"fontSize": {
  "sm": "14px",
  "md": "16px",
  "xl": "24px"
},
"lineHeight": {
  "tight": "1.25",
  "normal": "1.5"
}

Three sizes in px, two line-heights with no assignment rules.


Decision

Unit: rem (relative to the browser base)

All font sizes are expressed in rem (reference = 1rem = 16px). This convention:

  • Respects user zoom preferences (WCAG accessibility)
  • Allows low-vision users to enlarge text without breaking relative layouts
  • Remains compatible with the 4px grid (the equivalent px values are multiples of 4)

Scale: Minor Third (ratio 1.2)

The scale for large sizes (headings) follows the Minor Third musical scale (ratio 1.2), rounded to the nearest multiple of 4px to respect the grid.

16px × 1.200¹ = 19.2  → 20px  (lg)
16px × 1.200² = 23.0  → 24px  (xl)
16px × 1.200³ = 27.6  → 28px  (2xl)
16px × 1.200⁴ = 33.2  → 32px  (3xl)
16px × 1.200⁵ = 39.8  → 40px  (4xl)
TokenrempxLine-heightRole
fontSize.xs0.75rem12px1.6Details, annotations, captions
fontSize.sm0.875rem14px1.6Labels, metadata, helper text
fontSize.base1rem16px1.6Main body text
fontSize.lg1.25rem20px1.1Heading 5, subtitles
fontSize.xl1.5rem24px1.1Heading 4
fontSize.2xl1.75rem28px1.1Heading 3
fontSize.3xl2rem32px1.0Heading 2
fontSize.4xl2.5rem40px1.0Heading 1
fontSize.5xl3rem48px1.0Hero display

Line-height rules

Only three values, assigned by context:

TokenValueAssignment rule
lineHeight.reading1.6Any text of size ≤ base (xs, sm, base)
lineHeight.heading1.1Mid-size headings (lg, xl, 2xl)
lineHeight.display1.0Large headings (3xl, 4xl, 5xl)

Rationale:

  • 1.6 on small sizes → maximum legibility, WCAG 1.4.12 compliance
  • 1.0–1.1 on large headings → visual impact, consistency between marketing and SaaS sites

Rationale

Why Minor Third?

The Minor Third (1.200) is the scale best suited to hybrid interfaces (marketing + SaaS):

  • Expressive enough for marketing pages (visually hierarchical headings)
  • Restrained enough for SaaS (no gigantic gaps between levels)
  • Values rounded to 4px remain all memorable

Why rem and not px?

Browser accessibility preferences (text zoom, base font size) use rem as their lever. A px-based system ignores these preferences. WCAG recommends relative units for text (Success Criterion 1.4.4).

Why line-height 1.6 for common text?

WCAG 2.1 Success Criterion 1.4.12 (Text Spacing) requires that content remain readable with a line-height of 1.5×. A default value of 1.6 covers this criterion automatically with no override needed.


Rejected alternatives

AlternativeReason for rejection
Major Second (1.125)Too little differentiation between heading levels
Perfect Fourth (1.333)Too dramatic for high information-density SaaS interfaces
Fixed scale (8, 12, 16, 20, 24, 32, 40)No mathematical ratio — hard to extend coherently
Keep pxIncompatible with browser zoom preferences (WCAG 1.4.4)
Single 1.5 line-heightToo compressed on small text, too airy on very large text

Consequences

For tokens:

  • primitive.fontSize goes from 3 to 9 values (xs → 5xl)
  • primitive.lineHeight goes from 2 to 3 values (reading, heading, display)
  • semantic.typography expands from 3 styles (body, label, heading) to 9 (detail, label, body, h5→h1, hero)

For AI agents:

  • An agent can choose the correct typographic level by intent (body vs heading vs hero) without improvising sizes

For teams:

  • Simple rule: if it's body text → base (1rem); if it's a heading → choose among lg→4xl; if it's hero decoration → 5xl
  • No hardcoded intermediate value allowed

Contexte

L'échelle typographique précédente comportait 3 tailles (sm/md/xl en px) sans logique d'échelle formalisée, sans gestion du line-height par contexte, et sans distinction entre les besoins des contextes marketing, e-commerce et SaaS. L'objectif est une échelle accessible, cohérente, et adaptée aux profils d'usage.

Situation avant

"fontSize": {
  "sm": "14px",
  "md": "16px",
  "xl": "24px"
},
"lineHeight": {
  "tight": "1.25",
  "normal": "1.5"
}

Trois tailles en px, deux line-heights sans règles d'assignation.


Décision

Unité : rem (relatif à la base navigateur)

Toutes les tailles de police sont exprimées en rem (reference = 1rem = 16px). Cette convention :

  • Respecte les préférences de zoom utilisateur (accessibilité WCAG)
  • Permet aux utilisateurs malvoyants d'agrandir sans casser les mises en page relatives
  • Reste compatible avec la grille 4px (les px équivalents sont des multiples de 4)

Échelle : Minor Third (ratio 1.2)

L'échelle des grandes tailles (titres) suit la gamme musicale Minor Third (ratio 1.2), arrondie au multiple de 4px le plus proche pour respecter la grille.

16px × 1.200¹ = 19.2  → 20px  (lg)
16px × 1.200² = 23.0  → 24px  (xl)
16px × 1.200³ = 27.6  → 28px  (2xl)
16px × 1.200⁴ = 33.2  → 32px  (3xl)
16px × 1.200⁵ = 39.8  → 40px  (4xl)
TokenrempxLine-heightRôle
fontSize.xs0.75rem12px1.6Détails, annotations, captions
fontSize.sm0.875rem14px1.6Labels, métadonnées, helper text
fontSize.base1rem16px1.6Corps de texte principal
fontSize.lg1.25rem20px1.1Heading 5, sous-titres
fontSize.xl1.5rem24px1.1Heading 4
fontSize.2xl1.75rem28px1.1Heading 3
fontSize.3xl2rem32px1.0Heading 2
fontSize.4xl2.5rem40px1.0Heading 1
fontSize.5xl3rem48px1.0Hero display

Règles de line-height

Trois valeurs seulement, assignées par contexte :

TokenValeurRègle d'assignation
lineHeight.reading1.6Tout texte de taille ≤ base (xs, sm, base)
lineHeight.heading1.1Titres intermédiaires (lg, xl, 2xl)
lineHeight.display1.0Grands titres (3xl, 4xl, 5xl)

Justification :

  • 1.6 sur les petites tailles → lisibilité maximale, conformité WCAG 1.4.12
  • 1.0–1.1 sur les grands titres → impact visuel, cohérence entre sites marketing et SaaS

Argumentaire

Pourquoi Minor Third ?

Le Minor Third (1.200) est l'échelle la plus adaptée aux interfaces hybrides (marketing + SaaS) :

  • Assez expressive pour les pages marketing (titres visuellement hiérarchisés)
  • Assez sobre pour les SaaS (pas d'écarts gigantesques entre niveaux)
  • Les valeurs arrondies à 4px restent toutes mémorisables

Pourquoi rem et pas px ?

Les préférences d'accessibilité navigateur (zoom texte, taille police de base) utilisent rem comme levier. Un système en px ignore ces préférences. La WCAG recommande les unités relatives pour le texte (Success Criterion 1.4.4).

Pourquoi line-height 1.6 pour les textes courants ?

La WCAG 2.1 Success Criterion 1.4.12 (Text Spacing) demande que le contenu reste lisible avec un line-height de 1.5×. Une valeur par défaut de 1.6 couvre d'office ce critère sans nécessiter d'override.


Alternatives rejetées

AlternativeRaison du rejet
Major Second (1.125)Différenciation trop faible entre les niveaux de titre
Perfect Fourth (1.333)Trop dramatique pour les interfaces SaaS à forte densité d'information
Scale fixe (8, 12, 16, 20, 24, 32, 40)Pas de ratio mathématique — difficile à étendre cohéremment
Garder les pxIncompatible avec les préférences de zoom navigateur (WCAG 1.4.4)
line-height unique 1.5Trop compressé sur les petits textes, trop aéré sur les très grands

Conséquences

Pour les tokens :

  • primitive.fontSize passe de 3 à 9 valeurs (xs → 5xl)
  • primitive.lineHeight passe de 2 à 3 valeurs (reading, heading, display)
  • semantic.typography s'étend de 3 styles (body, label, heading) à 9 (detail, label, body, h5→h1, hero)

Pour les agents IA :

  • Un agent peut choisir le niveau typographique correct par intention (corps vs titre vs hero) sans improviser des tailles

Pour les équipes :

  • Règle simple : si c'est du texte courant → base (1rem) ; si c'est un titre → choisir parmi lg→4xl ; si c'est de la déco hero → 5xl
  • Aucune valeur intermédiaire en dur autorisée
← ADR-022 ADR-024 →