Ressources · Développeurs

Patterns FSE avancés pour l'accessibilité

Cinq patterns WordPress Full Site Editing prêts à copier, pour traiter les critères RGAA que les blocs natifs seuls ne couvrent pas.

Les encarts des fiches critères couvrent l'usage courant des blocs natifs. Cette page va plus loin : cinq patterns complets, prêts à copier, pour les cas où l'éditeur de site seul ne suffit pas. Chaque pattern pousse theme.json et les template parts jusqu'à leur limite utile.

Public visé : développeurs et prestataires qui construisent des thèmes FSE pour le secteur public, et chargés de communication qui veulent comprendre ce que leur prestataire peut mettre en place.

Compatibilité : WordPress 6.3 minimum. Les ajouts apportés par WordPress 7.0 (sortie prévue le 20 mai 2026) sont signalés quand ils simplifient un pattern.

Pattern 1 · Critère 12.7

Lien d'évitement intégré à theme.json

Le lien d'évitement est le premier pattern à traiter parce qu'aucun bloc natif ne le propose clé en main. L'encart du critère 12.7 donne une version avec CSS dans une feuille de style séparée. Ce pattern va un cran plus loin : tout se pilote depuis theme.json, sans feuille CSS additionnelle.

Principe

Le lien d'évitement est un bouton placé en première position dans le template part Header. Il utilise une classe CSS personnalisée lien-evitement. Les styles de masquage et d'apparition au focus sont déclarés dans theme.json via la section styles.css au niveau racine.

Template part Header

Créez parts/header.html avec le bloc Bouton en toute première position.

parts/header.html
<!-- wp:buttons {"className":"lien-evitement","layout":{"type":"flex","justifyContent":"left"}} -->
<div class="wp-block-buttons lien-evitement">
  <!-- wp:button {"className":"is-style-outline"} -->
  <div class="wp-block-button is-style-outline">
    <a class="wp-block-button__link wp-element-button" href="#contenu-principal">
      Aller au contenu principal
    </a>
  </div>
  <!-- /wp:button -->
</div>
<!-- /wp:buttons -->

<!-- wp:site-title /-->
<!-- wp:navigation /-->

Template Page

Dans templates/page.html, encapsulez le contenu dans un bloc Groupe avec l'ancre contenu-principal et la balise sémantique main.

templates/page.html
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<!-- wp:group {"tagName":"main","anchor":"contenu-principal","layout":{"type":"constrained"}} -->
<main id="contenu-principal" class="wp-block-group">
  <!-- wp:post-title {"level":1} /-->
  <!-- wp:post-content /-->
</main>
<!-- /wp:group -->

<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

Styles dans theme.json

Le champ styles.css accepte du CSS brut depuis WordPress 6.2. Placez-y les règles de masquage du lien d'évitement.

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "styles": {
    "css": ".lien-evitement .wp-block-button__link { position: absolute; left: -9999px; top: 0; z-index: 100; } .lien-evitement .wp-block-button__link:focus { left: 1rem; top: 1rem; }"
  }
}

Pourquoi cette approche

Tout le thème est autodescriptible : pas de style.css à maintenir, pas de dépendance à un build. Un autre prestataire qui reprend le thème comprend immédiatement où se trouvent les styles d'accessibilité. Le lien reste invisible pour les utilisateurs voyants et parfaitement fonctionnel au clavier.

Vérification : ouvrez la page sur le site. Appuyez sur Tab immédiatement après le chargement. Le bouton doit apparaître en haut à gauche avec un contraste visible. Appuyez sur Entrée : le focus doit sauter sur la zone de contenu principal.

Pattern 2 · Critère 3.3

Palette WCAG AAA verrouillée avec style variations

Le critère 3.3 demande un contraste AA minimum. Ce pattern livre une palette AAA (ratio 7:1 pour le texte standard, 4.5:1 pour le texte large), avec une variante sombre incluse. La saisie de couleurs libres est bloquée pour empêcher les rédacteurs de contourner la palette.

Palette principale dans theme.json

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "color": {
      "custom": false,
      "customDuotone": false,
      "customGradient": false,
      "defaultPalette": false,
      "defaultGradients": false,
      "defaultDuotone": false,
      "palette": [
        { "slug": "fond", "color": "#ffffff", "name": "Fond" },
        { "slug": "texte", "color": "#1a1a1a", "name": "Texte (ratio 16.9:1)" },
        { "slug": "texte-secondaire", "color": "#4a4a4a", "name": "Texte secondaire (ratio 8.9:1)" },
        { "slug": "accent", "color": "#0052a3", "name": "Accent (ratio 8.6:1)" },
        { "slug": "accent-fond", "color": "#e8f0fa", "name": "Fond accent clair" },
        { "slug": "alerte", "color": "#a00000", "name": "Alerte (ratio 7.5:1)" }
      ]
    }
  }
}

Les trois options custom, customDuotone, customGradient à false bloquent la saisie libre. Les trois options defaultPalette, defaultGradients, defaultDuotone à false retirent les couleurs par défaut de WordPress, qui ne sont pas toutes conformes.

Variante sombre dans styles/sombre.json

Une style variation sombre qui conserve les mêmes ratios.

styles/sombre.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "title": "Sombre",
  "settings": {
    "color": {
      "palette": [
        { "slug": "fond", "color": "#0f0f0f", "name": "Fond" },
        { "slug": "texte", "color": "#f5f5f5", "name": "Texte (ratio 16.1:1)" },
        { "slug": "texte-secondaire", "color": "#c0c0c0", "name": "Texte secondaire (ratio 9.0:1)" },
        { "slug": "accent", "color": "#7eb3f0", "name": "Accent (ratio 8.9:1)" },
        { "slug": "accent-fond", "color": "#1a2e42", "name": "Fond accent sombre" },
        { "slug": "alerte", "color": "#ff8080", "name": "Alerte (ratio 7.2:1)" }
      ]
    }
  },
  "styles": {
    "color": {
      "background": "var(--wp--preset--color--fond)",
      "text": "var(--wp--preset--color--texte)"
    }
  }
}

Les slugs sont identiques à la palette principale. Quand un utilisateur bascule sur la variante sombre depuis Apparence puis Éditeur puis Styles, tous les blocs qui utilisent var(--wp--preset--color--accent) se recolorent automatiquement.

Pourquoi cette approche

Un rédacteur qui insère un bloc Paragraphe ne peut littéralement pas choisir une couleur hors palette. La conformité est verrouillée à la source du thème, sans dépendre de la vigilance humaine à chaque publication. Les variations offrent un choix visuel sans compromis sur l'accessibilité.

Pour un travail rapide sur plusieurs combinaisons, L'Atelier WP génère des palettes AAA déterministes directement exploitables dans theme.json : latelier-wp.com (lien externe qui s'ouvre dans un nouvel onglet)

Pattern 3 · Critère 10.7

Focus styles pilotés par theme.json

Le critère 10.7 demande un indicateur de focus visible et contrasté. Ce pattern utilise les pseudo-classes supportées par theme.json depuis WordPress 6.3 pour les éléments, et les pseudo-states des blocs élargis avec WordPress 7.0.

Focus sur les éléments globaux (WordPress 6.3+)

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "styles": {
    "elements": {
      "link": {
        ":focus": {
          "outline": {
            "style": "solid",
            "width": "2px",
            "color": "var(--wp--preset--color--accent)",
            "offset": "2px"
          }
        },
        ":focus-visible": {
          "outline": {
            "style": "solid",
            "width": "3px",
            "color": "var(--wp--preset--color--accent)",
            "offset": "3px"
          }
        }
      },
      "button": {
        ":focus": {
          "outline": {
            "style": "solid",
            "width": "3px",
            "color": "var(--wp--preset--color--accent)",
            "offset": "2px"
          }
        }
      }
    }
  }
}

La pseudo-classe :focus-visible permet de ne montrer l'indicateur qu'au focus clavier, pas au clic souris. Les navigateurs modernes la supportent tous.

Nouveau · WordPress 7.0

Focus sur le bloc Bouton

WordPress 7.0 étend les pseudo-classes aux blocs eux-mêmes, pas seulement aux éléments. Le bloc Bouton peut désormais être stylé sur tous ses états.

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "styles": {
    "blocks": {
      "core/button": {
        "color": {
          "background": "var(--wp--preset--color--accent)",
          "text": "var(--wp--preset--color--fond)"
        },
        ":hover": {
          "color": { "background": "var(--wp--preset--color--texte)" }
        },
        ":focus": {
          "color": { "background": "var(--wp--preset--color--texte)" },
          "outline": {
            "style": "solid",
            "width": "3px",
            "color": "var(--wp--preset--color--alerte)",
            "offset": "3px"
          }
        }
      }
    }
  }
}

La couleur de l'outline utilise alerte pour maximiser le contraste avec le fond du bouton, qui est déjà coloré. C'est une astuce souvent oubliée : le focus ring d'un bouton doit contraster avec le bouton lui-même, pas avec le fond de la page.

Pourquoi cette approche

Les focus styles survivent aux mises à jour du thème et des blocs. Un développeur qui modifie le CSS global ne peut pas casser accidentellement l'indicateur de focus, puisqu'il est défini au niveau du thème. Les pseudo-états 7.0 permettent de piloter hover, focus et active avec la même cohérence.

Pattern 4 · Critères 9.2, 12.1, 12.2, 12.7

Navigation complète avec fil d'Ariane et landmarks

Ce pattern assemble le template part Header complet d'un site public conforme : lien d'évitement, fil d'Ariane, navigation principale, recherche. Il combine plusieurs critères dans une structure cohérente.

Template part Header complet

parts/header.html
<!-- wp:buttons {"className":"lien-evitement"} -->
<div class="wp-block-buttons lien-evitement">
  <!-- wp:button -->
  <div class="wp-block-button">
    <a class="wp-block-button__link wp-element-button" href="#contenu-principal">
      Aller au contenu principal
    </a>
  </div>
  <!-- /wp:button -->
</div>
<!-- /wp:buttons -->

<!-- wp:group {"tagName":"div","className":"header-top","layout":{"type":"flex","justifyContent":"space-between","flexWrap":"wrap"}} -->
<div class="wp-block-group header-top">
  <!-- wp:site-title {"level":0} /-->
  <!-- wp:search {"label":"Rechercher sur le site","showLabel":false,"buttonText":"Rechercher","buttonPosition":"button-inside"} /-->
</div>
<!-- /wp:group -->

<!-- wp:navigation {"ariaLabel":"Navigation principale"} /-->
Nouveau · WordPress 7.0

Template Page avec fil d'Ariane

WordPress 7.0 introduit un bloc Breadcrumbs natif qui génère automatiquement le fil d'Ariane sémantique correct.

templates/page.html
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<!-- wp:group {"tagName":"main","anchor":"contenu-principal","layout":{"type":"constrained"}} -->
<main id="contenu-principal" class="wp-block-group">

  <!-- wp:breadcrumbs {"showCurrentAsLink":false} /-->

  <!-- wp:post-title {"level":1} /-->
  <!-- wp:post-content /-->
</main>
<!-- /wp:group -->

<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

Sur WordPress 6.x, le bloc Breadcrumbs n'existe pas. Le fil d'Ariane se construit alors à la main avec un bloc Groupe balisé nav et un attribut aria-label, ou avec une extension comme Yoast SEO qui fournit son propre bloc fil d'Ariane.

Structure sémantique finale

Le rendu HTML produit respecte tous les landmarks attendus en audit.

Rendu HTML
<header>
  <!-- lien d'évitement -->
  <!-- site-title + search -->
  <nav aria-label="Navigation principale">...</nav>
</header>
<main id="contenu-principal">
  <nav aria-label="Vous êtes ici">...</nav>
  <h1>...</h1>
  <!-- contenu -->
</main>
<footer>...</footer>

Vérification

Un audit automatique (axe-core, WAVE, pa11y) valide cette structure sans remonter d'erreur sur les landmarks. L'extension Landmarks du navigateur permet de visualiser les zones une par une.

Pattern 5 · Critères 10.11, 10.12, 13.8

Typographie fluide et respect des préférences système

Ce pattern combine trois exigences : la typographie fluide du critère 10.11, les règles d'espacement du 10.12, et le respect de prefers-reduced-motion du critère 13.8.

Typographie fluide complète

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "typography": {
      "fluid": true,
      "fontSizes": [
        { "slug": "xs", "size": "0.875rem",
          "fluid": { "min": "0.875rem", "max": "1rem" }, "name": "Petit" },
        { "slug": "base", "size": "1rem",
          "fluid": { "min": "1rem", "max": "1.125rem" }, "name": "Normal" },
        { "slug": "lg", "size": "1.25rem",
          "fluid": { "min": "1.125rem", "max": "1.5rem" }, "name": "Grand" },
        { "slug": "xl", "size": "1.75rem",
          "fluid": { "min": "1.5rem", "max": "2.25rem" }, "name": "Très grand" },
        { "slug": "2xl", "size": "2.5rem",
          "fluid": { "min": "2rem", "max": "3.5rem" }, "name": "Titre" }
      ]
    }
  },
  "styles": {
    "typography": {
      "fontSize": "var(--wp--preset--font-size--base)",
      "lineHeight": "1.6"
    },
    "elements": {
      "h1": { "typography": { "fontSize": "var(--wp--preset--font-size--2xl)", "lineHeight": "1.2" } },
      "h2": { "typography": { "fontSize": "var(--wp--preset--font-size--xl)", "lineHeight": "1.3" } },
      "h3": { "typography": { "fontSize": "var(--wp--preset--font-size--lg)", "lineHeight": "1.4" } }
    }
  }
}

La valeur lineHeight à 1.6 pour le corps de texte couvre largement l'exigence du critère 10.12 (1.5 minimum quand un utilisateur force l'espacement). Les hiérarchies de titres conservent une interligne plus serrée pour rester lisibles.

Respect de prefers-reduced-motion via styles.css

WordPress 6.2 a ouvert la section styles.css au CSS brut. C'est le bon endroit pour les media queries d'accessibilité.

theme.json
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "styles": {
    "css": "@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } }"
  }
}

Cette règle neutralise toute animation ou transition pour les utilisateurs ayant configuré cette préférence dans leur système. La directive !important est nécessaire parce que cette règle doit l'emporter sur les animations définies par les blocs et les extensions.

Pourquoi cette approche

L'utilisateur final contrôle sa préférence de mouvement au niveau de son système d'exploitation. Votre thème respecte ce choix sans qu'aucune action supplémentaire ne soit demandée au rédacteur. La typographie s'adapte à la largeur de l'écran sans débordement sur mobile, ni texte trop petit sur grand écran.

Pour aller plus loin

Ces cinq patterns couvrent les cas où theme.json apporte une valeur systémique à l'accessibilité. Certains critères restent hors de leur portée : les formulaires complexes au-delà du bloc Recherche, les médias avec sous-titres, les composants interactifs custom. Ces sujets relèvent d'extensions dédiées ou de développement spécifique.

La lettre de l'Atelier A11Y

Ressources pédagogiques, critères RGAA commentés et retours de terrain : une lettre mensuelle pour progresser sur l'accessibilité numérique, sans jargon.

  • Nouveaux articles et ressources pédagogiques
  • Critères RGAA décortiqués avec des exemples concrets
  • Bonnes pratiques et retours d'expérience terrain
S'abonner à la newsletter (s'ouvre dans un nouvel onglet)

Gratuit. Désabonnement possible à tout moment.