Forme DS

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.

Nouveau — DS 2.0. 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

PropTypeDéfautDescription
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

PropTypeDéfautDescription
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

PropTypeDéfautDescription
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.
Zéro-saut de design. En passant entre les espaces, seules les entrées de menu changent : wordmark, recherche et footer/compte restent identiques. Ne redéclare pas le registre par app — importe 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é

  • ariaLabel nomme 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 Badge Forme : 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 icon déjà dimensionnée (size=18) : c'est une décoration, le label reste 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.