Design: Host Shell¶
Brand: Papillon HR Suite¶
Date: 24/02/2026¶
Version: 1.0¶
Author: DESIGNER (Claude — Senior UX/UI Designer)¶
For the DEVELOPER: This document is your single source of truth for Sprint 0 implementation. Every layout, state, interaction, token, and French string is specified here. If something is ambiguous, the design is wrong — flag it. You should never need to guess.
Table of Contents¶
- Design Decisions (Open Questions Resolved)
- Design Token System
- Layout Wireframes (All Breakpoints)
- Authentication Screens
- App Shell Layout
- Dashboard
- Navigation
- Sync Status Indicator
- Responsive Behavior
- Dark Mode
- Notification Panel
- PWA Spec
- Shared Component Specs
- French Micro-copy Document
- Brand Compliance Checklist
1. Design Decisions¶
These resolve the open questions from docs/specs/host-shell.md §10. Each decision includes rationale.
Q1: Bottom navigation — 4 tabs or 5?¶
Decision: 4 tabs.
| Tab | Icon | Label | Rationale |
|---|---|---|---|
| 1 | home |
Accueil | Dashboard — universal landing |
| 2 | calendar |
Congés | Most-used by P4 (Employee), P2 (RH) |
| 3 | qr-code |
Pointage | Daily action for P4, needs one-tap access |
| 4 | menu |
Plus | Opens full module list as bottom sheet |
Why 4, not 5: On a 360px screen, 5 tabs creates cramped 72px-wide targets. 4 tabs at 90px each sits comfortably above the 48px minimum touch target. The "Plus" tab opens all other modules — no one is locked out. Notification bell lives in the top bar, not the bottom nav (it's a status indicator, not a primary action).
Q2: Sidebar grouping¶
Decision: Functional groups with subtle visual separators, not nested menus.
Groups: - Quotidien (Daily): Tableau de bord, Congés & Absences, Pointage QR - Finance: Lignes budgétaires, Engagements, Paie, Notes de frais - Ressources Humaines: Employés, Performance, Compétences, Formation, Recrutement - Commercial: CRM Interne - Administration* (admin-only): Paramètres, Utilisateurs, Audit, Abonnement
(*) = locked "Bientôt" items
Why this grouping: Maps to mental models of the 3 primary desktop personas. P2 (RH) thinks "daily tasks" vs. "employee management." P3 (Comptable) thinks "finance." Groups use label headers (not collapsible accordions) to keep all items scannable — the sidebar should never hide information behind a click.
Q3: Dashboard layout¶
Decision: Stacked cards on mobile (1 column), 2×2 grid on tablet, 4-column grid on desktop. Two KPI cards visible above the fold on phone.
Why: On a 360px screen with 16px padding, a card is 328px wide — perfect for readability. Showing 2 KPI cards above the fold (each ~160px tall including padding) gives immediate value without scrolling. Quick actions section sits just below the fold, reachable with a thumb-swipe.
Q4: Locked module click behavior¶
Decision: Bottom sheet preview (mobile) / Popover card (desktop).
On tap, locked modules show a mini preview card with: - Module icon (full color, not greyed) - Module name and one-line description - "Bientôt disponible" badge - "Être notifié à la sortie" toggle (opt-in for launch notification) - Subtle illustration or icon hinting at the module's purpose
Why not a toast: Toasts are dismissive — they say "go away." A preview card says "look what's coming" and builds anticipation. The notification toggle creates engagement. Why not greyed-out icons in the sidebar: Grey communicates "disabled/broken." Instead, locked items use 50% opacity with a small ✦ Bientôt badge — still visible, clearly different, but aspirational.
Q5: Notification panel¶
Decision: Slide-in panel from the right (desktop/tablet), full-screen bottom sheet (mobile).
Why: A dropdown is too small for notification content (icon + title + body + timestamp). A right slide-in panel (320px wide on desktop) gives ample space and doesn't obscure the main content entirely. On mobile, a bottom sheet is the native pattern — users expect it from WhatsApp, Wave, and M-Pesa.
Q6: Login screen¶
Decision: Centered card on mobile. Split layout on desktop (branding left, form right).
Why: On mobile (360px), a split layout wastes precious space. A centered form with the Papillon logo above feels like a premium app login (think Wise or Wave). On desktop (1024px+), the left panel shows a warm branded illustration with the tagline, giving the first impression its gravitas. The form sits on the right in a clean white card.
Q7: Illustration style¶
Decision: Warm geometric flat illustrations with African-inspired color tones. No human faces (avoids representation bias). Abstract shapes suggesting growth, connection, community.
Used in: login screen branding panel, empty states, offline landing, locked module previews.
Style references: Abstract shapes like Notion's empty states but warmer — amber, teal, and earth tones. Circles and gentle curves (not sharp corporate angles). Could be implemented as simple SVG compositions to respect the performance budget.
Sprint 0 implementation: Use simple SVG geometric compositions (circles, arcs, dots) that can be created inline — no external illustration files needed. Each empty state gets a unique but consistent composition.
Q8: Animation philosophy¶
Decision: Signature transitions only, not pervasive micro-animations. Three signature moments:
- Page transition: Content area fades in with a subtle 200ms upward slide (12px). Feels alive without being slow.
- Sync pulse: The sync indicator uses a gentle breathing pulse (1.5s ease-in-out loop) when syncing. Calm, not urgent.
- Success flash: On successful actions (save, approve, submit), a brief green checkmark scales in (150ms) then fades (300ms). Satisfying.
Why minimal: Heavy animations kill performance on low-end Android. Three memorable transitions create more personality than 50 subtle ones. All animations respect prefers-reduced-motion.
Q9: Celebration moments¶
Decision: Two celebration moments in the shell:
- First login ever (per user): Subtle confetti burst (3s, then gone) with "Bienvenue sur Papillon ! 🦋" toast. Only triggers once. Stored in localStorage.
- PWA install success: Warm "Papillon est installé ! Accédez-y depuis votre écran d'accueil." toast with a butterfly icon animation.
Why these two: They mark genuine milestones — the user's first experience and their commitment to the platform. Module-specific celebrations (payroll completion, etc.) are designed per module, not in the shell.
2. Design Token System¶
All values below are implemented as CSS custom properties in theme.ts.
Token names follow the pattern: --{category}-{element}-{variant}.
2.1 Color Tokens — Light Mode¶
Brand Colors¶
| Token | Value | Usage |
|---|---|---|
--color-primary |
#F59E0B |
Primary buttons, active nav, links, accents |
--color-primary-hover |
#D97706 |
Primary button hover/press state |
--color-primary-light |
#FEF3C7 |
Primary tint backgrounds, selected states |
--color-primary-subtle |
#FFFBEB |
Very light primary wash, card highlights |
--color-secondary |
#0D6B6E |
Sidebar background, secondary buttons, section headers |
--color-secondary-hover |
#0A5456 |
Secondary button hover state |
--color-secondary-light |
#CCFBF1 |
Secondary tint backgrounds |
--color-secondary-dark |
#083F41 |
Sidebar text/icons active state |
Neutral Colors¶
| Token | Value | Usage |
|---|---|---|
--color-bg-primary |
#FFFDF7 |
Page background (warm off-white) |
--color-bg-secondary |
#FFF9EE |
Card backgrounds, elevated surfaces |
--color-bg-tertiary |
#F5F0E8 |
Input backgrounds, table row alternating |
--color-surface |
#FFFFFF |
Modal, dropdown, floating surfaces |
--color-border |
#E8E0D4 |
Card borders, dividers, input borders |
--color-border-light |
#F0EBE3 |
Subtle separators, group dividers |
--color-border-focus |
#F59E0B |
Input focus ring (= primary) |
Text Colors¶
| Token | Value | Usage |
|---|---|---|
--color-text-primary |
#1C1917 |
Headings, primary body text |
--color-text-secondary |
#57534E |
Secondary text, descriptions, placeholders |
--color-text-tertiary |
#A8A29E |
Disabled text, timestamps, hints |
--color-text-inverse |
#FFFDF7 |
Text on dark backgrounds |
--color-text-on-primary |
#1C1917 |
Text on amber/gold backgrounds (dark for contrast) |
Semantic Colors¶
| Token | Value | Usage |
|---|---|---|
--color-success |
#16A34A |
Success states, synced indicator, approved |
--color-success-light |
#DCFCE7 |
Success background tint |
--color-warning |
#F59E0B |
Warning states (shares primary for warmth) |
--color-warning-light |
#FEF9C3 |
Warning background tint |
--color-error |
#DC2626 |
Error states, destructive actions |
--color-error-light |
#FEE2E2 |
Error background tint |
--color-info |
#2563EB |
Informational states, links |
--color-info-light |
#DBEAFE |
Info background tint |
Sync Status Colors¶
| Token | Value | Usage |
|---|---|---|
--color-sync-synced |
#16A34A |
Green — all synced |
--color-sync-pending |
#F59E0B |
Orange/amber — operations queued |
--color-sync-syncing |
#2563EB |
Blue — actively syncing |
--color-sync-offline |
#78716C |
Warm grey — no connectivity |
Sidebar Colors¶
| Token | Value | Usage |
|---|---|---|
--color-sidebar-bg |
#0D6B6E |
Sidebar background |
--color-sidebar-bg-hover |
#0A5456 |
Nav item hover |
--color-sidebar-bg-active |
#083F41 |
Active nav item background |
--color-sidebar-text |
#CCFBF1 |
Sidebar text default |
--color-sidebar-text-active |
#FFFFFF |
Sidebar active item text |
--color-sidebar-icon |
#5EEAD4 |
Sidebar icons default |
--color-sidebar-icon-active |
#F59E0B |
Sidebar active icon (amber accent) |
--color-sidebar-divider |
#0A5456 |
Group separator lines |
--color-sidebar-group-label |
#5EEAD4 |
Group header text (e.g., "QUOTIDIEN") |
2.2 Typography Tokens¶
Font Family: Inter, system-ui, -apple-system, sans-serif
(Inter supports full Latin Extended including all French diacritics: é è ê ë ç à â ù û ô î ï)
| Token | Size | Weight | Line Height | Letter Spacing | Usage |
|---|---|---|---|---|---|
--font-display |
32px |
700 |
1.2 |
-0.02em |
Login screen title, major headings |
--font-h1 |
24px |
700 |
1.3 |
-0.01em |
Page titles |
--font-h2 |
20px |
600 |
1.35 |
0 |
Section titles, card headers |
--font-h3 |
18px |
600 |
1.4 |
0 |
Sub-section titles |
--font-h4 |
16px |
600 |
1.4 |
0 |
Small section titles, bold labels |
--font-body |
16px |
400 |
1.5 |
0 |
Default body text |
--font-body-medium |
16px |
500 |
1.5 |
0 |
Emphasized body text, nav labels |
--font-small |
14px |
400 |
1.4 |
0 |
Secondary descriptions, table cells |
--font-caption |
12px |
400 |
1.4 |
0.02em |
Timestamps, badges, helper text |
--font-overline |
11px |
600 |
1.2 |
0.08em |
Sidebar group labels, uppercase labels |
2.3 Spacing Tokens¶
Base unit: 4px. All spacing uses multiples of 4.
| Token | Value | Usage |
|---|---|---|
--space-1 |
4px |
Inline icon gaps, tight padding |
--space-2 |
8px |
Icon-to-text gap, compact padding |
--space-3 |
12px |
Input padding, small gaps between related items |
--space-4 |
16px |
Standard padding, gap between form fields |
--space-5 |
20px |
Card internal padding (mobile) |
--space-6 |
24px |
Card internal padding (desktop), section gaps |
--space-8 |
32px |
Between cards, between sections |
--space-10 |
40px |
Large section separators |
--space-12 |
48px |
Page top/bottom padding |
--space-16 |
64px |
Major layout gaps |
2.4 Border Radius Tokens¶
| Token | Value | Usage |
|---|---|---|
--radius-sm |
6px |
Badges, small tags |
--radius-md |
8px |
Buttons, inputs |
--radius-lg |
12px |
Cards, modals |
--radius-xl |
16px |
Large cards, panels, bottom sheets |
--radius-full |
9999px |
Avatar circles, pills |
2.5 Shadow / Elevation Tokens¶
| Token | Value | Usage |
|---|---|---|
--shadow-sm |
0 1px 2px rgba(28,25,23,0.05) |
Subtle lift — inputs, small cards |
--shadow-md |
0 4px 12px rgba(28,25,23,0.08) |
Cards, elevated surfaces |
--shadow-lg |
0 8px 24px rgba(28,25,23,0.12) |
Modals, dropdowns, popovers |
--shadow-xl |
0 16px 48px rgba(28,25,23,0.16) |
Toasts, floating action panels |
--shadow-sidebar |
2px 0 8px rgba(13,107,110,0.15) |
Sidebar edge shadow |
--shadow-focus |
0 0 0 3px rgba(245,158,11,0.3) |
Focus ring (amber glow) |
2.6 Animation Tokens¶
| Token | Value | Usage |
|---|---|---|
--duration-instant |
100ms |
Button state change, toggle |
--duration-fast |
150ms |
Success checkmark, small transitions |
--duration-normal |
200ms |
Page content fade-in, slide-in |
--duration-slow |
300ms |
Modal open/close, panel slide |
--duration-sync-pulse |
1500ms |
Sync indicator breathing animation |
--easing-default |
cubic-bezier(0.4, 0, 0.2, 1) |
Standard easing |
--easing-enter |
cubic-bezier(0, 0, 0.2, 1) |
Elements entering (decelerate) |
--easing-exit |
cubic-bezier(0.4, 0, 1, 1) |
Elements leaving (accelerate) |
2.7 Layout Tokens¶
| Token | Value | Usage |
|---|---|---|
--sidebar-width |
260px |
Expanded sidebar |
--sidebar-width-collapsed |
68px |
Collapsed sidebar (icons only) |
--topbar-height |
56px |
Top bar height (mobile) |
--topbar-height-desktop |
64px |
Top bar height (desktop) |
--bottomnav-height |
64px |
Bottom navigation bar (mobile) |
--content-max-width |
1200px |
Max width for content area |
--content-padding-mobile |
16px |
Content area horizontal padding (phone) |
--content-padding-tablet |
24px |
Content area horizontal padding (tablet) |
--content-padding-desktop |
32px |
Content area horizontal padding (desktop) |
2.8 Z-Index Scale¶
| Token | Value | Usage |
|---|---|---|
--z-base |
0 |
Default stacking |
--z-sidebar |
100 |
Sidebar overlay on mobile |
--z-topbar |
200 |
Top bar (above sidebar on mobile) |
--z-dropdown |
300 |
Dropdowns, popovers |
--z-modal-backdrop |
400 |
Modal backdrop overlay |
--z-modal |
500 |
Modal content |
--z-toast |
600 |
Toast notifications (topmost) |
--z-sync-panel |
350 |
Sync detail panel |
3. Layout Wireframes¶
3.1 Phone — 360px¶
┌──────────────────────────────────────┐
│ [☰] Papillon [●sync] [🔔2] │ ← Top bar (56px)
├──────────────────────────────────────┤
│ │
│ ┌──────────────────────────────┐ │
│ │ 👥 12 employés actifs │ │ ← KPI Card 1
│ │ +2 ce mois │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 📋 3 demandes en attente │ │ ← KPI Card 2
│ │ congés à approuver │ │
│ └──────────────────────────────┘ │
│ │ ← Scrollable content
│ ── Actions rapides ────────────── │
│ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ 🏖 Congé │ │ ✅ Approuver │ │ ← Quick actions (2-col)
│ └──────────┘ └──────────────┘ │
│ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ 📍 Pointer│ │ 📊 Budget │ │
│ └──────────┘ └──────────────┘ │
│ │
├──────────────────────────────────────┤
│ 🏠 📅 📱 ≡ │ ← Bottom nav (64px)
│ Accueil Congés Pointage Plus │
└──────────────────────────────────────┘
3.2 Tablet — 768px¶
┌────┬───────────────────────────────────────────────────┐
│ │ Tableau de bord [●sync] [🔔2] [👤] │ ← Top bar (64px)
│ 📊 ├───────────────────────────────────────────────────┤
│ 📅 │ │
│ 📱 │ ┌─────────────────┐ ┌─────────────────┐ │
│ 💰 │ │ 👥 12 employés │ │ 📋 3 demandes │ │ ← 2-col KPI
│ 📝 │ │ actifs │ │ en attente │ │
│ 🏢 │ └─────────────────┘ └─────────────────┘ │
│ 💼 │ │
│ 📊 │ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ 📊 Budget: │ │ 📝 5 engage- │ │ ← 2-col KPI
│ ·· │ │ 68% utilisé │ │ ments ce mois │ │
│ │ └─────────────────┘ └─────────────────┘ │
│ │ │
│ ⚙️ │ ── Actions rapides ──────────────────── │
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ │ Congé │ │Approuv.│ │Pointer │ │ Budget │ │
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │
│ │ │
└────┴───────────────────────────────────────────────────┘
68px Remaining width
(icons)
3.3 Desktop — 1024px¶
┌──────────────┬──────────────────────────────────────────────────────┐
│ │ Tableau de bord [●sync] [🔔2] [🌙] [👤 Menu]│
│ 🦋 Papillon ├──────────────────────────────────────────────────────┤
│ Acme Corp │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐│
│ ─QUOTIDIEN── │ │👥 12 │ │📋 3 │ │📊 68% │ │📝 5 ││
│ 📊 Tableau │ │employés │ │demandes │ │budget │ │engagements│
│ de bord │ │actifs │ │en attente│ │utilisé │ │ce mois ││
│ 📅 Congés & │ └──────────┘ └──────────┘ └──────────┘ └──────────┘│
│ Absences │ │
│ 📱 Pointage │ ── Actions rapides ────────────────────── │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ ─FINANCE──── │ │🏖 Nouveau│ │✅ Approu-│ │📍 Pointer│ │
│ 💰 Lignes │ │ congé │ │ver (3) │ │arrivée │ │
│ budgét. │ └──────────┘ └──────────┘ └──────────┘ │
│ 📝 Engage- │ │
│ ments │ ── Activité récente ───────────────── │
│ 💳 Paie │ ┌──────────────────────────────────────────┐ │
│ 🧾 Notes* │ │ • Moussa Koné — congé approuvé (il y a 2h)│ │
│ │ │ • Paie février — en cours de validation │ │
│ ─RH───────── │ │ • 2 nouveaux employés ajoutés │ │
│ 👥 Employés │ └──────────────────────────────────────────┘ │
│ 🎯 Perf.* │ │
│ 🧠 Compét.* │ │
│ 📚 Format.* │ │
│ 🔍 Recrut.* │ │
│ │ │
│ ─COMMERCIAL─ │ │
│ 💼 CRM │ │
│ │ │
│ ─ADMIN────── │ │
│ ⚙️ Paramètr. │ │
│ │ │
│ ──────────── │ │
│ 👤 Éric B. │ │
│ DG │ │
└──────────────┴──────────────────────────────────────────────────────┘
260px Remaining width (max 1200px content)
3.4 Large Desktop — 1440px¶
Same structure as 1024px. Content area centers within max-width 1200px. Extra horizontal space becomes equal left/right margins within the content zone. Dashboard KPI grid remains 4 columns. Activity feed may use a 2-column layout (feed left, quick stats right) if content justifies it.
4. Authentication Screens¶
4.1 Login Screen¶
Layout — Mobile (360px)¶
┌──────────────────────────────────────┐
│ (warm bg #FFFDF7) │
│ │
│ 🦋 │
│ Papillon │
│ Votre assistant RH de confiance │
│ │
│ ┌──────────────────────────────┐ │
│ │ │ │
│ │ Adresse e-mail │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ [email protected] │ │ │
│ │ └──────────────────────┘ │ │
│ │ │ │
│ │ Mot de passe │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ •••••••• [👁] │ │ │
│ │ └──────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Se connecter │ │ │ ← Primary button, full width
│ │ └──────────────────────┘ │ │
│ │ │ │
│ │ Mot de passe oublié ? │ │ ← Text link, centered
│ │ │ │
│ └──────────────────────────────┘ │
│ │
│ © 2026 ALTARYS ENTERPRISE │
└──────────────────────────────────────┘
Layout — Desktop (1024px+)¶
┌────────────────────────────┬─────────────────────────────────┐
│ │ │
│ (Deep teal panel) │ (White card surface) │
│ #0D6B6E │ │
│ │ 🦋 Papillon │
│ │ │
│ [Geometric SVG │ Connectez-vous à votre │
│ illustration: │ espace RH │
│ warm amber circles │ │
│ + teal arcs │ Adresse e-mail │
│ suggesting growth │ ┌───────────────────────┐ │
│ and connection] │ │ [email protected] │ │
│ │ └───────────────────────┘ │
│ │ │
│ │ Mot de passe │
│ "Votre assistant RH │ ┌───────────────────────┐ │
│ de confiance" │ │ •••••••• [👁] │ │
│ │ └───────────────────────┘ │
│ Simplifiez la gestion │ │
│ de vos ressources │ ┌───────────────────────┐ │
│ humaines. │ │ Se connecter │ │
│ │ └───────────────────────┘ │
│ │ │
│ │ Mot de passe oublié ? │
│ │ │
│ © 2026 ALTARYS │ │
│ ENTERPRISE │ │
└────────────────────────────┴─────────────────────────────────┘
50% width 50% width
States¶
Empty (initial): - Email field: empty, placeholder "[email protected]" - Password field: empty, no placeholder - Button: enabled (validation happens on submit)
Loading (submitting): - Button text changes to spinner + "Connexion…" - Both inputs disabled - No other UI change
Error — invalid credentials: - Inputs retain values (never clear the password on error — user may have mistyped one char) - Error toast appears at top: "Adresse e-mail ou mot de passe incorrect" - Error toast auto-dismisses after 5 seconds - No field-level error indicators (we don't reveal which field is wrong — security)
Error — network failure: - Toast: "Impossible de se connecter. Vérifiez votre connexion Internet." - Button re-enabled for retry
Error — account locked: - Toast: "Votre compte est temporairement verrouillé. Réessayez dans 15 minutes ou réinitialisez votre mot de passe." - "Mot de passe oublié ?" link pulses subtly to draw attention
Success: - Brief green checkmark animation on button (150ms) - Redirect to dashboard - First-time user: confetti + welcome toast (see §1 Q9)
Validation rules: - Email: validate format on blur. If invalid: "Veuillez saisir une adresse e-mail valide" below the field - Password: no client-side validation (server validates) - Submit: disabled only while request is in-flight
Accessibility:
- aria-label="Formulaire de connexion" on form
- aria-describedby linking error messages to inputs
- Password visibility toggle: aria-label="Afficher le mot de passe" / "Masquer le mot de passe"
- Focus order: email → password → submit → forgot password link
- Skip link not needed (no navigation on login page)
4.2 Password Recovery Screen¶
Layout — Mobile (360px)¶
┌──────────────────────────────────────┐
│ (warm bg) │
│ │
│ ← Retour à la connexion │ ← Back link with arrow
│ │
│ 🦋 │
│ Mot de passe oublié │
│ │
│ Saisissez votre adresse e-mail. │
│ Si un compte existe, vous │
│ recevrez un lien de │
│ réinitialisation. │
│ │
│ Adresse e-mail │
│ ┌──────────────────────────────┐ │
│ │ [email protected] │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ Réinitialiser mon mot │ │
│ │ de passe │ │
│ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
States¶
Empty (initial): Email field empty with placeholder.
Loading: Button shows spinner + "Envoi en cours…"
Success (always shown, regardless of whether the email exists — security): - Form is replaced by a success card:
✉️
E-mail envoyé !
Si l'adresse [email protected] est
associée à un compte, vous recevrez
un e-mail avec un lien de
réinitialisation.
Vérifiez aussi vos courriers
indésirables.
[Retour à la connexion]
Error — network failure: Toast: "Impossible d'envoyer la demande. Vérifiez votre connexion."
Offline: Toast: "Vous êtes hors ligne. Connectez-vous à Internet pour réinitialiser votre mot de passe."
5. App Shell Layout¶
5.1 Zone Architecture¶
The app shell consists of 3 persistent zones that wrap all module content:
┌─────────────────────────────────────────────────────┐
│ ZONE 2: TOP BAR │
├──────────┬──────────────────────────────────────────┤
│ │ │
│ ZONE 1 │ ZONE 3: CONTENT │
│ SIDEBAR │ (module pages render here) │
│ │ │
│ │ │
│ │ │
└──────────┴──────────────────────────────────────────┘
On mobile, Zone 1 becomes the bottom navigation bar:
┌──────────────────────────────────────┐
│ ZONE 2: TOP BAR │
├──────────────────────────────────────┤
│ │
│ ZONE 3: CONTENT │
│ │
│ │
├──────────────────────────────────────┤
│ ZONE 1: BOTTOM NAV │
└──────────────────────────────────────┘
5.2 Zone 1 — Sidebar (Desktop/Tablet)¶
Width: 260px expanded, 68px collapsed (icon-only)
Background: --color-sidebar-bg (#0D6B6E)
Behavior:
- Desktop (≥1024px): Always expanded
- Tablet (768px–1023px): Collapsed by default. Tap hamburger in top bar or swipe right from left edge to expand as an overlay (with backdrop).
- Phone (<768px): Hidden. Replaced by bottom navigation.
Structure (top to bottom):
┌──────────────────────┐
│ 🦋 Papillon │ ← Brand logo + product name
│ ─────────────────── │
│ 🏢 Acme Corp │ ← Tenant name + logo (or initials)
│ Abidjan, CI │ Tenant city + country code
├──────────────────────┤
│ QUOTIDIEN │ ← Group label (--font-overline, uppercase)
│ 📊 Tableau de bord │
│ 📅 Congés & Absences│
│ 📱 Pointage QR │
│ │
│ FINANCE │
│ 💰 Lignes budgét. │
│ 📝 Engagements │
│ 💳 Paie │
│ 🧾 Notes de frais ✦ │ ← Locked item with "Bientôt" badge
│ │
│ RESSOURCES HUMAINES │
│ 👥 Employés │
│ 🎯 Performance ✦ │
│ 🧠 Compétences ✦ │
│ 📚 Formation ✦ │
│ 🔍 Recrutement ✦ │
│ │
│ COMMERCIAL │
│ 💼 CRM Interne │
├──────────────────────┤
│ ADMINISTRATION │ ← Only for admin roles
│ ⚙️ Paramètres │
│ 👥 Utilisateurs │
│ 📋 Journal d'audit │
│ 💳 Abonnement │
├──────────────────────┤
│ ─────────────────── │
│ 👤 Éric Bonville │ ← User avatar/initials + name
│ Directeur Général│ Role label
│ [Se déconnecter] │ Logout link (subtle)
└──────────────────────┘
Nav item dimensions:
- Height: 44px (with 4px vertical gap between items)
- Icon: 20px, left-aligned with 16px left padding
- Label: --font-body-medium, 12px left of icon
- Active indicator: 3px amber left border + --color-sidebar-bg-active background
- Hover: --color-sidebar-bg-hover background, 100ms transition
Collapsed state (68px): - Only icons visible, centered - Tooltip on hover showing the full label - Active indicator: amber dot below icon - Tenant section: logo/initials only - User section: avatar only
5.3 Zone 1 — Bottom Navigation (Phone)¶
Height: 64px + safe area inset bottom (for phones with home indicator)
Background: --color-surface (#FFFFFF) with --shadow-lg upward
| Position | Icon | Label | Tap action |
|---|---|---|---|
| 1 | home (filled when active) |
Accueil | Go to dashboard |
| 2 | calendar |
Congés | Go to leave module |
| 3 | qr-code |
Pointage | Go to QR attendance |
| 4 | menu (grid) |
Plus | Open module bottom sheet |
Item dimensions:
- Touch target: 90px wide × 64px tall (exceeds 48px minimum)
- Icon: 24px
- Label: --font-caption (12px)
- Active state: icon fills, label becomes --color-primary, subtle 2px top border amber
"Plus" bottom sheet: On tap, slides up a bottom sheet listing ALL modules (same groups as sidebar). The sheet has a drag handle, can be dismissed by swipe-down or tapping outside. Max height: 80vh. Scrollable if needed. Locked modules show ✦ Bientôt badge inline.
5.4 Zone 2 — Top Bar¶
Height: 56px (mobile), 64px (desktop)
Background: --color-surface (#FFFFFF)
Border: 1px bottom --color-border-light
Mobile layout (360px)¶
┌──────────────────────────────────────┐
│ [☰] Papillon 🦋 [●] [🔔2] │
└──────────────────────────────────────┘
48px centered sync bell
- Hamburger
[☰]: only on tablet (opens sidebar overlay). On phone, no hamburger (bottom nav handles it). - On phone: Left side shows "Papillon" brand mark. Center is implicit.
- Right side: sync dot (16px) + notification bell (24px icon, with badge).
Desktop layout (1024px+)¶
┌──────────────────────────────────────────────────────────────────┐
│ Tableau de bord [● Synchronisé] [🔔2] [🌙] [👤▾]│
└──────────────────────────────────────────────────────────────────┘
Page title / breadcrumb sync label bell dark user
- Left: Page title (
--font-h2). If within a sub-page, shows breadcrumb:Employés > Moussa Koné - Right (left-to-right): Sync status with label, notification bell with count, dark mode toggle, user menu dropdown trigger (avatar + name + chevron)
User menu dropdown (desktop, triggered by clicking avatar/name):
┌──────────────────────┐
│ 👤 Éric Bonville │
│ [email protected] │
│ ────────────────── │
│ Mon profil │
│ Préférences │
│ ────────────────── │
│ Se déconnecter │
└──────────────────────┘
--shadow-lg.
5.5 Zone 3 — Content Area¶
Behavior:
- Scrolls independently from sidebar and top bar (sidebar and top bar are fixed/sticky)
- Padding: 16px (mobile), 24px (tablet), 32px (desktop)
- Max content width: 1200px, centered with auto margins on screens wider than 1200px + sidebar width
- Background: --color-bg-primary (#FFFDF7)
Module loading: When navigating between modules (React.lazy code splitting): 1. Content area shows skeleton loader matching the target module's layout 2. Skeleton fades in immediately (no delay) 3. When module loads, skeleton fades out and content fades in (200ms crossfade) 4. If loading takes >3s (slow 3G), add subtle pulsing text below skeleton: "Chargement du module…"
Empty state (when a module has no data): - Centered vertically in content area - SVG illustration (geometric, warm) - Title: explains what this module does - Subtitle: guides user to first action - Primary CTA button - Example for Employés: "Aucun employé enregistré — Ajoutez votre premier employé pour commencer"
6. Dashboard¶
6.1 Structure Overview¶
The dashboard is the landing page after login. It provides an at-a-glance summary and quick access to common actions. In Sprint 0, it displays placeholder data and static layout. The structure is designed for future role-aware content.
6.2 Layout Zones¶
┌──────────────────────────────────────────────────┐
│ ZONE A: KPI Cards (summary metrics) │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ KPI1 │ │ KPI2 │ │ KPI3 │ │ KPI4 │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
├──────────────────────────────────────────────────┤
│ ZONE B: Quick Actions (shortcuts) │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ Act1 │ │ Act2 │ │ Act3 │ │
│ └──────┘ └──────┘ └──────┘ │
├──────────────────────────────────────────────────┤
│ ZONE C: Recent Activity (feed) │
│ ┌──────────────────────────────────────┐ │
│ │ Activity list (scrollable) │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
6.3 KPI Cards¶
Each KPI card follows a consistent structure:
┌──────────────────────────────┐
│ [icon] │
│ │
│ 12 │ ← Metric value (--font-display, 32px bold)
│ employés actifs │ ← Metric label (--font-small, secondary text)
│ │
│ +2 ce mois ↑ │ ← Trend indicator (--font-caption, green/red)
└──────────────────────────────┘
Card specs:
- Background: --color-bg-secondary
- Border: 1px --color-border
- Border-radius: --radius-lg (12px)
- Padding: --space-5 (20px) on mobile, --space-6 (24px) on desktop
- Shadow: --shadow-sm
- Icon: 24px, --color-primary tint
- Hover (desktop): --shadow-md, translate Y -2px, 200ms transition
Sprint 0 KPI cards (placeholder data):
| # | Icon | Value | Label | Trend |
|---|---|---|---|---|
| 1 | users |
12 | employés actifs | +2 ce mois ↑ |
| 2 | calendar-clock |
3 | demandes de congé en attente | — |
| 3 | pie-chart |
68% | du budget utilisé | +12% vs mois dernier |
| 4 | file-text |
5 | engagements ce mois | — |
Responsive grid: - 360px: 1 column, cards stack vertically. First 2 visible above fold. - 768px: 2 columns - 1024px: 4 columns - 1440px: 4 columns (cards don't grow beyond ~280px; extra space is margin)
Gap between cards: --space-4 (16px) on mobile, --space-6 (24px) on desktop.
6.4 Quick Actions¶
Quick actions are pill-shaped buttons that shortcut to common tasks.
Specs:
- Background: --color-surface with --color-primary-light left border (4px)
- Border-radius: --radius-md (8px)
- Padding: --space-3 (12px) vertical, --space-4 (16px) horizontal
- Icon: 20px, --color-primary
- Text: --font-body-medium
- Touch target: min 48px tall
- Hover: background shifts to --color-primary-light
Sprint 0 quick actions:
| # | Icon | Label | Target route |
|---|---|---|---|
| 1 | palm-tree |
Nouvelle demande de congé | /conges/nouvelle |
| 2 | check-circle |
Approuver les demandes (3) | /conges/approbations |
| 3 | map-pin |
Pointer mon arrivée | /pointage |
Responsive layout:
- 360px: 1 column, full-width buttons stacked
- 768px+: horizontal row, wrapping if needed
- Section title: "Actions rapides" (--font-h3)
6.5 Recent Activity Feed¶
A chronological list of recent events across all modules.
Item structure:
┌──────────────────────────────────────────────────┐
│ [avatar] Moussa Koné a soumis une demande │
│ de congé (15/03 – 22/03) │
│ il y a 2 heures │
└──────────────────────────────────────────────────┘
Specs:
- Avatar: 32px circle (user initials or icon for system events)
- Title: --font-body, --color-text-primary
- Timestamp: --font-caption, --color-text-tertiary
- Divider: 1px --color-border-light between items
- Max items visible: 5 (with "Voir toute l'activité" link at bottom)
Sprint 0: Show 3 static placeholder items. Design the empty state:
6.6 Role-Aware Variants (Future Implementation)¶
Designed now, implemented when role system is connected.
P4 — Employee Dashboard¶
KPI Cards:
- Solde de congés: "12 jours restants" (with mini progress bar)
- Prochain congé: "15/03 – 22/03" or "Aucun congé planifié"
- Dernier bulletin: "Février 2026 — 450 000 XOF"
Quick Actions:
- Demander un congé
- Pointer mon arrivée
- Voir mes bulletins de paie
Activity: Own actions only
P2 — RH Manager Dashboard¶
KPI Cards:
- Effectif: "48 employés actifs" (+2 ce mois)
- Demandes en attente: "5" (clickable, goes to approvals)
- Absences aujourd'hui: "3 employés absents"
- Masse salariale: "22 500 000 XOF" (current month)
Quick Actions:
- Approuver les demandes (5)
- Ajouter un employé
- Voir le planning des absences
Activity: Team actions
P3 — Accountant Dashboard¶
KPI Cards:
- Budget consommé: "68%" (gauge visualization)
- Engagements en attente: "3" (pending approval)
- Paie en cours: "Février 2026 — en validation"
- Écart budgétaire: "+5%" (warning if over threshold)
Quick Actions:
- Valider la paie
- Nouveau engagement
- Exporter le journal budgétaire
Activity: Finance-related actions
P1 — DG Dashboard¶
KPI Cards:
- Effectif total: "48"
- Masse salariale: "22 500 000 XOF"
- Budget restant: "32%" (with gauge)
- Demandes à approuver: "2"
Quick Actions:
- Approuver (2)
- Voir le tableau de bord financier
- Exporter le rapport mensuel
Activity: All company actions
6.7 Dashboard Offline Behavior¶
- All KPI data is cached in IndexedDB on every successful fetch
- When offline, dashboard shows cached data
- Timestamp below KPI section: "Dernière mise à jour il y a X minutes"
- If cached data is >1 hour old, add subtle warning banner: "Les données affichées datent de plus d'une heure. Connectez-vous pour actualiser."
- Banner style:
--color-warning-lightbackground,--color-warningleft border,--font-smalltext - Quick actions that require connectivity (e.g., submit new leave) still navigate to the form — offline queueing handles the rest
7. Navigation¶
7.1 Module Registry¶
Complete list of all modules with their navigation metadata:
| # | Key | Icon | Label (FR) | Group | Status | Route |
|---|---|---|---|---|---|---|
| 1 | dashboard |
layout-dashboard |
Tableau de bord | Quotidien | active | / |
| 2 | absence |
calendar |
Congés & Absences | Quotidien | active | /conges |
| 3 | attendance |
qr-code |
Pointage QR | Quotidien | active | /pointage |
| 4 | budget |
wallet |
Lignes budgétaires | Finance | active | /budget |
| 5 | commitment |
file-signature |
Engagements | Finance | active | /engagements |
| 6 | payroll |
banknote |
Paie | Finance | active | /paie |
| 7 | expense |
receipt |
Notes de frais | Finance | locked | — |
| 8 | employee |
users |
Employés | Ressources Humaines | active | /employes |
| 9 | performance |
target |
Performance & Objectifs | Ressources Humaines | locked | — |
| 10 | skills |
brain |
Compétences & GPEC | Ressources Humaines | locked | — |
| 11 | training |
book-open |
Formation | Ressources Humaines | locked | — |
| 12 | recruitment |
search |
Recrutement | Ressources Humaines | locked | — |
| 13 | crm |
briefcase |
CRM Interne | Commercial | active | /crm |
| 14 | time |
clock |
Temps & Activités | — | locked | — |
| 15 | hse |
shield-check |
HSE | — | locked | — |
Icon library recommendation: Lucide Icons (open-source, consistent, supports all icons listed above, tree-shakeable). 24px size for sidebar, 20px for bottom nav labels.
7.2 Group Headers (Sidebar)¶
Group headers are NOT clickable, NOT collapsible. They are visual separators only.
Style:
- Text: --font-overline (11px, weight 600, letter-spacing 0.08em, uppercase)
- Color: --color-sidebar-group-label
- Margin: --space-6 (24px) top, --space-2 (8px) bottom
- No background, no border — just text with spacing
7.3 Active Module Indicator¶
The currently active module is highlighted in the sidebar:
Expanded sidebar:
- Left border: 3px solid --color-primary (#F59E0B)
- Background: --color-sidebar-bg-active
- Icon color: --color-sidebar-icon-active (amber)
- Text color: --color-sidebar-text-active (white)
- Text weight: 600 (medium → semibold shift)
Collapsed sidebar: - Small amber dot (6px) below the icon - Icon color: amber
Bottom navigation (mobile):
- Icon: filled variant (not outline)
- Label color: --color-primary
- Top border: 2px solid --color-primary on the active tab
7.4 Locked Module ("Bientôt") State¶
Sidebar appearance:
- Icon: same icon as active module, but at 50% opacity
- Label: 50% opacity
- Badge: small pill "Bientôt" to the right of the label
- Background: rgba(245,158,11,0.15) (amber tint)
- Text: --color-primary, --font-caption, weight 600
- Border-radius: --radius-full
- Padding: 2px 8px
- Cursor: pointer (it IS clickable — opens preview)
On click — Desktop (popover):
┌──────────────────────────────────────┐
│ │
│ 📚 Formation │
│ │
│ Gérez les plans de formation, │
│ suivez les certifications et │
│ développez les compétences de │
│ votre équipe. │
│ │
│ ┌──────────────────────────────┐ │
│ │ ✦ Bientôt disponible │ │ ← Amber badge, centered
│ └──────────────────────────────┘ │
│ │
│ ☐ Me notifier à la sortie │ ← Toggle/checkbox
│ │
└──────────────────────────────────────┘
Popover specs:
- Width: 300px
- Background: --color-surface
- Shadow: --shadow-lg
- Border-radius: --radius-lg
- Positioned: to the right of the sidebar item, vertically centered
- Dismiss: click outside, Escape key, or click X
- Animation: fade in + scale from 0.95 to 1.0, 200ms
On click — Mobile (bottom sheet): Same content as popover, displayed in a bottom sheet. Sheet height: auto (fits content). Drag handle at top. Dismiss: swipe down or tap backdrop.
7.5 Admin Section¶
Visible only when user has TENANT_ADMIN or PLATFORM_ADMIN role.
Sidebar placement: Below all module groups, separated by a thicker divider (2px --color-sidebar-divider).
Items:
| Icon | Label | Route |
|---|---|---|
settings |
Paramètres entreprise | /admin/parametres |
users |
Utilisateurs & Rôles | /admin/utilisateurs |
scroll-text |
Journal d'audit | /admin/audit |
credit-card |
Abonnement | /admin/abonnement |
Style: Same as regular nav items. Group header: "ADMINISTRATION" in --font-overline.
Non-admin users: These items are completely absent from the DOM (not hidden, not greyed — absent). Security by exclusion.
7.6 Keyboard Navigation (Desktop)¶
For P3 (Comptable) and power users:
| Shortcut | Action |
|---|---|
Alt + 1 through Alt + 9 |
Navigate to module 1–9 in the active list |
Alt + D |
Go to Dashboard |
Alt + S |
Focus search (future — not in Sprint 0) |
/ |
Focus search when not in an input (future) |
Ctrl + K |
Command palette (future — designed here, built later) |
Sprint 0 implements Alt + D (dashboard) and Alt + 1..9 (module shortcuts). The shortcuts are listed in the user menu under "Raccourcis clavier."
8. Sync Status Indicator¶
8.1 States¶
| State | Icon | Label (desktop) | Color | Animation |
|---|---|---|---|---|
| Synced | Filled circle (8px) | Synchronisé | --color-sync-synced (green) |
None — static |
| Pending | Filled circle (8px) + count badge | 3 en attente | --color-sync-pending (amber) |
None — static |
| Syncing | Filled circle (8px) | Synchronisation… | --color-sync-syncing (blue) |
Gentle pulse (opacity 0.4→1.0, 1500ms ease-in-out, infinite) |
| Offline | Filled circle (8px) | Hors ligne | --color-sync-offline (warm grey) |
None — static |
8.2 Placement & Size¶
Mobile (top bar): Circle dot only (8px). No label. Count badge (if pending) overlaps the dot as a mini number (like notification badges). Total touch target area: 40px × 40px (allows tapping to expand).
Desktop (top bar): Dot (8px) + label text (--font-small). Total clickable area: fits content + 8px padding.
8.3 Expanded Sync Panel¶
Triggered by tapping/clicking the sync indicator. Appears as a dropdown panel below the indicator (desktop) or bottom sheet (mobile).
Desktop Dropdown¶
┌──────────────────────────────────────────┐
│ ● Synchronisation │
│ │
│ Dernière synchronisation : │
│ il y a 12 minutes │
│ │
│ ── 3 opérations en attente ── │
│ │
│ 📅 Demande de congé │
│ Moussa Koné — 15/03 au 22/03 │
│ En attente de synchronisation │
│ │
│ 📍 Pointage │
│ Arrivée — 08:15 │
│ En attente de synchronisation │
│ │
│ 📝 Engagement n°2024-031 │
│ Modification du montant │
│ En attente de synchronisation │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Synchroniser maintenant │ │ ← Secondary button
│ └──────────────────────────────────┘ │
│ │
└──────────────────────────────────────────┘
Specs:
- Width: 360px
- Max-height: 400px (scrollable if more items)
- Background: --color-surface
- Shadow: --shadow-lg
- Border-radius: --radius-lg
- Positioned: below the sync indicator, right-aligned with it
Queue item:
- Icon: 20px, module-specific icon
- Title: --font-body-medium, --color-text-primary
- Subtitle: --font-small, --color-text-secondary
- Status: --font-caption, --color-text-tertiary
- Divider: 1px --color-border-light between items
- Height: ~72px per item
"Synchroniser maintenant" button: - Secondary button style (outlined) - Disabled when already syncing or when offline - When offline, replaced by text: "Connexion requise pour synchroniser"
States in the Panel¶
When synced (no pending):
┌──────────────────────────────────────────┐
│ ● Tout est à jour │
│ │
│ Dernière synchronisation : │
│ il y a 2 minutes │
│ │
│ ✓ Toutes les données sont │
│ synchronisées avec le serveur. │
└──────────────────────────────────────────┘
When offline:
┌──────────────────────────────────────────┐
│ ● Hors ligne │
│ │
│ Dernière synchronisation : │
│ il y a 45 minutes │
│ │
│ Vos modifications sont sauvegardées │
│ localement. La synchronisation │
│ reprendra automatiquement dès que │
│ la connexion sera rétablie. │
│ │
│ ── 2 opérations en attente ── │
│ [operation items...] │
└──────────────────────────────────────────┘
8.4 Sync Indicator UX Principles¶
- CALM, not alarming: "Hors ligne" is stated as a fact, not a warning. No red, no exclamation marks. Warm grey color conveys "we're handling this."
- Reassuring: "Vos modifications sont sauvegardées localement" — the user knows nothing is lost.
- Automatic: "La synchronisation reprendra automatiquement" — no user action required. The manual sync button is a power-user option, not a necessity.
- Transparent: Every queued operation is listed. No black box.
9. Responsive Behavior¶
9.1 Breakpoint Matrix¶
| Component | 360px (Phone) | 768px (Tablet) | 1024px (Desktop) | 1440px (Large) |
|---|---|---|---|---|
| Navigation | Bottom nav (4 tabs) | Collapsed sidebar (68px) | Expanded sidebar (260px) | Expanded sidebar (260px) |
| Top bar | 56px, compact | 64px, page title | 64px, breadcrumb + all controls | 64px, same as 1024 |
| Content padding | 16px | 24px | 32px | 32px |
| Content max-width | 100% | 100% | 100% | 1200px (centered) |
| KPI grid | 1 column | 2 columns | 4 columns | 4 columns |
| Quick actions | 1 column stack | 4 column row | 3–4 column row | 3–4 column row |
| Activity feed | Full width | Full width | Full width | 2/3 width (with sidebar widget) |
| Notification panel | Bottom sheet (full width) | Right slide-in (360px) | Right slide-in (360px) | Right slide-in (360px) |
| Sync panel | Bottom sheet | Dropdown | Dropdown | Dropdown |
| Login layout | Centered card | Centered card | Split (50/50) | Split (50/50) |
| Modals | Full screen | Centered (max 600px) | Centered (max 600px) | Centered (max 600px) |
| Tables | Card list view | Scrollable table | Full table | Full table |
| Forms | 1 column | 2 columns | 2 columns | 2 columns |
9.2 Transition Behavior¶
When the viewport crosses a breakpoint boundary (e.g., rotating a tablet from portrait to landscape):
- Layout changes are CSS-driven (media queries), not JS-driven
- Sidebar expansion/collapse: 200ms ease-in-out transition on width
- Bottom nav to sidebar: no animation (distinct layout modes, not morphing)
- Content re-flow: instant (grid columns snap, no animation on grid changes)
- Modals: if a full-screen modal is open on phone and user rotates to tablet width, it smoothly transitions to centered card (300ms)
9.3 Touch Considerations¶
- All interactive elements: minimum 48×48px touch target (even if visually smaller, pad with transparent hit area)
- Sidebar nav items: 44px tall + 4px gap = 48px effective touch target
- Bottom nav items: 64px tall × 90px wide (exceeds minimum)
- Swipe gestures:
- Swipe right from left edge (tablet): open sidebar overlay
- Swipe down on bottom sheet: dismiss
- Swipe down on content area: pull-to-refresh (triggers sync)
- NO swipe-to-navigate-back (conflicts with OS gesture)
- Long press: not used in Sprint 0 (reserved for future context menus on list items)
9.4 Orientation Handling¶
- Phone landscape: top bar stays, bottom nav stays. Content area gets wider but shorter. Not optimized for landscape — most phone usage is portrait. No special layout.
- Tablet portrait (768px): collapsed sidebar mode
- Tablet landscape (~1024px): expanded sidebar mode (matches desktop breakpoint)
10. Dark Mode¶
10.1 Toggle Behavior¶
- Location: top bar (desktop — moon/sun icon), user menu (mobile — toggle row)
- Default: follows OS
prefers-color-schemesetting - Manual override: persisted in
localStorageunder keypapillon-theme("light"|"dark"|"system") - Toggle: 3-state cycle: System → Light → Dark → System
- Icon: ☀️ (sun) when in dark mode (clicking switches to light), 🌙 (moon) when in light mode
- Transition: 200ms CSS transition on
background-color,color,border-color. No flash of unstyled content.
10.2 Dark Mode Token Overrides¶
All tokens below are set on [data-theme="dark"] or @media (prefers-color-scheme: dark).
Background Colors¶
| Token | Light Value | Dark Value | Notes |
|---|---|---|---|
--color-bg-primary |
#FFFDF7 |
#1A1614 |
Warm dark brown (not cold grey) |
--color-bg-secondary |
#FFF9EE |
#231F1B |
Card surfaces |
--color-bg-tertiary |
#F5F0E8 |
#2D2824 |
Input backgrounds |
--color-surface |
#FFFFFF |
#2D2824 |
Floating surfaces |
--color-border |
#E8E0D4 |
#3D3530 |
Borders |
--color-border-light |
#F0EBE3 |
#332D28 |
Subtle dividers |
Text Colors¶
| Token | Light Value | Dark Value |
|---|---|---|
--color-text-primary |
#1C1917 |
#F5F0E8 |
--color-text-secondary |
#57534E |
#A8A29E |
--color-text-tertiary |
#A8A29E |
#78716C |
--color-text-inverse |
#FFFDF7 |
#1C1917 |
Brand Colors (Adjusted for Dark)¶
| Token | Light Value | Dark Value | Notes |
|---|---|---|---|
--color-primary |
#F59E0B |
#FBBF24 |
Slightly brighter amber for contrast |
--color-primary-hover |
#D97706 |
#F59E0B |
|
--color-primary-light |
#FEF3C7 |
#3D2E0A |
Dark amber tint |
--color-primary-subtle |
#FFFBEB |
#2D2410 |
Very dark amber wash |
--color-secondary |
#0D6B6E |
#14B8A6 |
Brighter teal for dark backgrounds |
--color-secondary-hover |
#0A5456 |
#0D9488 |
Sidebar Colors (Dark)¶
| Token | Light Value | Dark Value |
|---|---|---|
--color-sidebar-bg |
#0D6B6E |
#0A3D3F |
--color-sidebar-bg-hover |
#0A5456 |
#0D4F4F |
--color-sidebar-bg-active |
#083F41 |
#0D6B6E |
--color-sidebar-text |
#CCFBF1 |
#99F6E4 |
Semantic Colors (Dark)¶
| Token | Light Value | Dark Value |
|---|---|---|
--color-success |
#16A34A |
#4ADE80 |
--color-success-light |
#DCFCE7 |
#14532D |
--color-warning |
#F59E0B |
#FBBF24 |
--color-warning-light |
#FEF9C3 |
#3D2E0A |
--color-error |
#DC2626 |
#F87171 |
--color-error-light |
#FEE2E2 |
#450A0A |
--color-info |
#2563EB |
#60A5FA |
--color-info-light |
#DBEAFE |
#1E3A5F |
Shadow Overrides (Dark)¶
In dark mode, shadows are less visible. Increase opacity and use darker values:
| Token | Light Value | Dark Value |
|---|---|---|
--shadow-sm |
0 1px 2px rgba(28,25,23,0.05) |
0 1px 2px rgba(0,0,0,0.2) |
--shadow-md |
0 4px 12px rgba(28,25,23,0.08) |
0 4px 12px rgba(0,0,0,0.3) |
--shadow-lg |
0 8px 24px rgba(28,25,23,0.12) |
0 8px 24px rgba(0,0,0,0.4) |
--shadow-xl |
0 16px 48px rgba(28,25,23,0.16) |
0 16px 48px rgba(0,0,0,0.5) |
10.3 Dark Mode Design Principles¶
- Warm, not cold: Background is warm dark brown (#1A1614), not cool grey (#1a1a1a). This maintains the Papillon warmth.
- Reduce, don't invert: Colors are adjusted to work on dark backgrounds, not mathematically inverted.
- Elevate with lightness: In dark mode, higher-elevation surfaces are slightly lighter (cards lighter than page bg). This replaces the shadow-based hierarchy of light mode.
- Primary stays amber: The amber/gold never shifts to a different hue. It only adjusts brightness for contrast.
- OLED-friendly: The page background (#1A1614) is dark enough to save battery on OLED screens while being warm enough to not feel like a void.
11. Notification Panel¶
11.1 Trigger¶
The notification bell icon in the top bar. When unread notifications exist, a count badge is displayed.
Bell icon specs:
- Icon: bell (Lucide), 24px
- Color: --color-text-secondary (default), --color-text-primary (when has unread)
- Badge: red circle with white count text
- Position: top-right of bell icon, offset -4px, -4px
- Size: 18px circle (for 1 digit), pill for 2+ digits
- Background: --color-error
- Text: white, --font-caption (12px), weight 700
- Max display: "9+" for 10+ notifications
- Animation on new notification: badge scales from 0 to 1 (150ms spring)
11.2 Panel Layout — Desktop (Slide-in)¶
Triggered by clicking the bell. Slides in from the right edge.
┌──────────────────────────────────────┐
│ Notifications [Tout lu] ✕ │ ← Header
├──────────────────────────────────────┤
│ │
│ AUJOURD'HUI │ ← Date group header
│ │
│ ┌──────────────────────────────┐ │
│ │ ● 📋 Demande de congé │ │ ← Unread (● blue dot)
│ │ Moussa Koné a demandé un │ │
│ │ congé du 15/03 au 22/03 │ │
│ │ Il y a 2 heures │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ ✅ Congé approuvé │ │ ← Read (no dot)
│ │ Votre demande de congé │ │
│ │ a été approuvée │ │
│ │ Il y a 5 heures │ │
│ └──────────────────────────────┘ │
│ │
│ HIER │
│ │
│ ┌──────────────────────────────┐ │
│ │ ⚙️ Système │ │
│ │ La paie de février est │ │
│ │ prête à être validée │ │
│ │ Hier à 14:30 │ │
│ └──────────────────────────────┘ │
│ │
│ ── Voir toutes les notifications ── │
│ │
└──────────────────────────────────────┘
Panel specs:
- Width: 380px
- Max-height: 100vh - topbar height
- Position: fixed, right: 0, top: topbar height
- Background: --color-surface
- Shadow: --shadow-xl (left-side shadow since it's right-aligned)
- Animation: slide in from right, 300ms --easing-enter
- Backdrop: semi-transparent overlay on content (mobile only)
- Close: ✕ button, Escape key, click outside
Header:
- "Notifications" — --font-h3 (18px, semibold)
- "Tout marquer comme lu" — text button, --color-primary, --font-small
- ✕ close button: 24px icon, --color-text-secondary
11.3 Notification Item¶
┌──────────────────────────────────────┐
│ [●] [icon] Title text │
│ Description text, up to │
│ 2 lines max │
│ Timestamp │
└──────────────────────────────────────┘
Specs:
- Padding: --space-4 (16px)
- Unread indicator: 8px blue dot (--color-info), left-aligned
- Icon: 20px, colored by notification type
- Title: --font-body-medium, --color-text-primary
- Description: --font-small, --color-text-secondary, max 2 lines (line-clamp)
- Timestamp: --font-caption, --color-text-tertiary
- Hover (desktop): --color-bg-tertiary background
- Tap: navigates to the related item + marks as read
- Unread background: --color-primary-subtle (very light amber wash)
- Read background: transparent
Notification types and their icons/colors:
| Type | Icon | Icon Color | Example |
|---|---|---|---|
| Approval request | clock |
--color-warning |
"Moussa Koné a demandé un congé" |
| Status change (positive) | check-circle |
--color-success |
"Votre congé a été approuvé" |
| Status change (negative) | x-circle |
--color-error |
"Votre congé a été refusé" |
| System alert | info |
--color-info |
"La paie est prête à valider" |
| Mention | at-sign |
--color-secondary |
"Vous avez été mentionné dans…" |
11.4 Empty State (Sprint 0 Default)¶
┌──────────────────────────────────────┐
│ Notifications ✕ │
├──────────────────────────────────────┤
│ │
│ │
│ 🔔 │
│ │
│ Aucune notification │
│ pour le moment │
│ │
│ Vous serez notifié lorsque │
│ quelque chose nécessite │
│ votre attention. │
│ │
│ │
└──────────────────────────────────────┘
Style:
- Icon: 48px, --color-text-tertiary, 50% opacity
- Title: --font-h3, --color-text-primary
- Subtitle: --font-body, --color-text-secondary, centered, max 240px width
11.5 Mobile Layout (Bottom Sheet)¶
On phone (<768px), the notification panel opens as a bottom sheet instead of a side panel.
- Height: 70vh (expandable to 90vh by dragging up)
- Drag handle: 32px × 4px rounded bar, centered,
--color-text-tertiary - Full-width
- Same content layout as desktop panel
- Dismiss: swipe down or tap backdrop
11.6 Date Grouping Logic¶
| Condition | Group Label |
|---|---|
| Today | "Aujourd'hui" |
| Yesterday | "Hier" |
| Within 7 days | "Cette semaine" |
| Older | "Plus ancien" |
Group label style: --font-overline, uppercase, --color-text-tertiary, --space-4 top margin.
12. PWA Spec¶
12.1 Install Banner¶
A non-intrusive banner encouraging PWA installation. Shown ONCE per device/session when the browser's beforeinstallprompt event fires.
┌──────────────────────────────────────────────────────────────┐
│ 🦋 Installer Papillon sur votre [Installer] [✕] │
│ téléphone pour un accès rapide │
└──────────────────────────────────────────────────────────────┘
Specs:
- Position: fixed, bottom: --bottomnav-height + 8px (above bottom nav on phone), bottom: 16px on desktop
- Width: 100% - 32px (mobile), max 480px (desktop), centered
- Background: --color-surface
- Shadow: --shadow-xl
- Border-radius: --radius-lg
- Padding: --space-3 (12px) vertical, --space-4 (16px) horizontal
- Layout: flex row — icon (butterfly) | text | Install button | dismiss X
- "Installer" button: Primary style, small size
- "✕" dismiss: icon button, --color-text-tertiary
- Animation: slides up from below, 300ms
Behavior:
- Only shows when beforeinstallprompt fires (browser supports PWA install)
- On "Installer": trigger native install prompt
- On "✕": dismiss banner, store papillon-pwa-dismissed: true in localStorage
- Never show again after dismissal (respect the user)
- Never show if PWA is already installed (check display-mode: standalone)
12.2 Splash Screen¶
Shown briefly when launching the PWA from the home screen.
Design:
┌──────────────────────────────────────┐
│ │
│ │
│ │
│ 🦋 │ ← Butterfly logo, 64px
│ │
│ Papillon │ ← Brand name, --font-display
│ │
│ Votre assistant RH de confiance │ ← Tagline, --font-small
│ │
│ │
│ │
│ │
│ │
│ © 2026 ALTARYS ENTERPRISE │ ← Footer, --font-caption
│ │
└──────────────────────────────────────┘
Implementation: Configured in manifest.json:
- background_color: #FFFDF7 (light mode) — matches --color-bg-primary
- theme_color: #0D6B6E (matches sidebar teal)
- icons: 192px and 512px app icons (butterfly logo on amber/gold circle background)
- The actual splash is browser-generated from manifest fields. The design above guides icon/color choices.
Dark mode splash: If user's OS is in dark mode, background_color should ideally be #1A1614. Since manifest doesn't support dark mode, the JS splash (displayed between manifest splash and app load) handles this:
- Brief JS-rendered splash (500ms max) that checks theme preference and renders accordingly
- Crossfades into the app
12.3 Offline Landing Page¶
Shown when user opens the app (or navigates to it) while offline AND has no cached session (not logged in, or JWT expired).
┌──────────────────────────────────────┐
│ │
│ │
│ │
│ [Geometric SVG illustration: │
│ warm amber/teal circles and │
│ arcs, suggesting connection │
│ with a gentle "disconnected" │
│ visual — e.g., two circles │
│ almost touching but not quite] │
│ │
│ Vous êtes hors ligne │ ← --font-h1
│ │
│ Connectez-vous à Internet pour │ ← --font-body
│ accéder à Papillon. │
│ │
│ Dès que la connexion sera │ ← --font-small, secondary text
│ rétablie, cette page se │
│ rechargera automatiquement. │
│ │
│ │
│ 🦋 │
│ Papillon │
│ │
└──────────────────────────────────────┘
Specs:
- Full screen, centered content
- Background: --color-bg-primary
- Illustration: simple inline SVG (no external file — must be available offline)
- The page registers a navigator.onLine listener and auto-reloads when connectivity returns
- No "retry" button needed (auto-reload is sufficient), but include one for user agency:
"Réessayer" subtle text button below the main text
Important: This page is cached by the Service Worker on first load. It MUST be available offline. All assets (SVG, fonts, CSS) must be in the SW cache.
12.4 Manifest Configuration¶
{
"name": "Papillon HR Suite",
"short_name": "Papillon",
"description": "Votre assistant RH de confiance — Gestion des ressources humaines pour les PME",
"start_url": "/",
"display": "standalone",
"orientation": "any",
"background_color": "#FFFDF7",
"theme_color": "#0D6B6E",
"lang": "fr",
"categories": ["business", "productivity"],
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
{ "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
App icon design: Butterfly silhouette (🦋) in white on an amber/gold (#F59E0B) circle background. Maskable versions include extra padding per Android adaptive icon guidelines. The icon should be simple enough to be recognizable at 48px on a home screen.
13. Shared Component Specs¶
These components live in packages/shared-components/ and are reused by both frontends. All components accept theme tokens via CSS custom properties — no hardcoded colors.
13.1 Button¶
The most-used interactive element. Five variants, three sizes.
Variants¶
| Variant | Background | Text | Border | Usage |
|---|---|---|---|---|
| primary | --color-primary |
--color-text-on-primary |
none | Primary actions: "Se connecter", "Enregistrer", "Soumettre" |
| secondary | transparent | --color-secondary |
1px --color-secondary |
Secondary actions: "Annuler", "Synchroniser" |
| ghost | transparent | --color-text-secondary |
none | Tertiary actions: "Voir tout", "Plus tard" |
| danger | --color-error |
white | none | Destructive: "Supprimer" (with undo, never confirm dialogs) |
| success | --color-success |
white | none | Positive confirmation: "Approuver" |
Sizes¶
| Size | Height | Padding H | Font | Icon size | Usage |
|---|---|---|---|---|---|
| sm | 32px | 12px | --font-small (14px) |
16px | Inline actions, table rows |
| md | 44px | 16px | --font-body (16px) |
20px | Default — most buttons |
| lg | 52px | 20px | --font-body (16px, 600) |
20px | Full-width mobile CTAs |
States¶
| State | Visual Change |
|---|---|
| default | As specified per variant |
| hover | Background darkens 10% (use --color-primary-hover etc.) |
| active/pressed | Background darkens 15%, scale(0.98) for 100ms |
| focused | --shadow-focus ring (3px amber glow) |
| loading | Text replaced by spinner (16px). Button width locked (no layout shift). Disabled. |
| disabled | 50% opacity. cursor: not-allowed. No hover effects. |
Layout Options¶
- Icon + text: icon left of text,
--space-2(8px) gap - Text only: centered text
- Icon only: square button (same height as width based on size)
- Full width:
width: 100%— used for mobile CTAs
Accessibility¶
role="button"(implicit for<button>)aria-disabled="true"when disabled (not justdisabledattr for screen readers)aria-busy="true"when loading- Focus indicator: visible on keyboard focus (
:focus-visible), hidden on mouse click - Minimum touch target: 48×48px (even for
sm— pad with invisible hit area)
13.2 Input¶
Text input fields with labels, helper text, and validation states.
Structure¶
Label text (optional badge)
┌──────────────────────────────────────┐
│ [icon] Placeholder text [⟐] │
└──────────────────────────────────────┘
Helper text or error message
Specs¶
| Property | Value |
|---|---|
| Height | 48px (touch-friendly) |
| Padding | 12px horizontal, centered vertical |
| Background | --color-bg-tertiary |
| Border | 1px --color-border |
| Border-radius | --radius-md (8px) |
| Font | --font-body (16px) — prevents iOS zoom on focus |
| Label | --font-small (14px), --font-body-medium weight, --color-text-primary |
| Label gap | --space-1 (4px) below label |
| Placeholder | --color-text-tertiary |
| Helper text | --font-caption (12px), --color-text-secondary |
Types¶
| Type | Specifics |
|---|---|
| text | Standard single-line text |
inputmode="email", email keyboard on mobile |
|
| password | Visibility toggle button (eye icon) inside input, right-aligned |
| number | inputmode="numeric", no browser spinners (CSS: appearance: none) |
| date | Native date picker on mobile, custom picker on desktop. Format display: DD/MM/YYYY |
| tel | inputmode="tel", phone keyboard. Country code prefix select (see below) |
| select | Custom styled dropdown (not native <select>). Searchable for lists >7 items |
| textarea | Multi-line. Min-height: 96px. Auto-resize up to 240px, then scroll |
States¶
| State | Border | Background | Label | Icon |
|---|---|---|---|---|
| default | --color-border |
--color-bg-tertiary |
--color-text-primary |
--color-text-tertiary |
| hover | --color-border darkened |
same | same | same |
| focused | --color-border-focus (amber) + --shadow-focus |
--color-surface |
--color-primary |
--color-primary |
| filled | --color-border |
--color-bg-tertiary |
--color-text-primary |
--color-text-secondary |
| error | --color-error |
--color-error-light |
--color-error |
--color-error |
| disabled | --color-border-light |
--color-bg-tertiary 50% opacity |
--color-text-tertiary |
--color-text-tertiary |
| read-only | none | transparent | --color-text-secondary |
hidden |
Validation¶
- Validate on blur (not on every keystroke — avoids premature errors)
- Error message appears with a subtle slide-down animation (150ms)
- Error message:
--font-caption,--color-error, 4px below input - Success indicator: green checkmark icon inside input (right side) when valid after previous error
- Required fields: asterisk (*) after label text,
--color-error
Phone Input Variant¶
┌─────┬──────────────────────────────┐
│ +225│ 07 00 00 00 00 │
│ ▾ │ │
└─────┴──────────────────────────────┘
- Country code selector: 72px wide, left section
- Default: +225 (CI), also shows 🇨🇮 flag
- Options: +225 (CI), +229 (BJ), +237 (CM)
- Format mask auto-applied based on country
13.3 Card¶
Container component for grouping related content.
Variants¶
| Variant | Usage | Specs |
|---|---|---|
| default | KPI cards, content sections | --color-bg-secondary bg, --color-border border, --shadow-sm |
| elevated | Floating panels, popovers | --color-surface bg, --shadow-md |
| interactive | Clickable cards (module previews) | Same as default + hover: --shadow-md, translateY(-2px) |
| outlined | Subtle grouping without elevation | transparent bg, --color-border 1px border |
Structure¶
┌──────────────────────────────────────┐
│ [Header area — optional] │ ← Card.Header
│ Title [Action button] │
├──────────────────────────────────────┤
│ │
│ [Content area] │ ← Card.Content
│ │
├──────────────────────────────────────┤
│ [Footer area — optional] │ ← Card.Footer
└──────────────────────────────────────┘
Specs:
- Border-radius: --radius-lg (12px)
- Padding: --space-5 (20px) mobile, --space-6 (24px) desktop
- Header/footer separation: 1px --color-border-light divider
- Full-bleed content option: negative margins to touch card edges (for tables/images inside cards)
13.4 Modal / Dialog¶
Overlay dialog for focused interactions. Full-screen on mobile, centered card on tablet+.
Layout¶
Mobile (<768px): Full screen
┌──────────────────────────────────────┐
│ ← Titre du modal [✕] │ ← Sticky header, 56px
├──────────────────────────────────────┤
│ │
│ [Scrollable content area] │
│ │
│ │
├──────────────────────────────────────┤
│ [Annuler] [Action primaire]│ ← Sticky footer
└──────────────────────────────────────┘
Tablet+ (≥768px): Centered card
┌──────────────────────────┐
│ Titre du modal [✕] │ ← Header
├──────────────────────────┤
│ │
│ [Content area] │ ← Max-height: 70vh, scrollable
│ │
├──────────────────────────┤
│ [Annuler] [Action] │ ← Footer, right-aligned buttons
└──────────────────────────┘
Backdrop: rgba(0,0,0,0.5)
Specs:
- Desktop width: 480px (small), 600px (medium), 800px (large)
- Background: --color-surface
- Border-radius: --radius-xl (16px) — desktop only (mobile is full-screen)
- Shadow: --shadow-xl
- Backdrop: rgba(0,0,0,0.5) with blur (4px)
- Animation open: backdrop fades in (200ms), modal scales from 0.95 + fades in (200ms)
- Animation close: reverse (150ms — faster close feels snappy)
- Close triggers: ✕ button, Escape key, backdrop click (unless persistent prop)
- Focus trap: Tab cycles within modal. First focusable element auto-focused on open.
- aria-modal="true", role="dialog", aria-labelledby pointing to title
13.5 Toast / Snackbar¶
Brief feedback messages that appear at screen edge and auto-dismiss.
Variants¶
| Variant | Icon | Background | Border-left |
|---|---|---|---|
| success | check-circle |
--color-success-light |
4px --color-success |
| error | alert-circle |
--color-error-light |
4px --color-error |
| warning | alert-triangle |
--color-warning-light |
4px --color-warning |
| info | info |
--color-info-light |
4px --color-info |
Layout¶
┌──────────────────────────────────────┐
│ [icon] Message text [✕] │
│ Optional action link │
└──────────────────────────────────────┘
Specs:
- Position: fixed, bottom-center (mobile), top-right (desktop)
- Mobile: full width - 32px margin, 16px from bottom nav
- Desktop: 400px max-width, 16px from top-right corner
- Border-radius: --radius-md (8px)
- Shadow: --shadow-lg
- Padding: --space-3 (12px) vertical, --space-4 (16px) horizontal
- Icon: 20px
- Text: --font-body, --color-text-primary
- Action link: --font-body-medium, --color-primary, underline on hover
- Auto-dismiss: 5 seconds (success/info), 8 seconds (warning), persistent until dismissed (error)
- Animation: slide in from bottom (mobile) or right (desktop), 200ms
- Stack: multiple toasts stack vertically with 8px gap, max 3 visible
- Z-index: --z-toast (600)
Accessibility:
- role="alert" for error/warning, role="status" for success/info
- aria-live="polite" (success/info) or aria-live="assertive" (error)
13.6 Loading Skeleton¶
Placeholder shapes shown while content loads. ALWAYS use skeletons, never spinners (skeletons feel faster and show expected layout).
Variants¶
| Variant | Shape | Usage |
|---|---|---|
| text | Rounded rectangle, height 16px | Body text placeholder |
| title | Rounded rectangle, height 24px, width 60% | Heading placeholder |
| avatar | Circle, 40px diameter | User avatar placeholder |
| card | Rounded rectangle matching card size | KPI card, content card |
| button | Rounded rectangle matching button size | Action button placeholder |
| table-row | Full-width rectangle, height 48px | Table row placeholder |
Animation: Shimmer effect — gradient sweep from left to right, 1.5s ease-in-out infinite.
- Light mode shimmer: --color-bg-tertiary → --color-bg-secondary → --color-bg-tertiary
- Dark mode shimmer: --color-bg-tertiary → slightly lighter → --color-bg-tertiary
Specs:
- Border-radius: --radius-sm (6px) for text/table, --radius-lg for cards, --radius-full for avatars
- Background: --color-bg-tertiary
- No border
- Opacity: 0.7 (subtle, not aggressive)
Accessibility: aria-hidden="true" on skeletons, aria-busy="true" on the container being loaded.
Usage rule: The skeleton must match the shape of the content it's replacing. Never use a generic "loading bar" — use shaped skeletons that preview the layout.
13.7 Empty State¶
Displayed when a list, table, or section has no data.
Structure¶
[SVG illustration] ← 120px, themed geometric
shapes (amber + teal)
Title text ← --font-h2, --color-text-primary
Description text that ← --font-body, --color-text-secondary
explains what this area max-width: 320px, centered
is for and what to do.
[Primary CTA button] ← Primary button, md size
Specs:
- Centered vertically and horizontally in the parent container
- SVG illustration: unique per context (different shape compositions for different modules)
- Padding: --space-12 (48px) around the entire block
- Gap between elements: --space-4 (16px)
- CTA is optional (some empty states are informational only)
Sprint 0 empty states needed:
| Context | Illustration hint | Title | Subtitle | CTA |
|---|---|---|---|---|
| Dashboard activity | Circles connected by arcs | Aucune activité récente | L'activité de votre équipe apparaîtra ici. | — |
| Notifications | Bell with gentle waves | Aucune notification pour le moment | Vous serez notifié lorsque quelque chose nécessite votre attention. | — |
| Module placeholder | Shapes suggesting the module | Bientôt disponible | Ce module est en cours de développement. | Me notifier |
| Generic list | Dotted circle with plus | Aucun élément | Commencez par ajouter votre premier élément. | Ajouter |
13.8 Data Table¶
Basic table for Sprint 0. Enhanced with sort, filter, and pagination in Sprint 1.
Desktop Layout (≥768px)¶
┌──────────────────────────────────────────────────────┐
│ Nom │ Poste │ Statut │ Actions │ ← Header row
├──────────────────────────────────────────────────────┤
│ Moussa Koné │ Commercial │ ● Actif │ [⋮] │ ← Data row
│ Awa Diallo │ Comptable │ ● Actif │ [⋮] │
│ Ibrahim Touré │ Développeur │ ○ Congé │ [⋮] │
├──────────────────────────────────────────────────────┤
│ ◀ 1 2 3 ... 12 ▶ │ 50 résultats │ ← Footer (pagination)
└──────────────────────────────────────────────────────┘
Specs:
- Container: --color-surface background, --radius-lg border-radius, --shadow-sm
- Header row: --color-bg-tertiary background, --font-small weight 600, --color-text-secondary
- Header height: 44px
- Data row: --color-surface background, --font-body
- Row height: 52px (touch-friendly)
- Alternating rows: even rows get --color-bg-primary background (very subtle)
- Row hover: --color-primary-subtle background
- Cell padding: --space-4 (16px) horizontal
- Border: 1px --color-border-light between rows
- Action column: icon button ⋮ (more actions), opens dropdown on click
Mobile Layout (<768px) — Card List View¶
Tables transform into card lists on mobile:
┌──────────────────────────────────────┐
│ 👤 Moussa Koné │
│ Commercial ● Actif │
│ [⋮] │
├──────────────────────────────────────┤
│ 👤 Awa Diallo │
│ Comptable ● Actif │
│ [⋮] │
└──────────────────────────────────────┘
Each row becomes a card with key data visible and secondary data available on tap (expand).
13.9 Badge / Tag¶
Small labels for status, counts, and categorization.
Variants¶
| Variant | Background | Text | Border | Usage |
|---|---|---|---|---|
| default | --color-bg-tertiary |
--color-text-secondary |
none | Neutral categorization |
| primary | --color-primary-light |
--color-primary-hover |
none | Active/selected filter |
| success | --color-success-light |
--color-success |
none | "Actif", "Approuvé" |
| warning | --color-warning-light |
--color-warning (darkened) |
none | "En attente", "Bientôt" |
| error | --color-error-light |
--color-error |
none | "Refusé", "Expiré" |
| info | --color-info-light |
--color-info |
none | "Nouveau", informational |
Sizes:
- sm: height 20px, padding 0 6px, --font-caption (12px)
- md: height 24px, padding 2px 8px, --font-caption (12px, weight 500)
Specs:
- Border-radius: --radius-full (pill shape)
- Optional leading dot (8px circle, same color as text) for status badges
- Optional dismiss ✕ icon for removable tags (only on interactive variants)
13.10 Avatar¶
User/company identity display.
Sizes¶
| Size | Diameter | Font | Usage |
|---|---|---|---|
| xs | 24px | 10px | Inline mentions, compact lists |
| sm | 32px | 12px | Activity feed, notification items |
| md | 40px | 16px | Sidebar user, table rows |
| lg | 56px | 20px | User profile header |
| xl | 80px | 28px | Profile page, large display |
Variants¶
| Variant | Display | Usage |
|---|---|---|
| image | User photo (cropped circle) | When photo available |
| initials | 1-2 letter initials on colored background | Default fallback |
| icon | Generic user icon on neutral background | Anonymous/system |
| company | Company logo (square, rounded corners) | Tenant logo |
Initials variant colors: Generated from user name hash to ensure consistency. Palette of 8 warm/cool colors that work in both light and dark mode:
#F59E0B, #0D6B6E, #7C3AED, #DC2626, #2563EB, #059669, #DB2777, #EA580C
Specs:
- Border-radius: --radius-full (circle)
- Border: 2px --color-surface (creates a white ring, useful when overlapping)
- Initials: centered, white text on colored background, weight 600
- Online status dot (optional): 10px circle, bottom-right, --color-success with 2px white border
- Company variant: --radius-md border-radius (not circle)
13.11 Breadcrumb¶
Navigation path indicator. Desktop only (hidden on mobile where screen title is sufficient).
Layout¶
Specs:
- Separator: / character, --color-text-tertiary, --space-2 (8px) horizontal padding
- Link items: --font-small, --color-text-secondary, underline on hover, --color-primary on hover
- Current (last item): --font-small, --color-text-primary, weight 500, not clickable
- Truncation: on narrow viewports, early items collapse to … with a dropdown on click
- aria-label="Fil d'Ariane" on the nav element
- Current item: aria-current="page"
Responsive:
- ≥1024px: Full breadcrumb visible
- 768px–1023px: Show last 2 items, earlier ones collapse to …
- <768px: Hidden (page title in top bar is sufficient)
14. French Micro-copy Document¶
Every user-facing string in the host shell. Organized by component/screen. These are the EXACT strings the DEVELOPER implements in the i18n JSON file.
14.1 Authentication¶
| Key | String |
|---|---|
auth.login.title |
Connectez-vous à votre espace RH |
auth.login.email.label |
Adresse e-mail |
auth.login.email.placeholder |
[email protected] |
auth.login.password.label |
Mot de passe |
auth.login.password.show |
Afficher le mot de passe |
auth.login.password.hide |
Masquer le mot de passe |
auth.login.submit |
Se connecter |
auth.login.submit.loading |
Connexion… |
auth.login.forgot |
Mot de passe oublié ? |
auth.login.error.credentials |
Adresse e-mail ou mot de passe incorrect |
auth.login.error.network |
Impossible de se connecter. Vérifiez votre connexion Internet. |
auth.login.error.locked |
Votre compte est temporairement verrouillé. Réessayez dans 15 minutes ou réinitialisez votre mot de passe. |
auth.login.error.generic |
Une erreur est survenue. Veuillez réessayer. |
auth.login.welcome |
Bienvenue sur Papillon ! 🦋 |
auth.recovery.title |
Mot de passe oublié |
auth.recovery.description |
Saisissez votre adresse e-mail. Si un compte existe, vous recevrez un lien de réinitialisation. |
auth.recovery.email.label |
Adresse e-mail |
auth.recovery.submit |
Réinitialiser mon mot de passe |
auth.recovery.submit.loading |
Envoi en cours… |
auth.recovery.success.title |
E-mail envoyé ! |
auth.recovery.success.description |
Si l'adresse {email} est associée à un compte, vous recevrez un e-mail avec un lien de réinitialisation. |
auth.recovery.success.spam |
Vérifiez aussi vos courriers indésirables. |
auth.recovery.back |
Retour à la connexion |
auth.recovery.error.network |
Impossible d'envoyer la demande. Vérifiez votre connexion. |
auth.offline.title |
Vous êtes hors ligne |
auth.offline.description |
Connectez-vous à Internet pour accéder à Papillon. |
auth.offline.auto |
Dès que la connexion sera rétablie, cette page se rechargera automatiquement. |
auth.offline.retry |
Réessayer |
auth.token.expired |
Votre session a expiré. Veuillez vous reconnecter. |
14.2 Navigation — Module Labels¶
| Key | String |
|---|---|
nav.group.daily |
QUOTIDIEN |
nav.group.finance |
FINANCE |
nav.group.hr |
RESSOURCES HUMAINES |
nav.group.commercial |
COMMERCIAL |
nav.group.admin |
ADMINISTRATION |
nav.dashboard |
Tableau de bord |
nav.absence |
Congés & Absences |
nav.attendance |
Pointage QR |
nav.budget |
Lignes budgétaires |
nav.commitment |
Engagements |
nav.payroll |
Paie |
nav.expense |
Notes de frais |
nav.employee |
Employés |
nav.performance |
Performance & Objectifs |
nav.skills |
Compétences & GPEC |
nav.training |
Formation |
nav.recruitment |
Recrutement |
nav.crm |
CRM Interne |
nav.time |
Temps & Activités |
nav.hse |
HSE |
nav.settings |
Paramètres entreprise |
nav.users |
Utilisateurs & Rôles |
nav.audit |
Journal d'audit |
nav.billing |
Abonnement |
14.3 Navigation — States & Actions¶
| Key | String |
|---|---|
nav.locked.badge |
Bientôt |
nav.locked.status |
Bientôt disponible |
nav.locked.notify |
Me notifier à la sortie |
nav.locked.notified |
Vous serez notifié |
nav.mobile.more |
Plus |
nav.mobile.more.title |
Tous les modules |
nav.skip |
Aller au contenu principal |
14.4 Locked Module Descriptions¶
| Key | String |
|---|---|
module.expense.desc |
Gérez les notes de frais, les justificatifs et les remboursements de votre équipe. |
module.performance.desc |
Définissez des objectifs, réalisez les entretiens annuels et suivez la performance de vos collaborateurs. |
module.skills.desc |
Cartographiez les compétences, planifiez la GPEC et identifiez les besoins en formation. |
module.training.desc |
Gérez les plans de formation, suivez les certifications et développez les compétences de votre équipe. |
module.recruitment.desc |
Publiez des offres, suivez les candidatures et gérez votre processus de recrutement. |
module.time.desc |
Suivez le temps passé par projet, activité et collaborateur. |
module.hse.desc |
Gérez la santé, la sécurité et l'environnement au travail. |
14.5 Dashboard¶
| Key | String |
|---|---|
dashboard.title |
Tableau de bord |
dashboard.kpi.employees |
employés actifs |
dashboard.kpi.employees.trend |
+{count} ce mois |
dashboard.kpi.leaves |
demandes de congé en attente |
dashboard.kpi.budget |
du budget utilisé |
dashboard.kpi.budget.trend |
+{percent}% vs mois dernier |
dashboard.kpi.commitments |
engagements ce mois |
dashboard.actions.title |
Actions rapides |
dashboard.actions.leave |
Nouvelle demande de congé |
dashboard.actions.approve |
Approuver les demandes ({count}) |
dashboard.actions.clockin |
Pointer mon arrivée |
dashboard.activity.title |
Activité récente |
dashboard.activity.empty.title |
Aucune activité récente |
dashboard.activity.empty.description |
L'activité de votre équipe apparaîtra ici. |
dashboard.activity.viewall |
Voir toute l'activité |
dashboard.stale.warning |
Les données affichées datent de plus d'une heure. Connectez-vous pour actualiser. |
dashboard.lastupdated |
Dernière mise à jour il y a {time} |
14.6 Sync Status¶
| Key | String |
|---|---|
sync.synced |
Synchronisé |
sync.synced.description |
Toutes les données sont synchronisées avec le serveur. |
sync.pending |
{count} en attente |
sync.pending.description |
{count} opérations en attente de synchronisation |
sync.syncing |
Synchronisation… |
sync.offline |
Hors ligne |
sync.offline.description |
Vos modifications sont sauvegardées localement. La synchronisation reprendra automatiquement dès que la connexion sera rétablie. |
sync.offline.required |
Connexion requise pour synchroniser |
sync.lastsync |
Dernière synchronisation : il y a {time} |
sync.panel.title |
Synchronisation |
sync.panel.allclear |
Tout est à jour |
sync.panel.manual |
Synchroniser maintenant |
sync.item.pending |
En attente de synchronisation |
14.7 Notifications¶
| Key | String |
|---|---|
notifications.title |
Notifications |
notifications.markallread |
Tout marquer comme lu |
notifications.empty.title |
Aucune notification pour le moment |
notifications.empty.description |
Vous serez notifié lorsque quelque chose nécessite votre attention. |
notifications.viewall |
Voir toutes les notifications |
notifications.group.today |
Aujourd'hui |
notifications.group.yesterday |
Hier |
notifications.group.thisweek |
Cette semaine |
notifications.group.older |
Plus ancien |
14.8 User Menu¶
| Key | String |
|---|---|
user.profile |
Mon profil |
user.preferences |
Préférences |
user.shortcuts |
Raccourcis clavier |
user.logout |
Se déconnecter |
user.logout.success |
Vous avez été déconnecté avec succès. |
14.9 Dark Mode¶
| Key | String |
|---|---|
theme.system |
Système |
theme.light |
Clair |
theme.dark |
Sombre |
theme.toggle.label |
Changer le thème |
14.10 PWA¶
| Key | String |
|---|---|
pwa.install.message |
Installer Papillon sur votre téléphone pour un accès rapide |
pwa.install.button |
Installer |
pwa.install.dismiss |
Plus tard |
pwa.install.success |
Papillon est installé ! Accédez-y depuis votre écran d'accueil. |
14.11 Time Formatting¶
| Key | String |
|---|---|
time.now |
à l'instant |
time.minutes |
il y a {count} minutes |
time.hours |
il y a {count} heures |
time.yesterday |
hier à {time} |
time.date |
le {date} |
14.12 Generic / Shared¶
| Key | String |
|---|---|
generic.loading |
Chargement… |
generic.loading.module |
Chargement du module… |
generic.error.generic |
Une erreur est survenue. Veuillez réessayer. |
generic.error.network |
Problème de connexion. Vérifiez votre accès Internet. |
generic.retry |
Réessayer |
generic.cancel |
Annuler |
generic.save |
Enregistrer |
generic.save.loading |
Enregistrement… |
generic.save.success |
Modifications enregistrées |
generic.delete |
Supprimer |
generic.delete.undo |
Annuler la suppression |
generic.confirm |
Confirmer |
generic.close |
Fermer |
generic.back |
Retour |
generic.next |
Suivant |
generic.previous |
Précédent |
generic.search |
Rechercher… |
generic.noResults |
Aucun résultat trouvé |
generic.required |
Ce champ est obligatoire |
generic.invalidEmail |
Veuillez saisir une adresse e-mail valide |
generic.copyright |
© 2026 ALTARYS ENTERPRISE |
generic.tagline |
Votre assistant RH de confiance |
generic.empty.title |
Aucun élément |
generic.empty.description |
Commencez par ajouter votre premier élément. |
generic.empty.add |
Ajouter |
generic.results |
{count} résultats |
generic.selected |
{count} sélectionnés |
14.13 Accessibility Labels (ARIA)¶
| Key | String |
|---|---|
aria.login.form |
Formulaire de connexion |
aria.nav.main |
Navigation principale |
aria.nav.breadcrumb |
Fil d'Ariane |
aria.nav.bottom |
Navigation rapide |
aria.sync.indicator |
Indicateur de synchronisation |
aria.notifications.bell |
Notifications |
aria.notifications.unread |
{count} notifications non lues |
aria.theme.toggle |
Changer le thème d'affichage |
aria.user.menu |
Menu utilisateur |
aria.sidebar.toggle |
Ouvrir le menu de navigation |
aria.sidebar.collapse |
Réduire le menu |
aria.modal.close |
Fermer la fenêtre |
aria.toast.dismiss |
Fermer la notification |
15. Brand Compliance Checklist¶
Papillon HR Suite ✓¶
- [x] Amber/gold primary color used —
#F59E0Bas--color-primary, used for buttons, active nav, accents, focus rings - [x] Mobile-first layout (360px designed first) — All wireframes start at 360px. Bottom navigation, single-column KPIs, full-screen modals on mobile
- [x] Offline states specified for ALL write operations — Sync indicator (§8) covers all 4 states. Dashboard offline behavior (§6.7). Login offline (§4.1). PWA offline landing (§12.3). Sync panel shows queued operations
- [x] Touch targets 48px minimum — Inputs: 48px height. Buttons (md): 44px + padding. Bottom nav: 64×90px. Sidebar items: 44px + 4px gap. All verified per spec
- [x] French micro-copy warm and human — All strings in §14 use warm, conversational French. Errors are helpful, not technical. "Oops" avoided (too informal for professional context), replaced with empathetic phrasing
- [x] Loading skeletons (not spinners) specified — §13.6 specifies skeleton variants for text, title, avatar, card, button, table-row. Shimmer animation specified. Only exception: button loading state uses inline spinner (contextually appropriate)
- [x] Generous whitespace — not crowded — Spacing tokens range 4–64px. Cards use 20–24px padding. Content padding 16–32px per breakpoint. KPI grid has 16–24px gaps. Single-column mobile layout prevents crowding
- [x] Celebration animation specified for milestone actions — §1 Q9: First login confetti + welcome toast. PWA install success toast with butterfly animation. Module-specific celebrations deferred to module design docs
Additional Compliance¶
- [x] Deep teal/forest green secondary —
#0D6B6Esidebar, consistent throughout - [x] Warm off-white background —
#FFFDF7, not clinical white - [x] Dark mode warm —
#1A1614base, warm brown tones, not cold charcoal - [x] Typography 16px body minimum —
--font-body: 16px. Smallest text:--font-overline: 11px(labels only, never body) - [x] French diacritics support — Inter font family specified, supports full Latin Extended
- [x] Low density — One action per screen on mobile. Dashboard shows 2 KPI cards above fold, not 4
- [x] Guided onboarding — First login welcome, contextual empty states with CTAs, locked module previews educate about upcoming features
- [x] "Votre assistant RH de confiance" tone — Tagline in login, splash screen. Micro-copy is helpful and reassuring throughout
- [x] WCAG AA compliance — Contrast ratios verified for all color pairs. Color never sole indicator. ARIA labels in French. Focus indicators specified. Skip navigation link included
- [x] Design tokens (not hardcoded) — Complete token table in §2. All component specs reference tokens, not hex values
- [x] Currency: XOF with zero decimals — Specified in dashboard KPI examples: "450 000 XOF" (space as thousands separator)
- [x] Date format: DD/MM/YYYY — Specified in notification examples and input specs
- [x] Number format: space thousands separator — "22 500 000 XOF" in dashboard specs
- [x]
prefers-reduced-motionrespected — §1 Q8 animation philosophy: all animations respect reduced motion preference - [x] Performance budget compatible — SVG icons (not images), max 2 font families, code splitting, skeleton loading, minimal animations
Appendix A: Component File Mapping¶
For the DEVELOPER — where each component should be created:
| Component | Location | Rationale |
|---|---|---|
AppShell |
frontend/src/components/shell/AppShell.tsx |
Shell-only, not shared |
Sidebar |
frontend/src/components/shell/Sidebar.tsx |
Shell-only |
BottomNav |
frontend/src/components/shell/BottomNav.tsx |
Shell-only |
TopBar |
frontend/src/components/shell/TopBar.tsx |
Shell-only |
SyncIndicator |
frontend/src/components/shell/SyncIndicator.tsx |
Shell-only |
SyncPanel |
frontend/src/components/shell/SyncPanel.tsx |
Shell-only |
NotificationBell |
frontend/src/components/shell/NotificationBell.tsx |
Shell-only |
NotificationPanel |
frontend/src/components/shell/NotificationPanel.tsx |
Shell-only |
UserMenu |
frontend/src/components/shell/UserMenu.tsx |
Shell-only |
DarkModeToggle |
frontend/src/components/shell/DarkModeToggle.tsx |
Shell-only |
Dashboard |
frontend/src/pages/Dashboard.tsx |
Page-level |
LoginPage |
frontend/src/pages/auth/LoginPage.tsx |
Page-level |
RecoveryPage |
frontend/src/pages/auth/RecoveryPage.tsx |
Page-level |
OfflineLanding |
frontend/src/pages/auth/OfflineLanding.tsx |
Page-level |
Button |
packages/shared-components/src/Button.tsx |
Shared |
Input |
packages/shared-components/src/Input.tsx |
Shared |
Card |
packages/shared-components/src/Card.tsx |
Shared |
Modal |
packages/shared-components/src/Modal.tsx |
Shared |
Toast |
packages/shared-components/src/Toast.tsx |
Shared |
Skeleton |
packages/shared-components/src/Skeleton.tsx |
Shared |
EmptyState |
packages/shared-components/src/EmptyState.tsx |
Shared |
DataTable |
packages/shared-components/src/DataTable.tsx |
Shared |
Badge |
packages/shared-components/src/Badge.tsx |
Shared |
Avatar |
packages/shared-components/src/Avatar.tsx |
Shared |
Breadcrumb |
packages/shared-components/src/Breadcrumb.tsx |
Shared |
theme.ts |
packages/shared-components/src/theme.ts |
Token system |
theme-papillon.ts |
frontend/src/theme-papillon.ts |
Papillon overrides |
Appendix B: Design Token Implementation Guide¶
For the DEVELOPER — how to implement the token system:
// packages/shared-components/src/theme.ts
// 1. Define the token interface (type-safe)
export interface ThemeTokens {
colors: {
primary: string;
primaryHover: string;
primaryLight: string;
// ... all color tokens from §2.1
};
typography: {
fontFamily: string;
display: TypographyToken;
h1: TypographyToken;
// ... all type tokens from §2.2
};
spacing: Record<string, string>; // from §2.3
radius: Record<string, string>; // from §2.4
shadows: Record<string, string>; // from §2.5
animation: Record<string, string>; // from §2.6
layout: Record<string, string>; // from §2.7
zIndex: Record<string, number>; // from §2.8
}
// 2. Papillon theme implements this interface
// frontend/src/theme-papillon.ts
export const papillonTheme: ThemeTokens = {
colors: {
primary: '#F59E0B',
primaryHover: '#D97706',
// ... all values from §2.1
},
// ...
};
// 3. Theme is injected via CSS custom properties at the root
// This allows Enterprise frontend to inject different values
// at Month 12 without changing component code
End of document. This design spec is complete and self-contained for Sprint 0 implementation.