Design system

Tokens, portails, typographie, composants Nuxt UI 4, accessibilité et mode sombre — référence complète du design system Pulse ERP.

Design system

Pulse ERP repose sur un design system sobre et dense conçu pour des usages mixtes : technicien SAV en magasin sous néon, commercial sur tablette en clientèle, ADV sur triple écran. La même grammaire visuelle traverse tous les portails ; seul un accent de couleur identifie l'espace de l'utilisateur.

Source de cette page : docs/03-design-system.md. Tokens définis dans app/assets/css/main.css via CSS variables exposées à Tailwind 4 via @theme.

Principes fondateurs

  • Lisibilité d'abord — contrastes nets, jamais de décoration pour la décoration.
  • Densité maîtrisée — tables compactes mais zones cliquables ≥ 44 × 44 px sur mobile.
  • Cohérence multi-portail — même grammaire, accent de couleur différent par portail.
  • Composants Nuxt UI 4 par défaut — aucun composant ré-implémenté si un équivalent stock existe.
  • Accessibilité WCAG 2.2 AA non négociable — testée en CI via axe-core.
  • Mobile-first pour les rôles terrain (commercial, technicien, logisticien) ; desktop-friendly pour les rôles bureau.

Tokens couleur

Palette de marque

@theme {
  /* Pulse — couleur primaire (logo, CTA) */
  --color-pulse-500: #06b6d4;   /* base — cyan teal */
  --color-pulse-600: #0891b2;   /* hover */
  --color-pulse-700: #0e7490;   /* active */

  /* Atmo — couleur secondaire (admin, accents techniques) */
  --color-atmo-500: #6366f1;    /* indigo */
  --color-atmo-600: #4f46e5;
  --color-atmo-700: #4338ca;

  /* Neutral — gris froids (stone alias) */
  --color-neutral-50:  #fafaf9;
  --color-neutral-500: #78716c;
  --color-neutral-900: #1c1917;
}
gray-* est interdit dans le code. Toujours utiliser neutral-* (alias de la palette stone).

Couleurs sémantiques

@theme {
  --color-success: #16a34a;   /* green-600 */
  --color-warning: #d97706;   /* amber-600 */
  --color-danger:  #dc2626;   /* red-600 */
  --color-info:    #2563eb;   /* blue-600 */
}

Couleurs métier (statuts)

StatutTokenUsage
Brouillonneutral-400Devis, contrat, facture en draft
À traiterinfoDemandes en cours, action attendue
Validé / signésuccessDevis accepté, contrat signé, facture payée
En attente externewarningAgrément bailleur en attente, paiement partiel
Refusé / annulédangerRefus bailleur, annulation, impayé
Archivéneutral-500Soft-deleted, ancien

Thèmes de portail

Chaque portail partage la même grammaire mais s'identifie par un bandeau d'accent de 4 px en haut de la topbar et un label visible.

PortailAccentBandeauLabel topbar
Workspace (interne)Pulse cyanbg-pulse-500Logo Pulse seul
AdminAtmo indigobg-atmo-600"Mode administrateur" pill
ClientPulse cyan + logo white-labelbg-pulse-500Nom du client
BailleurBleu marinebg-blue-900"Espace bailleur"
Partenaire (apporteur)Vertbg-emerald-600"Espace partenaire"
FournisseurOrangebg-orange-600"Espace fournisseur"
Audit (lecture seule)Gris foncé + watermark "AUDIT"bg-neutral-700"Mode audit (lecture seule)"

Typographie

@theme {
  --font-family-sans: 'Inter Variable', system-ui, sans-serif;
  --font-family-mono: 'JetBrains Mono', ui-monospace, monospace;
}
TokenTailleUsage
text-xs12 pxLabels métadonnée, badges
text-sm14 pxBody table, formulaires denses
text-base16 pxBody courant
text-lg18 pxSous-titres
text-xl20 pxTitres de section
text-2xl24 pxTitres de page
text-3xl30 pxHero / dashboard
text-4xl36 pxTitres exceptionnels (landing, états vides)

Graisses autorisées : font-normal (400), font-medium (500), font-semibold (600), font-bold (700). font-light est banni (illisible sous-pixel).


Espacements & rayons

Échelle Tailwind 4 (rem × 4 = px) stricte, pas de valeurs custom.

Espacements clés : px-4 py-2 (bouton primaire), px-3 py-2 / px-4 py-3 (cellule table desktop/mobile), gap-y-6 à gap-y-8 (sections), p-4 à p-6 (cards).

TokenValeurUsage
rounded-sm2 pxBadges, chips
rounded4 pxInputs, boutons (défaut Nuxt UI)
rounded-md6 pxCards, modals
rounded-lg8 pxHero cards
rounded-fullAvatars, pills

Ombres sobres : shadow-xs (survol), shadow-sm (card flottante), shadow-md (dropdown), shadow-lg (modal).


Composants Nuxt UI 4

Tous les composants interactifs passent par Nuxt UI 4. Aucun composant ré-implémenté si un équivalent stock existe. Les wrappers partagés vivent sous app/components/shared/.

Composant Nuxt UIUsage principal
UButton / UButtonGroupTous les boutons et groupes d'actions
UInput / UTextareaChamps texte
USelect / USelectMenuSelects (USelectMenu pour searchable)
UForm / UFormGroupWrapping formulaires + gestion erreurs
UCardConteneurs
UModal / USlideoverDialogs / drawers
UTableTables (wrappé par DataTable partagé)
UTabsOnglets
UBadge / UChipBadges et pastilles de statut
UCommandPaletteCmd+K — recherche globale
UToastToasts via useToast()

Conventions composants partagés

  • <script setup lang="ts"> exclusivement.
  • Props et émissions typées strict : defineProps<T>() / defineEmits<{ ... }>().
  • Pas de <style scoped> — Tailwind. Variantes complexes via cva.
  • Aucune logique métier dans les composants : toujours un composable.
  • Slots nommés explicitement (#empty, #header, #actions).
USelect avec un item value: '' casse reka-ui (dropdown HS + corruption rendu Vue). Utiliser le wrapper USelectNullable pour les champs optionnels.

Iconographie

Source : Iconify via @nuxt/icon. Collections privilégiées : lucide (sobre, ligne, cohérent) et tabler. mdi (Material Design Icons, héritage Quasar) est banni.

ConventionRègle
Action positiveIcône pleine — i-lucide-check, i-lucide-plus
Action neutreIcône ligne — i-lucide-eye, i-lucide-pencil
Action destructiveIcône ligne + couleur dangeri-lucide-trash-2
StatutPastille de couleur + icône optionnelle (jamais l'icône seule)

Tailles : size-4 (table), size-5 (bouton), size-6 (header), size-8 (état vide).


Accessibilité (WCAG 2.2 AA)

L'accessibilité est non négociable. La CI bloque si axe-core détecte une violation serious ou critical.

Règles techniques

  • Tous les inputs ont un <label> associé (for=) ou un aria-label.
  • Tous les boutons icône ont un aria-label.
  • La couleur ne véhicule jamais une info seule — toujours doublée par icône ou texte.
  • Ratios de contraste : ≥ 4,5:1 (texte normal), ≥ 3:1 (texte large, UI).
  • Focus visible : ring pulse-500 2 px, non supprimable via CSS.
  • Skip link <a href="#main">Aller au contenu</a> en début de page.
  • <h1> unique par page, hiérarchie cohérente.
  • Pas de tabindex > 0, pas d'auto-play vidéo/audio.
  • Cibles tactiles min 44 × 44 px, espacement min 8 px entre cibles.

Tests

Playwright + @axe-core/playwright sur chaque page principale (CI bloquante). Audit manuel trimestriel par un consultant a11y.


Mode sombre

Activé via @nuxt/color-mode, défaut system.

@variant dark {
  --color-pulse-500: #22d3ee;   /* plus clair en dark pour contraste */
  --color-neutral-50: #0c0a09;  /* inversion */
  --color-neutral-100: #1c1917;
  /* etc. */
}
  • Pas de blanc pur (#fff) en surface dark — utiliser neutral-50 ou plus foncé.
  • Images sombres → variante claire en dark (ou bordure pour les distinguer du fond).
  • Charts : palette dédiée dark, plus saturée pour le contraste.

Responsive & layouts

Breakpoints Tailwind 4 standard : sm 640 px, md 768 px, lg 1024 px, xl 1280 px, 2xl 1536 px.

Page Liste

PageHero → FilterBar (URL-persisted) → DataTable (cards empilées sur mobile) → Footer export CSV/XLSX.

Page Détail

PageHero + stats clés → Onglets (Détails, Historique, Documents, Commentaires) → Contenu onglet.

Page Wizard

PageHero compact + stepper horizontal (vertical sur mobile) → formulaire structuré → footer sticky Précédent / Suivant.

Sidebar : visible lg:+, drawer lg:max-. Modal : centrée desktop, fullscreen mobile.