chore init

This commit is contained in:
devcodetools committed 2026-09-16 21:34:09 +02:00
1 parent 1b7406b1d3
commit a0d312a884
60 files changed
+5046 -125

No files matched your search

+168
View File
@@ -0,0 +1,168 @@
# 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`).
## 2bis. Pied de page (`Footer`)
- **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 à 5
- Basé sur la primitive `ui/Tabs` : `role="tablist"`, boutons `role="tab"`, `aria-selected`, `role="tabpanel"`.
- Affichage : barre de 5 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-5). Boutons : **Enregistrer** et **Annuler** (annulation quand modification).
- **Liste numérotée** : sous le formulaire.
États internes suivants :
```ts
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
```tsx
<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 à 5, 1/3)
Section générique `CvSection` :
```tsx
<CvSection title="État civil" html={htmlEtatCivil} />
<CvSection title="Formations" html={htmlFormations} />
<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`).