Jour 47 Day 47 · mercredi 14 octobre 2026 Wednesday 14 October 2026 Web Fondamental

CSS moderne & accessibilité Modern CSS & accessibility

Spécificité, flexbox vs grid, unités modernes, et l'accessibilité qui commence par le HTML : les fondamentaux front que les recruteurs vérifient en cinq questions. Specificity, flexbox vs grid, modern units, and accessibility that starts with HTML: the front-end fundamentals recruiters check in five questions.

L’essentiel

CSS a mûri : là où il fallait des hacks (floats pour la mise en page, JS pour centrer), le langage offre aujourd’hui des outils natifs — flexbox, grid, custom properties, clamp(). Ce qui n’a pas changé : la cascade et la spécificité, le cœur du langage, que la plupart des bugs CSS « mystérieux » révèlent mal compris.

L’accessibilité (a11y) n’est pas une couche cosmétique ajoutée à la fin : elle commence par le HTML sémantique. Un <button> natif apporte gratuitement le focus clavier, le rôle annoncé aux lecteurs d’écran, l’activation Entrée/Espace ; un <div onclick> n’apporte rien de tout ça. En entretien front, ces deux sujets tombent systématiquement — et distinguent celui qui « fait du CSS » de celui qui le comprend.

La spécificité, le vrai calcul : un triplet (id, classes, éléments) comparé de gauche à droite — pas une somme magique :

Spécificité = (id, classes/attributs/pseudo-classes,
               éléments/pseudo-éléments)

style="…"        → inline : au-dessus de tout
#nav .link:hover → (1, 2, 0)
.menu .link      → (0, 2, 0)
a.link           → (0, 1, 1)
nav a            → (0, 0, 2)

(1,0,0) bat (0,15,3) : un id bat n'importe quel
nombre de classes. À égalité : la dernière règle
déclarée gagne. !important court-circuite tout.

Comment ça marche

La cascade résout chaque conflit dans cet ordre : origine et importance (styles navigateur < feuille auteur < !important), couches (@layer), spécificité, et enfin ordre d’apparition. !important gagne contre tout — c’est pour ça que c’est un aveu : on l’utilise quand on a perdu le contrôle de sa spécificité, et il déclenche la surenchère (le prochain conflit exigera un autre !important). La solution durable : rester bas et homogène en spécificité (des classes, pas d’id dans les sélecteurs), pour pouvoir toujours surcharger simplement.

Flexbox vs Grid : une dimension contre deux.

FlexboxGrid
Dimension1D : une ligne OU une colonne2D : lignes ET colonnes ensemble
Qui piloteLe contenu (les items se répartissent)Le conteneur (la grille est définie)
Cas typiquesnavbar, groupe de boutons, centragelayout de page, galerie, dashboard
Retour à la ligneflex-wrap, rangées indépendantesalignement 2D cohérent
Le duo gagnantL’intérieur des composantsLa structure de la page

Les deux se combinent : grid pour le squelette de la page, flexbox à l’intérieur des composants. Et le centrage, question piège favorite : display: grid; place-items: center; sur le parent — deux lignes.

Unités et outils modernes :

  • rem (relative à la racine) pour le texte : respecte le réglage de taille de l’utilisateur — contrairement au px figé. C’est un enjeu d’accessibilité, pas de style.
  • clamp(min, préféré, max) : la typo fluide sans media query — font-size: clamp(1rem, 2.5vw, 1.5rem).
  • dvh (dynamic viewport height) : corrige le bug classique du 100vh sur mobile, où la barre d’URL masque le bas de page.
  • Custom properties (--accent: #0057b8) : de vraies variables vivantes dans la cascade, surchargeables par contexte (thème sombre, composant) — ce que les variables Sass, compilées puis figées, ne savent pas faire.
  • Container queries (en survol) : @container adapte un composant à la taille de son conteneur, pas de l’écran. Le chaînon manquant du responsive par composant : une card réutilisée dans une sidebar étroite ou une zone large s’adapte seule.

Mobile-first : les styles de base ciblent le petit écran, puis les @media (min-width: …) enrichissent en montant. Plus simple à raisonner — on ajoute au lieu d’annuler — et aligné sur la réalité du trafic.

Concepts clés à maîtriser

L’accessibilité, dans l’ordre d’importance :

  1. HTML sémantique d’abord : <button>, <a>, <nav>, <main>, <label> relié à son input. L’essentiel de l’accessibilité est gratuit quand les bons éléments sont utilisés.
  2. Alternatives textuelles : alt descriptif sur les images informatives ; alt="" (vide, pas absent) sur les décoratives, pour que les lecteurs d’écran les sautent au lieu de lire le nom du fichier.
  3. Contraste : 4.5:1 minimum pour le texte normal (WCAG AA), 3:1 pour le grand texte. Vérifiable en deux clics (DevTools, WebAIM).
  4. Focus visible : :focus-visible affiche un contour net à la navigation clavier sans polluer le clic souris. Supprimer l’outline sans remplacement rend le site inutilisable au clavier.
  5. Navigation clavier : tout ce qui se clique doit être atteignable (Tab) et activable (Entrée/Espace). Test à coût zéro : lâcher la souris cinq minutes et traverser sa page.
  6. ARIA en dernier recours : pour les états que le HTML ne sait pas exprimer (aria-expanded, aria-live). La première règle d’ARIA, écrite noir sur blanc par le W3C : ne pas utiliser ARIA quand un élément natif suffit.

Un composant accessible, au complet :

<!-- Un vrai <button> : focus, rôle, clavier — tout est natif -->
<button class="menu-btn" aria-expanded="false" aria-controls="menu">
  Menu
</button>
<ul id="menu" hidden>…</ul>

<style>
  .menu-btn {
    font-size: 1rem;              /* rem : suit le réglage utilisateur */
    padding: 0.5em 1em;           /* em : suit la taille du texte */
    cursor: pointer;
  }
  .menu-btn:focus-visible {       /* visible au clavier, pas au clic */
    outline: 3px solid var(--accent, #0057b8);
    outline-offset: 2px;
  }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {      /* respecte le réglage système */
      animation-duration: 0.01ms !important;
      transition-duration: 0.01ms !important;
    }
  }
</style>

Le seul ARIA nécessaire ici : aria-expanded (l’état ouvert/fermé, que le HTML ne sait pas exprimer) et aria-controls (le lien vers le menu). Le JS bascule aria-expanded et hidden — c’est tout.

⚠️ Le div cliquable — <div onClick={...}> est le péché originel du front moderne : pas de focus (invisible au Tab), pas de rôle (muet pour les lecteurs d’écran), pas d’activation clavier (Entrée/Espace ignorés), pas d’état :disabled. Le rendre accessible exige tabindex="0", role="button", un handler keydown… soit réimplémenter, mal, ce que <button> donne en une balise. Si ça se clique : <button> pour une action, <a> pour une navigation.

💡 prefers-reduced-motion — certains utilisateurs (troubles vestibulaires, sensibilité au mouvement) désactivent les animations dans leur OS. Le réglage arrive en CSS via @media (prefers-reduced-motion: reduce) : le respecter coûte cinq lignes, l’ignorer rend le site physiquement pénible. Le mentionner en entretien signale une culture a11y au-dessus de la moyenne.

En entretien

« C’est quoi la spécificité CSS ? » — Le mécanisme qui départage deux règles ciblant le même élément : un triplet (id, classes/pseudo-classes/attributs, éléments) comparé position par position, de gauche à droite. Un id bat n’importe quel nombre de classes ; à égalité exacte, la dernière règle déclarée gagne ; le style inline surclasse le tout, et !important court-circuite le mécanisme. Bonne pratique : rester bas et homogène (des classes) pour garder la main sur les surcharges.

« Flexbox ou Grid : comment tu choisis ? » — Une dimension → flexbox : navbar, ligne de boutons, les items se répartissent le long d’un axe. Deux dimensions → grid : layout de page, galerie, lignes et colonnes définies ensemble. Ils se combinent : grid pour la structure, flexbox dans les composants. Bonus : place-items: center sur un parent grid centre un élément en deux lignes.

« Pourquoi un <button> plutôt qu’un <div> cliquable ? » — Le natif apporte gratuitement : focus clavier, rôle annoncé aux lecteurs d’écran, activation Entrée/Espace, état :disabled, comportement dans les formulaires. Le div exige de tout réimplémenter (tabindex, role, keydown) et le résultat est toujours incomplet. La règle : ARIA et JS ne réparent pas un mauvais HTML.

« Comment tu rends un site responsive ? » — Mobile-first : styles de base pour petit écran, media queries min-width pour enrichir. Layouts fluides avec grid/flexbox (minmax(), auto-fit), typo fluide avec clamp(), images en max-width: 100%. Et pour les composants réutilisés dans des contextes de largeurs différentes : container queries.

« C’est quoi ARIA, et quand l’utiliser ? » — Des attributs qui décrivent rôles et états aux technologies d’assistance quand le HTML natif ne suffit pas : aria-expanded pour un menu déroulant, aria-live pour du contenu mis à jour dynamiquement. Première règle d’ARIA : préférer l’élément natif. Et un mauvais ARIA est pire que pas d’ARIA : il promet aux lecteurs d’écran des comportements que le JS n’assure pas.

Pièges & idées reçues

  • !important pour « régler » un conflit : il le masque et déclenche la surenchère — le prochain conflit exigera un !important de plus. Le vrai fix : comprendre quelle règle gagne et pourquoi, puis baisser la spécificité.
  • « L’accessibilité, c’est pour la fin du projet » : rattraper l’a11y sur du mauvais HTML coûte dix fois le prix de partir des bons éléments. Et le public concerné est large : handicaps permanents, temporaires (bras cassé) et situationnels (plein soleil, bébé dans un bras).
  • outline: none sans remplacement : le classique du « c’est moche » qui rend le site inutilisable au clavier. :focus-visible règle l’esthétique proprement : contour au clavier, rien au clic.
  • ARIA partout : role="button" sur un <button>, aria-label redondants… Le sur-ARIA dégrade l’expérience au lecteur d’écran. La doc du W3C le dit littéralement : no ARIA is better than bad ARIA.
  • px pour les tailles de texte : ignore le réglage de l’utilisateur qui a agrandi sa police système. rem pour le texte, toujours.
  • Tester uniquement à la souris sur son écran : cinq minutes au clavier + un test de contraste attrapent la moitié des problèmes d’accessibilité avant la review.

Pour aller plus loin

The essentials

CSS has matured: where hacks were once required (floats for layout, JS for centering), the language now offers native tools — flexbox, grid, custom properties, clamp(). What hasn’t changed: the cascade and specificity, the core of the language, which most “mysterious” CSS bugs reveal to be misunderstood.

Accessibility (a11y) is not a cosmetic layer added at the end: it starts with semantic HTML. A native <button> gives you keyboard focus, a role announced to screen readers, and Enter/Space activation for free; a <div onclick> gives you none of that. In front-end interviews, both topics come up systematically — and they separate whoever “does CSS” from whoever understands it.

Specificity, the real calculation: a triplet (id, classes, elements) compared left to right — not some magic sum:

Specificity = (id, classes/attributes/pseudo-classes,
               elements/pseudo-elements)

style="…"        → inline: above everything
#nav .link:hover → (1, 2, 0)
.menu .link      → (0, 2, 0)
a.link           → (0, 1, 1)
nav a            → (0, 0, 2)

(1,0,0) beats (0,15,3): one id beats any number
of classes. On an exact tie: the last declared
rule wins. !important short-circuits it all.

How it works

The cascade resolves each conflict in this order: origin and importance (browser styles < author stylesheet < !important), layers (@layer), specificity, and finally order of appearance. !important beats everything — that’s why it’s a confession: you use it when you’ve lost control of your specificity, and it triggers an arms race (the next conflict will require yet another !important). The durable solution: keep specificity low and homogeneous (classes, no ids in selectors), so you can always override simply.

Flexbox vs Grid: one dimension versus two.

FlexboxGrid
Dimension1D: a row OR a column2D: rows AND columns together
Who drivesThe content (items distribute themselves)The container (the grid is defined)
Typical casesnavbar, button group, centeringpage layout, gallery, dashboard
Wrappingflex-wrap, independent rowsconsistent 2D alignment
The winning duoInside componentsThe page structure

The two combine: grid for the page skeleton, flexbox inside components. And centering, the favorite trick question: display: grid; place-items: center; on the parent — two lines.

Modern units and tools:

  • rem (relative to the root) for text: respects the user’s font-size setting — unlike frozen px. That’s an accessibility issue, not a style one.
  • clamp(min, preferred, max): fluid typography without a media query — font-size: clamp(1rem, 2.5vw, 1.5rem).
  • dvh (dynamic viewport height): fixes the classic 100vh bug on mobile, where the URL bar hides the bottom of the page.
  • Custom properties (--accent: #0057b8): real live variables inside the cascade, overridable per context (dark theme, component) — something Sass variables, compiled then frozen, cannot do.
  • Container queries (in passing): @container adapts a component to its container’s size, not the screen’s. The missing link of per-component responsive design: a card reused in a narrow sidebar or a wide area adapts by itself.

Mobile-first: base styles target the small screen, then @media (min-width: …) queries enrich on the way up. Simpler to reason about — you add instead of undoing — and aligned with real-world traffic.

Key concepts to master

Accessibility, in order of importance:

  1. Semantic HTML first: <button>, <a>, <nav>, <main>, <label> linked to its input. Most of accessibility is free when the right elements are used.
  2. Text alternatives: descriptive alt on informative images; alt="" (empty, not missing) on decorative ones, so screen readers skip them instead of reading the file name.
  3. Contrast: 4.5:1 minimum for normal text (WCAG AA), 3:1 for large text. Checkable in two clicks (DevTools, WebAIM).
  4. Visible focus: :focus-visible shows a clear outline during keyboard navigation without polluting mouse clicks. Removing the outline with no replacement makes the site unusable by keyboard.
  5. Keyboard navigation: everything clickable must be reachable (Tab) and activatable (Enter/Space). Zero-cost test: drop the mouse for five minutes and traverse your page.
  6. ARIA as a last resort: for states HTML cannot express (aria-expanded, aria-live). The first rule of ARIA, written in black and white by the W3C: don’t use ARIA when a native element is enough.

An accessible component, in full:

<!-- A real <button>: focus, role, keyboard — all native -->
<button class="menu-btn" aria-expanded="false" aria-controls="menu">
  Menu
</button>
<ul id="menu" hidden>…</ul>

<style>
  .menu-btn {
    font-size: 1rem;              /* rem: follows the user's setting */
    padding: 0.5em 1em;           /* em: follows the text size */
    cursor: pointer;
  }
  .menu-btn:focus-visible {       /* visible on keyboard, not on click */
    outline: 3px solid var(--accent, #0057b8);
    outline-offset: 2px;
  }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {      /* respects the system setting */
      animation-duration: 0.01ms !important;
      transition-duration: 0.01ms !important;
    }
  }
</style>

The only ARIA needed here: aria-expanded (the open/closed state, which HTML cannot express) and aria-controls (the link to the menu). The JS toggles aria-expanded and hidden — that’s all.

⚠️ The clickable div — <div onClick={...}> is the original sin of modern front-end: no focus (invisible to Tab), no role (mute for screen readers), no keyboard activation (Enter/Space ignored), no :disabled state. Making it accessible requires tabindex="0", role="button", a keydown handler… that is, reimplementing, badly, what <button> gives you in one tag. If it’s clickable: <button> for an action, <a> for navigation.

💡 prefers-reduced-motion — some users (vestibular disorders, motion sensitivity) disable animations in their OS. That setting reaches CSS through @media (prefers-reduced-motion: reduce): honoring it costs five lines, ignoring it makes the site physically unpleasant. Mentioning it in an interview signals an above-average a11y culture.

In an interview

“What is CSS specificity?” — The mechanism that settles two rules targeting the same element: a triplet (id, classes/pseudo-classes/attributes, elements) compared position by position, left to right. One id beats any number of classes; on an exact tie, the last declared rule wins; inline style tops everything, and !important short-circuits the mechanism. Good practice: keep it low and homogeneous (classes) to stay in control of overrides.

“Flexbox or Grid: how do you choose?” — One dimension → flexbox: navbar, row of buttons, items distributing along an axis. Two dimensions → grid: page layout, gallery, rows and columns defined together. They combine: grid for the structure, flexbox inside components. Bonus: place-items: center on a grid parent centers an element in two lines.

“Why a <button> rather than a clickable <div>?” — The native element gives you for free: keyboard focus, a role announced to screen readers, Enter/Space activation, the :disabled state, form behavior. The div requires reimplementing everything (tabindex, role, keydown) and the result is always incomplete. The rule: ARIA and JS don’t fix bad HTML.

“How do you make a site responsive?” — Mobile-first: base styles for small screens, min-width media queries to enrich. Fluid layouts with grid/flexbox (minmax(), auto-fit), fluid type with clamp(), images at max-width: 100%. And for components reused in contexts of different widths: container queries.

“What is ARIA, and when do you use it?” — Attributes describing roles and states to assistive technologies when native HTML isn’t enough: aria-expanded for a dropdown menu, aria-live for dynamically updated content. First rule of ARIA: prefer the native element. And bad ARIA is worse than no ARIA: it promises screen readers behaviors the JS doesn’t deliver.

Pitfalls & misconceptions

  • !important to “fix” a conflict: it hides the conflict and triggers the arms race — the next one will require one more !important. The real fix: understand which rule wins and why, then lower the specificity.
  • “Accessibility is for the end of the project”: retrofitting a11y onto bad HTML costs ten times the price of starting with the right elements. And the audience is wide: permanent, temporary (broken arm) and situational (bright sunlight, baby in one arm) impairments.
  • outline: none with no replacement: the classic “it’s ugly” move that makes the site unusable by keyboard. :focus-visible solves the aesthetics cleanly: outline on keyboard, nothing on click.
  • ARIA everywhere: role="button" on a <button>, redundant aria-labels… Over-ARIA degrades the screen reader experience. The W3C docs say it literally: no ARIA is better than bad ARIA.
  • px for text sizes: ignores the setting of a user who enlarged their system font. rem for text, always.
  • Testing only with a mouse on your own screen: five minutes on the keyboard + one contrast check catch half of the accessibility problems before review.

Going further

S'entraîner sur ce sujet → Practice this topic →