Patterns & Shell
FormeSideNav
La barre de navigation latérale du DS 2.0. Composant phare du shell : il enrobe le
SideNav d'Astryx sous une API Forme data-mode — tu passes des
groups et des slots, le rendu (radius, densité, badges pilule) vient des
tokens Forme. Auto-stylé, donc l'aperçu ci-dessous est le vrai composant en conditions réelles.
FormeSideNav est la source unique de composition
de la barre latérale pour Forme®Admin, AI, Space et Frame. Il remplace l'ancien
<FormeSidebar> (markup .sidebar__* + CSS legacy) et les recompositions
par-app du SideNav Astryx. Une app, une composition : l'harmonie est garantie par construction.
Aperçu
Une barre avec deux sections, des icons (via @forme-ch/ui/icons), un item
isSelected, deux badges numériques, un slot
header (lockup), un topContent (recherche) et un
footer (compte).
import { FormeSideNav, FormeAdminLockup } from "@forme-ch/ui";
import { SearchInput, IconButton } from "@forme-ch/ui/controls";
import { ReceiptIcon, FileTextIcon, UsersThreeIcon } from "@forme-ch/ui/icons";
<FormeSideNav
ariaLabel="Navigation Forme®Admin"
header={<FormeAdminLockup height={20} />}
topContent={<SearchInput value={q} onChange={setQ} placeholder="Rechercher…" size="sm" />}
footer={<AccountBlock />}
groups={[
{ title: "Ventes", items: [
{ label: "Factures", href: "/admin/factures", icon: <ReceiptIcon size={18} />, isSelected: true, badge: 12 },
{ label: "Devis", href: "/admin/devis", icon: <FileTextIcon size={18} />, badge: 3 },
]},
{ title: "Organisation", items: [
{ label: "Clients", href: "/admin/clients", icon: <UsersThreeIcon size={18} /> },
]},
]}
/> Import
// Ré-exporté par l'index et par l'entrée /shell
import { FormeSideNav } from "@forme-ch/ui";
// ou
import { FormeSideNav } from "@forme-ch/ui/shell"; Props
| Prop | Type | Défaut | Description |
|---|---|---|---|
groups * | FormeSideNavGroup[] | — | Sections de navigation (data-mode). Chaque groupe : title, items, isHeaderHidden?. |
header | ReactNode | — | En-tête sticky : le commutateur des espaces <FormeEspaceSwitcher /> (recommandé), ou un lockup / heading custom. |
topContent | ReactNode | — | Contenu sticky sous le header — typiquement le déclencheur de recherche ⌘K. |
footer | ReactNode | — | Zone footer : compte, actions, promos. C'est ici qu'on compose le menu de compte. |
footerIcons | ReactNode | — | Barre d'icônes sticky tout en bas (réglages, déconnexion…). |
children | ReactNode | — | Contenu libre rendu après les groups (listes dynamiques : conversations, projets…). |
ariaLabel | string | "Navigation" | Nom accessible de la barre de navigation. |
linkComponent | LinkComponentType | — | Composant lien du framework (ex. next/link), branché via LinkProvider pour tous les items href. |
collapsible | boolean | { hasButton?: boolean } | false | Repli de la barre. Défaut false (DS 2.0) — pas de chevron « réduire » esseulé. Opt-in explicite par app si besoin. |
resizable | boolean | ResizableConfig | — | Redimensionnement. ResizableConfig = { defaultWidth?, minWidth?, maxWidth?, autoSaveId? }. |
className | string | — | Classe(s) sur le conteneur racine (intégration shell / drawer par-app). |
Item — FormeSideNavItem
| Prop | Type | Défaut | Description |
|---|---|---|---|
label * | string | — | Libellé de l'item. |
href | string | — | Cible du lien (rendu via linkComponent si fourni). |
icon | ReactNode | — | Icône outline (état normal), déjà dimensionnée : <ReceiptIcon size={18} />. |
selectedIcon | ReactNode | — | Icône filled affichée quand l'item est sélectionné. |
isSelected | boolean | — | Item actif (page courante). |
isDisabled | boolean | — | Item désactivé. |
badge | number | string | ReactNode | — | Compteur → Badge neutre en pilule. Nombre, chaîne ou nœud libre. |
endContent | ReactNode | — | Contenu de fin libre — prime sur badge. |
onClick | (e) => void | — | Handler de clic (items action sans href). |
key | string | — | Clé React (défaut = href ?? label). |
Groupe — FormeSideNavGroup
title (requis) · items (requis) ·
isHeaderHidden? — masque le titre visuellement tout en le laissant lisible
par les lecteurs d'écran.
Commutateur des espaces — FormeEspaceSwitcher
En tête de la barre, le commutateur des espaces remplace le lockup fixe : il coiffe
la FormeSideNav (slot header) et permet de passer
entre les espaces Forme (Admin, Intranet, Hermès, Studio, Space, Design). Direction
« marque constante » : le wordmark Forme® ne bouge jamais ;
le nom de lespace actif suit après une fine barre verticale, avec un chevron. Monochrome, navigation
en place. Le registre des destinations est partagé au socle
(FORME_ESPACES) : une app importe la liste et déclare son
current — ajouter un espace = une ligne, propagée partout.
import { FormeSideNav, FormeEspaceSwitcher, FORME_ESPACES } from "@forme-ch/ui/shell";
import { FormeWordmark } from "@forme-ch/ui";
import { ChevronDownIcon, CheckIcon } from "@forme-ch/ui/icons";
<FormeSideNav
linkComponent={Link}
header={
<FormeEspaceSwitcher
wordmark={<FormeWordmark height={22} />}
espaces={FORME_ESPACES}
current="admin"
linkComponent={Link}
chevron={<ChevronDownIcon size={16} />}
check={<CheckIcon size={16} />}
/>
}
groups={[]}
/> Props — FormeEspaceSwitcher
| Prop | Type | Défaut | Description |
|---|---|---|---|
wordmark * | ReactNode | — | Le glyphe Forme® canonique — passer <FormeWordmark /> (intouchable). |
espaces * | FormeEspace[] | — | Registre des destinations. Utiliser FORME_ESPACES (source unique du socle). |
current * | string | — | Id de lespace actif : admin, intranet, ai, frame, space, design. |
linkComponent | LinkLike | — | Composant lien du framework (next/link). Optionnel — les cibles sont surtout inter-domaines. Ne pas passer depuis un Server Component (crash RSC) : lomettre rend des <a>. |
chevron | ReactNode | — | Chevron douverture — pivote à louverture. |
check | ReactNode | — | Coche de lespace actif dans le menu. |
FORME_ESPACES.
Composition & slots
Le squelette sticky est fixe : header en haut, puis
topContent, puis les groups (zone scrollable),
children juste après pour les listes dynamiques, enfin
footer et footerIcons collés en bas. Le
menu de compte se compose dans le slot footer — voir la page
« Menu de compte ».
Accessibilité
ariaLabelnomme la région de navigation (défaut « Navigation »). Donne-lui un nom distinctif s'il y a plusieurs<nav>.- Le badge numérique passe par le
BadgeForme : il est lu comme texte, pas comme décoration. - L'item actif porte l'état sélectionné natif Astryx (
aria-current) — ne le simule pas en CSS. - Fournis
icondéjà dimensionnée (size=18) : c'est une décoration, lelabelreste le nom accessible.
Do & Don't
✓ À faire
Passer une groups calculée depuis l'app (actif, compteurs). Composer le compte dans footer. Brancher linkComponent pour un routing SPA.
✕ À éviter
Recréer une barre .sidebar__* maison. Mettre un <a> brut dans un item alors que href + linkComponent suffisent. Empiler deux barres par surface.