Combien de gabarits de thème déclarent un <header>, puis un second à l’intérieur du premier, sans qu’aucun des deux ne porte clairement le rôle de bannière ? La question n’est pas anecdotique : un lecteur d’écran s’appuie sur les repères de navigation pour permettre à une personne de sauter directement au contenu principal, à la recherche ou au pied de page. Quand la structure de fichiers du thème n’a pas anticipé ces repères, on les rajoute après coup, en général mal.
Ce billet propose une organisation de fichiers de gabarit qui garantit, dès la création du thème, des repères cohérents et une hiérarchie de titres logique. L’objectif n’est pas de lister les rôles ARIA un par un, mais de montrer où les poser dans l’arborescence pour ne plus avoir à y revenir à chaque nouvelle page.
Pourquoi les repères ne sont pas un détail cosmétique
Un repère de navigation (landmark, en anglais dans les spécifications) est une région de la page identifiée par un rôle : bannière, navigation, contenu principal, information complémentaire, pied de page. Les technologies d’assistance construisent à partir de ces rôles une carte de la page que la personne peut parcourir sans lire le document du début à la fin. Sans repères, on retombe sur une lecture linéaire, beaucoup plus lente.
Le rôle peut venir d’un élément HTML natif (<header>, <nav>, <main>, <footer>) ou d’un attribut role explicite. Le piège classique : un thème qui multiplie les <header> imbriqués finit avec plusieurs régions candidates au rôle banner, ce qui brouille la carte de navigation plutôt que de l’éclaircir.

L’arborescence de fichiers qui pose les bases
Avant d’écrire la moindre ligne de balisage, il vaut mieux fixer les responsabilités de chaque fichier. Voici l’organisation que je mets en place systématiquement sur un thème classique (avant l’éditeur de site, donc index.php, header.php, footer.php et leurs dérivés) :
mon-theme/
├── header.php → un seul <header> racine (role banner implicite)
├── footer.php → un seul <footer> racine (role contentinfo implicite)
├── template-parts/
│ ├── navigation.php → <nav> unique, aria-label distinct par usage
│ └── search-form.php → <search> ou role="search"
├── index.php → <main> posé une seule fois
├── page.php
├── single.php
└── archive.php
Cette arborescence impose une règle simple : le rôle banner et le rôle contentinfo n’existent qu’une fois, dans les fichiers qui portent leur nom. Aucun gabarit de contenu (single.php, page.php) n’a le droit de rouvrir un <header> ou un <footer> racine : il vient s’insérer entre l’appel à get_header() et get_footer().
Un en-tête qui déclare son rôle sans ambiguïté
Dans header.php, je place systématiquement le titre du site et la navigation principale à l’intérieur du même <header>, avec un seul niveau d’imbrication pour la navigation :
<header>
<p class="site-title"><a href="<?php echo esc_url( home_url( '/' ) ); ?>">
<?php bloginfo( 'name' ); ?>
</a></p>
<?php get_template_part( 'template-parts/navigation' ); ?>
</header>
Si le thème comporte une barre d’annonce au-dessus du menu (promotion, bandeau de cookies), elle ne va pas dans ce <header> : elle mérite son propre conteneur, sans quoi elle se retrouve annoncée comme faisant partie de la bannière du site alors qu’elle n’en a pas la fonction.
La hiérarchie de titres pensée dès le gabarit
Le second pilier de cette organisation, c’est la réservation du niveau <h1>. Sur un thème classique, je fixe une règle stricte : le <h1> est toujours généré par le gabarit de contenu (le titre de l’article ou de la page via the_title() entouré de balises <h1>), jamais par header.php. Le logo textuel du site reste en paragraphe stylé, pas en titre.
- Le
<h1>apparaît une seule fois par page, dans le gabarit de contenu. - Les widgets de la zone latérale démarrent en
<h3>, jamais en<h2>, pour laisser la place aux sections du corps principal. - Le pied de page n’introduit aucun nouveau niveau : ses colonnes utilisent des paragraphes en gras stylés en CSS plutôt que des titres.
Un cas concret sur une page d’archive
Sur archive.php, le titre de l’archive (via the_archive_title()) reçoit le niveau <h1>, et chaque titre d’article listé descend en <h2>. C’est cette discipline, fixée dans l’arborescence dès le premier commit du thème, qui évite les doubles <h1> qu’un audit remonte systématiquement sur les thèmes improvisés.
Pièges à éviter dans cette organisation
Trois erreurs reviennent souvent quand cette structure n’est pas posée dès le début du projet :
- Un
<nav>pour le menu principal et un second pour le fil d’Ariane sansaria-labeldistinct : les deux régions apparaissent avec le même nom pour la personne qui navigue au clavier. - Un widget de recherche dupliqué dans l’en-tête et dans la zone latérale, chacun avec un
<label>identique, ce qui crée une confusion sur lequel activer. - Un
<main>ouvert dansheader.phpet fermé dansfooter.php: pratique en apparence, mais cela empêche toute page de personnaliser sa propre région principale (par exemple pour insérer un id d’ancrage de lien d’évitement précis).
Sur mes projets, je fixe cette arborescence dans un fichier
STRUCTURE.mdà la racine du thème avant d’écrire le premier gabarit : ça évite les débats de convention trois mois plus tard, quand un second développeur reprend le projet.
En résumé
Une organisation de fichiers qui réserve un rôle précis à chaque gabarit — header.php pour la bannière unique, footer.php pour les informations complémentaires, les fichiers de contenu pour le <h1> et la région principale — règle à la source la plupart des problèmes de repères qu’un audit manuel révèle a posteriori. Ce n’est pas une contrainte technique lourde : c’est une convention d’équipe à fixer avant d’écrire la première ligne de balisage, pas après.