Aller au contenu

Header et menu utilisateur

Référence du header partagé de Kirexo et de son menu utilisateur déroulant. Le header est un composant d'interface transverse aux trois espaces (public, utilisateur, admin) : cette page décrit le markup qu'il produit, les entrées du menu selon le contexte, et le contrat d'accessibilité du panneau déroulant.

Cette page décrit ce que le header rend et comment il se comporte. Pour le pourquoi (un seul header pour trois contextes, la visibilité de la bascule comme commodité d'affichage), voir l'explication Entête partagé : identité, déconnexion et bascule. Pour les règles générales de couleurs, de thème et d'accessibilité auxquelles ce composant se conforme, voir Design system : tokens, thèmes et accent.

Le composant <twig:Header>

Le header est rendu par un composant Twig unique — src/Twig/Components/Header.php et son gabarit templates/components/Header.html.twig — instancié dans chaque layout via une seule balise :

<twig:Header space="public" />
<twig:Header space="user" />
<twig:Header space="admin" />

C'est un composant Twig simple (pas un Live Component) : le panneau du menu s'ouvre et se ferme entièrement côté client (assets/js/user-menu.js), sans aller-retour serveur.

Les trois espaces utilisent désormais le même header. En particulier, l'accueil de l'espace utilisateur (/u, templates/user/home.html.twig) a migré de sa topbar custom .ud-topbar vers <twig:Header space="user" /> ; les éléments propres à cette page (barre de recherche, onglets Carnets) restent dans le corps de la page, hors du header.

La prop space

Le composant reçoit une unique propriété space ('public', 'user' ou 'admin'), fournie par le layout. Elle pilote l'affichage : brand, présence de l'action principale, route de bascule et de déconnexion, présence du lien Réglages. Elle n'est jamais une donnée de sécurité — le cloisonnement des espaces reste porté par les firewalls (voir Architecture des firewalls).

Méthodes exposées

Le composant Header (src/Twig/Components/Header.php) expose les méthodes suivantes au gabarit.

Méthode Type retour Rôle
getUser() ?User Le compte connecté (instanceof User), ou null.
isMixed() bool Vrai si le compte porte à la fois ROLE_USER et ROLE_ADMIN.
getSwitchTarget() 'admin'\|'user'\|null Espace de destination de la bascule selon space, ou null si non éligible (compte non mixte, ou espace public).
getDisplayName() ?string Prénom du compte connecté (User::getFirstName()), ou null si personne n'est connecté — le bouton du menu n'est alors pas rendu.
getSwitchLabel() 'Espace admin'\|'Espace user'\|null Libellé UI de la cible de bascule, dérivé de getSwitchTarget().

Le nom affiché sur le bouton du menu est toujours le prénom, jamais l'email (règle §3 du design system). getFirstName() renvoie un string non nullable : dès qu'un compte est connecté, un prénom est affiché.

Structure du header rendu

Le header rend, de gauche à droite :

  • la brand « Kirexo » (logomark + mot), pointant vers l'accueil (app_home) ; suffixée d'une pastille .tag « Admin » quand space == 'admin' ;
  • la zone .header__actions, dont le contenu dépend de l'état de connexion (détaillé ci-dessous).

Compte connecté

Deux blocs sont rendus dans .header__actions :

Action principale visible (hors menu) — « Mon espace ». Un lien .header__primary vers app_user_home, rendu uniquement sur l'accueil public (space == 'public'). Il n'apparaît ni dans l'espace utilisateur (on y est déjà sur la home utilisateur), ni dans l'espace admin : là, l'accès à l'espace utilisateur passe par l'entrée « Espace user » de la bascule d'espace du menu, qui joue déjà ce rôle — pas de doublon (design system §2).

Menu utilisateur (disclosure). Un conteneur .user-menu[data-user-menu] avec :

  • un bouton déclencheur .user-menu__trigger[data-user-menu-trigger] portant le prénom (.user-menu__name) et un chevron ;
  • un panneau #user-menu-panel.user-menu__panel[data-user-menu-panel], masqué par l'attribut hidden à l'état fermé.

Le panneau regroupe les actions secondaires, dans cet ordre :

Ordre Entrée Condition d'affichage Cible
1 Sélecteur de thème (.theme-toggle, 3 boutons clair / sombre / système) toujours recâblé par assets/js/theme.js
2 Bascule d'espace (« Espace admin » / « Espace user ») getSwitchTarget() non nul — comptes mixtes uniquement, hors accueil POST app_switch_to_admin / app_switch_to_user (jeton CSRF switch_space)
3 Réglages space == 'admin' uniquement app_admin_settings
séparateur .user-menu__sep toujours
4 Se déconnecter (.user-menu__action--danger) toujours, en dernier POST app_admin_logout si space == 'admin', sinon app_logout

Le sélecteur de thème est secondaire une fois connecté : il vit dans le panneau, pas exposé à plat (règle §4 du design system). La bascule d'espace n'apparaît que pour les comptes mixtes ; sa visibilité est une commodité d'affichage, l'autorisation réelle étant portée par CanSwitchSpaceVoter — voir Mécanisme de bascule entre espaces.

Utilisateur anonyme (accueil public)

Quand personne n'est connecté et que space == 'public' (hors page de login), il n'y a pas de menu. À la place :

  • le sélecteur de thème reste visible à plat dans le header (pas de menu compte où le loger — règle §4 du design system) ;
  • un lien « Se connecter » (.header__login) vers app_login. Aucun lien vers le login admin n'est jamais exposé depuis l'accueil.

Contrat d'accessibilité — pattern disclosure

Le menu applique le pattern disclosure de la WAI-ARIA APG, pas un menu (role="menu") : son contenu est une liste de liens et de formulaires POST standards, parcourus à la tabulation naturelle. Ce choix est une règle du projet (§1 du design system) ; il évite d'annoncer un menu applicatif qui attendrait une navigation aux flèches non fournie.

Attributs ARIA

Élément État fermé État ouvert
.user-menu__trigger[data-user-menu-trigger] aria-expanded="false" aria-expanded="true"
#user-menu-panel[data-user-menu-panel] attribut hidden présent hidden retiré

Le trigger porte en permanence aria-controls="user-menu-panel" et aria-haspopup="true". Il n'y a aucun role="menu" ni role="menuitem" dans le panneau.

Comportement clavier et souris

Géré par assets/js/user-menu.js :

  • Ouverture — clic, Entrée ou Espace sur le trigger (comportement natif du <button>) : retire hidden, passe aria-expanded à true.
  • Échap — ferme le panneau et rend le focus au trigger.
  • Clic extérieur — un clic hors d'un [data-user-menu] ouvert ferme le panneau (sans voler le focus).
  • Sortie au Tab (focusout dont la cible de destination est hors de l'hôte) — ferme le panneau.
  • Les soumissions des formulaires POST (bascule, déconnexion) ne sont pas interceptées : le submit natif passe.

Voir aussi