Design system
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.
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)
| Statut | Token | Usage |
|---|---|---|
| Brouillon | neutral-400 | Devis, contrat, facture en draft |
| À traiter | info | Demandes en cours, action attendue |
| Validé / signé | success | Devis accepté, contrat signé, facture payée |
| En attente externe | warning | Agrément bailleur en attente, paiement partiel |
| Refusé / annulé | danger | Refus bailleur, annulation, impayé |
| Archivé | neutral-500 | Soft-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.
| Portail | Accent | Bandeau | Label topbar |
|---|---|---|---|
| Workspace (interne) | Pulse cyan | bg-pulse-500 | Logo Pulse seul |
| Admin | Atmo indigo | bg-atmo-600 | "Mode administrateur" pill |
| Client | Pulse cyan + logo white-label | bg-pulse-500 | Nom du client |
| Bailleur | Bleu marine | bg-blue-900 | "Espace bailleur" |
| Partenaire (apporteur) | Vert | bg-emerald-600 | "Espace partenaire" |
| Fournisseur | Orange | bg-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;
}
| Token | Taille | Usage |
|---|---|---|
text-xs | 12 px | Labels métadonnée, badges |
text-sm | 14 px | Body table, formulaires denses |
text-base | 16 px | Body courant |
text-lg | 18 px | Sous-titres |
text-xl | 20 px | Titres de section |
text-2xl | 24 px | Titres de page |
text-3xl | 30 px | Hero / dashboard |
text-4xl | 36 px | Titres 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).
| Token | Valeur | Usage |
|---|---|---|
rounded-sm | 2 px | Badges, chips |
rounded | 4 px | Inputs, boutons (défaut Nuxt UI) |
rounded-md | 6 px | Cards, modals |
rounded-lg | 8 px | Hero cards |
rounded-full | ∞ | Avatars, 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 UI | Usage principal |
|---|---|
UButton / UButtonGroup | Tous les boutons et groupes d'actions |
UInput / UTextarea | Champs texte |
USelect / USelectMenu | Selects (USelectMenu pour searchable) |
UForm / UFormGroup | Wrapping formulaires + gestion erreurs |
UCard | Conteneurs |
UModal / USlideover | Dialogs / drawers |
UTable | Tables (wrappé par DataTable partagé) |
UTabs | Onglets |
UBadge / UChip | Badges et pastilles de statut |
UCommandPalette | Cmd+K — recherche globale |
UToast | Toasts 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 viacva. - 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.
| Convention | Règle |
|---|---|
| Action positive | Icône pleine — i-lucide-check, i-lucide-plus |
| Action neutre | Icône ligne — i-lucide-eye, i-lucide-pencil |
| Action destructive | Icône ligne + couleur danger — i-lucide-trash-2 |
| Statut | Pastille 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)
serious ou critical.Règles techniques
- Tous les inputs ont un
<label>associé (for=) ou unaria-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-5002 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 — utiliserneutral-50ou 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.