Files
cv-maker/docs/conception/04-specifications-ui.md

8.2 KiB
Raw Permalink Blame History

04 — Spécifications UI et composants

1. Design tokens (Tailwind CSS 4)

Fichier source : app/globals.css (via @import "tailwindcss" + @theme).

Token Valeur Usage
--color-surface blanc (#ffffff) feuille A4
--color-paper #f1f5f9 gris très clair fond de l'application hors feuille
--color-ink #0f172a texte principal
--color-muted #64748b texte secondaire
--color-accent #2563eb actions, focus, onglet actif
--color-danger #dc2626 boutons « × » suppression
--color-border #e2e8f0 bordures et séparateurs
--radius-* 0.5rem (md), 0.375rem (sm) cohérence des angles
--font-sans système + Inter en fallback lisibilité écran et impression

Typographie du CV (feuille A4) :

Élément Suggestion
Nom / accroche (header) text-base font-semibold à text-xl font-bold
Titres de section text-[0.75rem] uppercase tracking-wide text-muted font-bold
Corps text-[0.8rem] leading-snug text-ink

Palette neutre recommandée

Le CV imprimé doit rester sobre : encre sombre sur blanc, onglet actif en accent, aucun fond de couleur sur la feuille A4 (économie d'encre et lisibilité).

2. Barre d'application (CvToolbar)

Élément Contenu
Gauche Logo (icône FileText) + « CV-Maker »
Droite Bouton Imprimer / PDF, bouton Importer JSON, bouton Exporter JSON, bouton Réinitialiser, lien Documentation

Rendre les liens actifs en font-semibold et inactifs en text-muted (page courante) — navigation classique App Router (usePathname).

  • Masqué à l'impression (print-hidden), bordure supérieure.
  • Contenu : nom de l'application + lien Mentions légales vers /mentions-legales (remplace l'ancien lien RGPD de la barre).

3. Panneau latéral gauche

3.1 CvTabs — onglets 1 à 6

  • Basé sur la primitive ui/Tabs : role="tablist", boutons role="tab", aria-selected, role="tabpanel".
  • Affichage : barre de 6 boutons horizontaux, libellé = numéro + label (1 · Accroche, 2 · État civil, …).
  • Onglet actif : fond accent, texte blanc ; inactifs : bordure border + texte muted.
  • Contenu piloté par le parent (activeTab dans CvInputPanel) : un seul MarkdownTextarea monté pour l'onglet actif. Le texte de l'onglet actif est le value contrôlé.

3.2 MarkdownTextarea

Élément Spécification
Props label: string, value: string, onChange(text: string): void, placeholder?: string
Comportement textarea contrôlé, onChange → mise à jour immédiate de l'état + persistance
UX min-h variable (accroche + courte), resize-y, monospace en saisie (font-mono) avec bascule visuelle explicite « le texte est en Markdown »
Note Markdown petite légende « Syntaxe Markdown supportée » avec lien vers /documentation

3.3 ExperienceManager

Composition :

  • En-tête : titre « Expériences » + bouton + (ui/Button, variante primary) → prépare une nouvelle expérience (zones titre/contenu vidées, focus du formulaire).
  • Formulaire : deux champs — titre (input texte) + contenu (textarea Markdown, même composant que les zones 1-6). Boutons : Enregistrer et Annuler (annulation quand modification).
  • Liste numérotée : sous le formulaire.

États internes suivants :

type ExperienceEditor =
  | { mode: "closed" }                    // rien en édition
  | { mode: "new" }                       // ajout en cours
  | { mode: "edit"; id: Experience["id"] }; // modification en cours

3.4 Ligne d'expérience (ExperienceItem)

Format visuel : 1. Titre de l'expérience [Modifier] [×]

Action Clé Comportement
Modifier bouton icône Pencil + libellé passe en mode edit, recharge titre/contenu dans le formulaire
Supprimer bouton × (IconButton, variante danger) confirmation (petit état interne « Confirmer la suppression ? [Oui/Non] » ou window.confirm) puis removeExperience(id)

Composants ui/Button, ui/IconButton, ui/Textarea, ui/Tabs sont les primitives réutilisables (cf. le volet « composants réutilisables » du cahier des charges).

4. Panneau latéral droit — feuille A4 (CvPreview)

4.1 Conteneur de feuille

<div className="cv-sheet">           {/* wrapper lecture imprimée */}
  <header>CvHeader</header>
  <div className="cv-sheet-body">
    <aside>CvLeftColumn</aside>     {/* 1/3 */}
    <main>CvRightColumn</main>       {/* 2/3 */}
  </div>
</div>

Dimensions écran :

Propriété Valeur
Largeur 210mm (with max-w-full pour rester contenu)
Hauteur 297mm (min-h, la feuille peut être plus longue si contenu dépassant — dépassement scrollable)
Ombre / bordure shadow-lg border border-border (écran) / supprimées à l'impression
Padding 16mm (marges type CV)
Fond blanc surface

Ratio 1/3–2/3 : .cv-sheet-body { display:grid; grid-template-columns: 1fr 2fr; gap: 12mm; }.

4.2 CvHeader (accroche — zone 1)

  • Toute la largeur de la feuille.
  • Rendu Markdown de data.accroche (contenu typiquement : nom + poste + accroche).
  • border-bottom de séparation avec le corps de feuille.

4.3 CvLeftColumn (sections 2 à 6, 1/3)

Section générique CvSection :

<CvSection title="État civil" html={htmlEtatCivil} />
<CvSection title="Formations" html={htmlFormations} />
<CvSection title="Compétences" html={htmlCompetences} />
<CvSection title="Soft skills" html={htmlSoftSkills} />
<CvSection title="Centres d'intérêt" html={htmlCentresInteret} />

Règles :

  • Titre stylé selon le token « titres de section » (petit, majuscules, espacement).
  • Contenu : dangerouslySetInnerHTML={{ __html: sanitizedHtml }} uniquement si le HTML a été produit par lib/markdown (marked + DOMPurify) — jamais de HTML saisi brut.
  • Si une section est vide → masquée (pas de cadre vide) ; le GOAL.md ne demande pas d'emplacement fixe vide.

4.4 CvRightColumn (expériences, 2/3)

  • Titre fixe : « Expériences professionnelles » (selon token titres de section).
  • Liste des data.experiences dans l'ordre du tableau = ordre de saisie (la 1ʳᵉ entrée en haut, cf. module 03).
  • Chaque CvExperiencePreview : titre en gras + rendu Markdown du contenu.
  • Section vide → texte léger : instruction « Ajoutez une expérience dans le panneau gauche » (rendu écran uniquement, masqué à l'impression via print:hidden).

4.5 Dépassement de contenu

  • Sur le feuille : la hauteur est le hapax A4 à l'impression (une page). En revanche, le contenu peut dépasser → overflow tolérable sur une « page 2 » de preview est hors scope v1. Comportement choisi : la feuille s'allonge (min-h seulement) pour montrer tout le contenu ; l'impression sera paginée par le navigateur (2 pages éventuelles). Documenté comme limitation assumée.

5. États UI transverses

État Signal
Erreur de sauvegarde console.warn de l'erreur (enregistrement silencieux, sans pastille)
Onglet actif fond accent (haut contraste)
Formulaire expérience (nouveau / modification) zone surlignée border-accent
Suppression état de confirmation inline (Oui / Non)

6. Responsive minimal (nice to have)

  • ≥ 1024 px : mise en page 2 colonnes (1/3 – 2/3) inchangée.
  • < 1024 px : le panneau gauche passe en w-full au-dessus de la preview (empilés verticalement) ; la preview se centre. Tablette viable ; mobile lisible mais non optimisé (desktop first).
  • L'impression ne dépend pas du viewport (elle est pilotée par @media print, voir module 02 §7).

7. Accessibilité (bonnes pratiques)

  • Onglets : role="tablist"/"tab"/"tabpanel", navigation clavier (flèches ←/→), aria-selected, aria-controls, id liés.
  • Boutons icônes : aria-label explicites (« Modifier l'expérience », « Supprimer l'expérience »).
  • Inputs/textarea : <label htmlFor> avec id unique.
  • Lien focus : contour focus-visible accent (Tailwind focus-visible:outline-2 + focus-visible:outline-accent).