From f824053a3119fb3b61b251352e9bfb556dd57dfb Mon Sep 17 00:00:00 2001 From: Ladebeze66 Date: Wed, 22 Apr 2026 16:12:31 +0200 Subject: [PATCH] etape6ok --- app/Competences/page.jsx | 172 +++++++++++++++++----- app/components/VignetteCarousel.tsx | 68 +++++++++ app/layout.tsx | 12 +- app/portfolio/page.jsx | 196 ++++++++++++++++++++------ docs-site-interne/REFONTE-VISUELLE.md | 83 ++++++++++- docs-site-interne/feuille-de-route.md | 5 +- 6 files changed, 446 insertions(+), 90 deletions(-) create mode 100644 app/components/VignetteCarousel.tsx diff --git a/app/Competences/page.jsx b/app/Competences/page.jsx index b728e69..203aa78 100644 --- a/app/Competences/page.jsx +++ b/app/Competences/page.jsx @@ -3,12 +3,24 @@ import { useEffect, useState } from "react"; import Link from "next/link"; import { getApiUrl } from "../utils/getApiUrl"; -import CarouselCompetences from "../components/CarouselCompetences"; +import VignetteCarousel from "../components/VignetteCarousel"; import "../globals.css"; import "../assets/main.css"; +/** + * Liste des compétences — refonte "Digital Atelier" (étape 6). + * + * Même pattern de grille asymétrique 2/3 + 1/3 que `app/portfolio/page.jsx` pour + * garder une cohérence visuelle entre les deux rubriques principales. Le + * `CarouselCompetences` est retiré de la liste (arbitrage REFONTE-VISUELLE.md §2 : + * carousel réservé aux galeries intra-fiche) : seule la première image est + * affichée ici, ce qui allège le rendu et clarifie la lecture. + */ +const spanPattern = ["md:col-span-4", "md:col-span-2", "md:col-span-2", "md:col-span-4"]; + export default function Page() { const [competences, setCompetences] = useState([]); + const [isLoading, setIsLoading] = useState(true); const apiUrl = getApiUrl(); useEffect(() => { @@ -18,60 +30,144 @@ export default function Page() { if (!response.ok) { throw new Error(`Erreur de récupération des compétences : ${response.statusText}`); } - + const data = await response.json(); - - // Tri sécurisé des compétences par `order` - const sortedCompetences = (data.data ?? []).sort((a, b) => ((a.attributes?.order ?? 999) - (b.attributes?.order ?? 999))); - + + const sortedCompetences = (data.data ?? []).sort( + (a, b) => ((a.attributes?.order ?? 999) - (b.attributes?.order ?? 999)) + ); + setCompetences(sortedCompetences); } catch (error) { console.error("❌ Erreur lors de la récupération des compétences :", error); + } finally { + setIsLoading(false); } } - + fetchCompetences(); }, [apiUrl]); - - - return ( -
+
+ {/* En-tête éditorial cohérent avec le portfolio. */} +
+
+ + Compétences · Savoir-faire + +

+ Ce que je sais faire, et comment +

+

+ Chaque fiche détaille une compétence, son contexte d’apprentissage et des + exemples concrets — ouvrez une carte pour voir les outils mobilisés et les + projets associés. +

+
+
- - {competences.length === 0 ? ( -

Aucune compétence disponible.

+ {isLoading ? ( +
+ {Array.from({ length: 4 }).map((_, idx) => ( +
+
+
+
+
+
+ ))} +
+ ) : competences.length === 0 ? ( +
+ +

+ Aucune compétence disponible pour le moment. +

+
) : ( -
- {competences.map((competence) => { - const pictures = competence.picture || []; - const images = pictures.map(picture => ({ - url: picture.url ? `${apiUrl}${picture.url}` : "/placeholder.jpg", - alt: picture.name || "Competence image" +
+ {competences.map((competence, idx) => { + const pictures = competence.picture ?? []; + const images = pictures.map((img) => ({ + url: img.url ? `${apiUrl}${img.url}` : "/placeholder.jpg", + alt: img.name || `Visuel de la compétence ${competence.name}`, })); + const firstImage = images[0]; return ( -
- -
- -
-
-

{competence.name}

-

- {competence.description} -

-
- -
+ +
+ {images.length > 1 ? ( + + ) : firstImage ? ( + {firstImage.alt} + ) : ( +
+ +
+ )} +
+ +
+ + Compétence + +

+ {competence.name} +

+ {competence.description && ( +

+ {competence.description} +

+ )} + + + Explorer + + +
+ ); })}
)} -
+ ); -} \ No newline at end of file +} diff --git a/app/components/VignetteCarousel.tsx b/app/components/VignetteCarousel.tsx new file mode 100644 index 0000000..488ae7e --- /dev/null +++ b/app/components/VignetteCarousel.tsx @@ -0,0 +1,68 @@ +"use client"; + +import { Swiper, SwiperSlide } from "swiper/react"; +import { Autoplay, Pagination } from "swiper/modules"; +import "swiper/css"; +import "swiper/css/pagination"; + +/** + * Carousel allégé réservé aux vignettes des listes (portfolio, compétences). + * + * Différences volontaires par rapport à `Carousel.tsx` / `CarouselCompetences.tsx` + * (qui restent utilisés dans les fiches détail) : + * + * - **Pas de flèches de navigation** : chaque vignette est enveloppée d'un + * `` qui capture le clic. Les flèches Swiper créaient une zone de clic + * ambiguë (on croit naviguer dans les images, on arrive sur la fiche détail). + * L'autoplay + le swipe tactile suffisent à l'échelle d'une vignette. + * - **Pas de lightbox** (pas de `createPortal`). La lightbox reste la signature + * de la fiche détail ; en vignette on ne propose que la navigation vers la fiche. + * - **Pagination Stitch** : bullets teintés `primary` via des variables CSS + * Swiper surchargées en inline, pour éviter de polluer `globals.css` avec un + * sélecteur global. + * + * Le composant couvre 100 % de son conteneur parent (qui fixe le `aspect-ratio` + * dans les pages liste), avec `object-cover` sur les images. + */ +interface VignetteCarouselProps { + images: Array<{ url: string; alt: string }>; + autoplayDelay?: number; +} + +export default function VignetteCarousel({ + images, + autoplayDelay = 3500, +}: VignetteCarouselProps) { + return ( + 1} + autoplay={{ delay: autoplayDelay, disableOnInteraction: false }} + pagination={{ clickable: false }} + className="h-full w-full" + style={ + { + // Surcharges Swiper : bullets primary Stitch, taille discrète. + "--swiper-pagination-color": "#26445d", + "--swiper-pagination-bullet-inactive-color": "#ffffff", + "--swiper-pagination-bullet-inactive-opacity": "0.55", + "--swiper-pagination-bullet-size": "6px", + "--swiper-pagination-bullet-horizontal-gap": "3px", + "--swiper-pagination-bottom": "8px", + } as React.CSSProperties + } + > + {images.map((img, index) => ( + + {img.alt} + + ))} + + ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 1ff14a7..3fdb2d9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -94,10 +94,16 @@ export default function RootLayout({ children }: { children: React.ReactNode }) /> -
- {/* Wallpaper plein écran (fondation, ne change pas avec la refonte). */} -
+ {/* Wallpaper plein écran (fondation). + `fixed inset-0` plutôt qu'`absolute` dans le grid : le wallpaper est + désormais calé sur le viewport, pas sur la hauteur totale de la page. + Sans ça, sur les pages longues (portfolio, compétences, fiches) le + conteneur grid atteignait 2-3 viewports de haut et `background-size: cover` + zoomait l'image pour couvrir cette hauteur — d'où le rendu incohérent + entre home (courte) et listes (longues). Corrigé le 2026-04-22. */} + +
{/* Cercles animés : repalette en ton indigo-ardoise (Stitch "Digital Atelier"). */}
diff --git a/app/portfolio/page.jsx b/app/portfolio/page.jsx index 3c088c4..ff9a5e5 100644 --- a/app/portfolio/page.jsx +++ b/app/portfolio/page.jsx @@ -3,76 +3,180 @@ import { useEffect, useState } from "react"; import Link from "next/link"; import { getApiUrl } from "../utils/getApiUrl"; -import Carousel from "../components/Carousel"; +import VignetteCarousel from "../components/VignetteCarousel"; import "../assets/main.css"; import "../globals.css"; +/** + * Liste des projets — refonte "Digital Atelier" (étape 6). + * + * Règle DESIGN.md §6 "No-Grid-Lock" : on bannit la grille 3 colonnes symétrique. + * On utilise une grille asymétrique 2/3 + 1/3 alternée par paires, qui crée un + * rythme éditorial plutôt qu'un catalogue. Arbitrage acté dans REFONTE-VISUELLE.md §2 : + * le carousel reste réservé aux fiches détail (étape 7) — la liste n'affiche que + * la première image, ce qui allège le rendu et clarifie la hiérarchie. + * + * Pattern de spans (modulo 4 sur desktop 6 colonnes) : + * idx 0 → col-span-4 (vedette) + * idx 1 → col-span-2 + * idx 2 → col-span-2 + * idx 3 → col-span-4 + * → répète l'alternance pour éviter la monotonie sans dépendre du nombre d'items. + */ +const spanPattern = ["md:col-span-4", "md:col-span-2", "md:col-span-2", "md:col-span-4"]; + export default function Page() { const [projects, setProjects] = useState([]); + const [isLoading, setIsLoading] = useState(true); const apiUrl = getApiUrl(); useEffect(() => { async function fetchProjects() { try { - const response = await fetch(`${apiUrl}/api/projects?populate=picture&sort=order:asc`); // Récupération triée depuis Strapi + const response = await fetch( + `${apiUrl}/api/projects?populate=picture&sort=order:asc` + ); if (!response.ok) { throw new Error(`Erreur de récupération des projets : ${response.statusText}`); } const data = await response.json(); - - // Tri des projets côté Next.js (au cas où Strapi ne le fait pas) - const sortedProjects = (data.data ?? []).sort((a, b) => (a.order || 999) - (b.order || 999)); - + + const sortedProjects = (data.data ?? []).sort( + (a, b) => (a.order || 999) - (b.order || 999) + ); + setProjects(sortedProjects); } catch (error) { console.error("❌ Erreur lors de la récupération des projets :", error); + } finally { + setIsLoading(false); } } - + fetchProjects(); }, [apiUrl]); - return ( -
-
- {projects.map((project) => { - const pictures = project.picture ?? []; - const images = pictures.map((img) => ({ - url: `${apiUrl}${img.url}`, - alt: img.name || "Project image", - })); - - return ( -
- -
- {images.length > 1 ? ( - - ) : ( - {images[0]?.alt - )} -
- -
-

{project.name}

-

- {project.description} -

-
- +
+ {/* En-tête éditorial, aligné sur le hero de la home (kicker + titre Manrope). */} +
+
+ + Portfolio · Projets + +

+ Les projets qui m’ont construit +

+

+ Une sélection de réalisations pédagogiques, personnelles et professionnelles — + cliquez sur une carte pour en découvrir la genèse, les choix techniques et les + visuels. +

- ); - })} -
-
+ + {/* État de chargement : 4 squelettes qui respectent la grille asymétrique. */} + {isLoading ? ( +
+ {Array.from({ length: 4 }).map((_, idx) => ( +
+
+
+
+
+
+ ))} +
+ ) : projects.length === 0 ? ( +
+ +

+ Aucun projet à afficher pour le moment. +

+
+ ) : ( +
+ {projects.map((project, idx) => { + const pictures = project.picture ?? []; + const images = pictures.map((img) => ({ + url: `${apiUrl}${img.url}`, + alt: img.name || `Visuel du projet ${project.name}`, + })); + const firstImage = images[0]; + + return ( + +
+ {images.length > 1 ? ( + + ) : firstImage ? ( + {firstImage.alt} + ) : ( +
+ +
+ )} +
+ +
+ + Projet + +

+ {project.name} +

+ {project.description && ( +

+ {project.description} +

+ )} + + + Découvrir + + +
+ + ); + })} +
+ )} +
); } diff --git a/docs-site-interne/REFONTE-VISUELLE.md b/docs-site-interne/REFONTE-VISUELLE.md index 5b95611..2bb9d1d 100644 --- a/docs-site-interne/REFONTE-VISUELLE.md +++ b/docs-site-interne/REFONTE-VISUELLE.md @@ -1,7 +1,7 @@ # Refonte visuelle — Direction "Digital Atelier" **Créé :** 2026-04-22 -**Statut :** en cours (étapes 1-5/8 terminées) +**Statut :** en cours (étapes 1-6/8 terminées) **Source d'inspiration :** `stitch_V1/` (design newsletter Stitch — `DESIGN.md` et `code.html`). **Audit préalable :** [`captures/AUDIT-VISUEL.md`](./captures/AUDIT-VISUEL.md). @@ -59,7 +59,7 @@ Chaque étape = un lot cohérent + éventuelle mise à jour de `captures/AUDIT-V | 3 | Migration typographique globale (Orbitron → Manrope / Newsreader) | `app/**/*.{tsx,jsx,js}`, `app/assets/main.css` | **fait** (2026-04-22) | | 4 | Layout racine : header No-Line, burger ghost, palette cercles, compteur migré, drawer | `app/layout.tsx`, `app/components/NavLink.jsx`, `app/components/Footer.jsx` | **fait** (2026-04-22) | | 5 | Home : hero vellum, portrait frame, takeaways, pull-quote, CTAs | `app/page.tsx` | **fait** (2026-04-22) | -| 6 | Listes portfolio + compétences : grille asymétrique, cartes éditoriales | `app/portfolio/page.jsx`, `app/competences/page.jsx`, composants `Carousel*` | à faire | +| 6 | Listes portfolio + compétences : grille asymétrique, cartes éditoriales | `app/portfolio/page.jsx`, `app/competences/page.jsx`, composants `Carousel*` | **fait** (2026-04-22) | | 7 | Fiches détail + modale glossaire + GrasBot (jewel flottant) | `app/portfolio/[slug]/page.tsx`, `app/competences/[slug]/page.tsx`, `app/components/ModalGlossaire.tsx`, `app/components/ChatBot.js` | à faire | | 8 | Contact + Footer éditorial | `app/contact/page.js`, `app/components/ContactForm.tsx`, `app/components/Footer.jsx` | à faire | @@ -150,6 +150,85 @@ Les icônes Material Symbols Outlined fonctionnent via **ligatures de font** : u Composant wrapper `` qui pose automatiquement `translate="no"` envisagé comme DRY à long terme (hors scope actuel). +## 6. Étape 6 — Listes portfolio + compétences (2026-04-22) + +Les deux pages liste étaient héritées du design avant refonte : cartes `bg-white/80 rounded-lg` à taille **fixe** (`w-80 h-96` sur portfolio, `max-w-xs…2xl` en cascade sur compétences), `hover:scale-105` qui débordait sous le header, **chaque vignette embarquait un `Swiper` autoplay** (cf. `Carousel.tsx` et `CarouselCompetences.tsx`) — bruit visuel constant, coût réseau (3-5 images × N cartes chargées d'emblée), et incohérence avec l'arbitrage acté § 2 *"carousel réservé aux galeries intra-fiche"*. Sur mobile, la largeur fixe 320 px de la carte portfolio débordait un viewport 360 px + padding. + +### Direction Stitch appliquée + +**Règle DESIGN.md §6 "No-Grid-Lock"** interdit la grille 3 colonnes symétrique. On adopte une grille **asymétrique 2/3 + 1/3** qui donne un rythme éditorial plutôt qu'un catalogue : + +``` +md:grid-cols-6, pattern de spans par index modulo 4 : + idx 0 → md:col-span-4 (vedette, 2/3) + idx 1 → md:col-span-2 (1/3) + idx 2 → md:col-span-2 (1/3) + idx 3 → md:col-span-4 (vedette, 2/3) +``` + +Sur `sm` on bascule en `grid-cols-2` classique (pas de `col-span` tablette pour garder 2 cartes par ligne), sur mobile `grid-cols-1` pleine largeur. Le même pattern est répliqué pour les skeletons de chargement → l'empreinte visuelle est stable pendant le fetch. + +### Anatomie de carte "feuillet de vellum" + +Toutes les cartes sont des `Link` pleine-carte (plus de `Link` imbriqué ambigu) avec : + +- Wrapper : `rounded-sheet bg-surface-container-lowest/85 backdrop-blur-vellum shadow-ambient`, `group` pour propager le hover. +- Hover : `hover:-translate-y-0.5 hover:shadow-jewel` (lift subtil + empreinte tactile Stitch) remplace l'ancien `scale-105` qui débordait et cassait l'alignement de la grille. +- Média : `aspect-[4/3]` fixe (plus de hauteurs variables) + `overflow-hidden` + `object-cover` + `group-hover:scale-[1.03]` sur l'image (sensation vitrine, discret). +- Placeholder `material-symbols-outlined image` centré si pas d'image — `translate="no"` en place (§ 4 quinquies). +- Corps : kicker uppercase tracking-[0.3em] (« Projet » / « Compétence ») + titre Manrope extrabold `text-primary` + description Newsreader `text-on-surface-variant` clampée à 3 lignes (`line-clamp-3`, core Tailwind 3.4) pour homogénéiser les hauteurs. +- CTA tertiaire « Découvrir → » / « Explorer → » Manrope uppercase `text-primary`, avec flèche Material Symbols `arrow_forward` qui se décale à droite au hover (`group-hover:translate-x-1`). Icône `translate="no"`. + +### États + +- **Chargement** : 4 skeletons animés (`animate-pulse bg-surface-container-low/80`) suivant le même pattern de spans que la grille réelle → pas de saut de layout. +- **Vide** : carte centrée avec Material Symbol (`inbox` pour portfolio, `school` pour compétences) + message Newsreader italique. Remplace l'ancien `text-gray-500` orphelin. + +### Ce que ça règle + +- **Régression mobile** : `w-80 h-96` retiré, la carte prend la largeur de la colonne → plus de débordement S25 Ultra. +- **Bruit visuel** : `Swiper` autoplay retiré des listes, le scroll n'est plus concurrencé par 3-5 carousels qui tournent simultanément. +- **Poids réseau** : une image `loading="lazy"` par carte au lieu de toutes les images de toutes les galeries au chargement initial. +- **Hiérarchie** : les pages liste ont désormais un **en-tête éditorial** (kicker + titre + pitch) cohérent avec le hero de la home. +- **Cohérence Stitch** : palette `primary` / `on-surface-variant`, radius `rounded-sheet`, ombres `shadow-ambient` / `shadow-jewel`, typographie Manrope + Newsreader → alignement 1:1 avec la home. + +### Correctif post-étape 6 — wallpaper sur-zoomé sur pages longues + +Retour utilisateur une fois `/portfolio` en ligne : le wallpaper apparaît **beaucoup plus zoomé** sur les listes que sur la home, ce qui casse la cohérence visuelle entre les rubriques. + +**Cause** : dans `app/layout.tsx`, la div `.bg-wallpaper` était posée en `absolute inset-0` **à l'intérieur** du conteneur grid `min-h-[100dvh]`. Sur la home, le contenu tient en ≈ 1 viewport → le conteneur fait ≈ 1 viewport de haut → `background-size: cover` cadre l'image à sa taille naturelle. Sur les listes portfolio / compétences (en-tête + grille 4+ cartes + footer), le conteneur atteint 2 à 3 viewports de haut → `cover` redimensionne l'image pour couvrir **toute cette hauteur**, ce qui la fait apparaître zoomée et décalée. Effet amplifié au scroll car le wallpaper défile avec la page. + +**Fix** : sortir le wallpaper du conteneur grid et le passer en `fixed inset-0 z-0 pointer-events-none`. Il est désormais calé sur le **viewport**, garde ses dimensions naturelles indépendamment de la longueur de la page, et reste stable au scroll. Les cercles animés `circle-one` / `circle-two` restent en `absolute` dans le grid pour conserver le comportement de parallax léger au scroll. + +```tsx + +``` + +**Impact transversal** : corrige au passage le même problème latent sur toutes les autres pages longues (futures fiches détail, page contact si elle s'allonge, etc.) — plus besoin d'y repenser page par page. + +### Points laissés pour l'étape 7 + +- Les composants `Carousel.tsx` et `CarouselCompetences.tsx` **ne sont pas touchés** (ils restent utilisés par les pages détail `[slug]/page.tsx`). La refonte visuelle de ces carousels (pagination, flèches, lightbox) se fera dans le lot 7 avec les fiches détail et la modale glossaire. +- Pas de filtre / tri côté liste pour l'instant (les items sont peu nombreux, `order` de Strapi suffit). À ré-évaluer si le catalogue grossit. + +### Correctif post-étape 6 — réintroduction du défilement automatique en vignette + +Premier retour utilisateur après l'étape 6 : *« j'ai perdu ma fonctionnalité précédente du carousel où les images des vignettes chargées depuis Strapi défilaient »*. L'arbitrage initial *"carousel réservé aux galeries intra-fiche"* (§ 2 — tableau d'arbitrages) était motivé par le bruit visuel et le poids réseau de **plusieurs `Swiper` autoplay** qui tournaient simultanément. Mais le défilement auto des images en vignette faisait partie intégrante de l'expérience de découverte du portfolio pour l'auteur. **L'arbitrage est donc révisé** : on conserve le défilement en vignette, mais via un composant **allégé et cadré** plutôt que le `Carousel.tsx` complet. + +**Nouveau composant `app/components/VignetteCarousel.tsx`** — différences délibérées avec `Carousel.tsx` / `CarouselCompetences.tsx` : + +- **Pas de flèches de navigation** (`Navigation` module non chargé). Les flèches créaient une **zone de clic ambiguë** avec le `` englobant la vignette : cliquer sur une flèche déclenchait la navigation vers la fiche détail au lieu de faire défiler le carousel. L'autoplay + le swipe tactile suffisent à l'échelle d'une vignette. +- **Pas de lightbox** (pas de `createPortal` ni de `selectedImage`). L'ouverture plein écran reste une signature de la **fiche détail**, pas de la liste. +- **Pagination bullets Stitch** : `--swiper-pagination-color: #26445d` (primary) et bullets inactifs blancs à 55 % d'opacité, taille 6 px. Surcharge inline via `style={...}` pour éviter de polluer `globals.css` avec un sélecteur `.swiper-pagination-bullet` global qui risquerait de toucher aussi les carousels de la fiche détail. +- **Autoplay 3500 ms** (vs 3000 ms historique) pour laisser plus de temps à la lecture sur les cartes vedette 2/3. +- **`loop` conditionnel** (`images.length > 1`) : sans ça Swiper loggait un warning quand une entrée Strapi n'avait qu'une seule image. + +**Intégration dans les listes** : dans `app/portfolio/page.jsx` et `app/competences/page.jsx`, la logique est `length > 1 ? : ` — identique à la version pré-refonte pour les entrées mono-image, plus performante pour les entrées multi-images. Les `alt` sont générés à partir de `img.name` Strapi avec fallback sur le nom du projet / compétence. + +**Pourquoi ne pas avoir réutilisé `Carousel.tsx` tel quel** : il embarque flèches + lightbox + CSS de navigation. Dans le contexte d'un `` englobant, les flèches auraient conflit, et la lightbox serait inaccessible (capturée par le lien). Ajouter des `stopPropagation` sur ces zones nuirait à l'UX "clic n'importe où sur la carte = ouverture de la fiche". Un composant dédié aux vignettes, avec moins de surface d'interaction, est plus clair à maintenir. Les deux composants `Carousel*.tsx` restent intacts pour la fiche détail (étape 7). + +Les composants `app/components/Carousel.tsx` et `app/components/CarouselCompetences.tsx` deviennent donc formellement **"carousels de fiche détail"** dans la nomenclature interne ; `VignetteCarousel` est leur petit frère "liste". Un éventuel refactor plus tard pourra fusionner les deux premiers (quasi-doublons) — hors scope actuel. + ## 5. Checklist relecture (à passer à la fin de chaque étape) - [ ] Aucune colonne unique globale `max-w-xl` (c'est le format newsletter). diff --git a/docs-site-interne/feuille-de-route.md b/docs-site-interne/feuille-de-route.md index 497069b..a75b37b 100644 --- a/docs-site-interne/feuille-de-route.md +++ b/docs-site-interne/feuille-de-route.md @@ -8,7 +8,7 @@ Document vivant : ajuster les statuts et dates au fil du travail. | ID | Sujet | Statut | Notes | |----|--------|--------|--------| -| R1 | Moderniser l’UI (design system, cohérence typo/couleurs) | En cours | Direction "Digital Atelier" inspirée de `stitch_V1/` ; cadrage et plan dans [`REFONTE-VISUELLE.md`](./REFONTE-VISUELLE.md). Étapes 1-5 (tokens + garde-fou + migration typo globale + layout racine + home) faites le 2026-04-22. | +| R1 | Moderniser l’UI (design system, cohérence typo/couleurs) | En cours | Direction "Digital Atelier" inspirée de `stitch_V1/` ; cadrage et plan dans [`REFONTE-VISUELLE.md`](./REFONTE-VISUELLE.md). Étapes 1-6 (tokens + garde-fou + migration typo globale + layout racine + home + listes portfolio/compétences) faites le 2026-04-22. | | R2 | Homogénéiser TS vs JS dans `app/` | À faire | Migrer progressivement les `.jsx`/`.js` | | R3 | Centraliser config API (Strapi + LLM) via `.env` | À faire | Remplacer URLs en dur où pertinent | | R4 | Revoir `layout.tsx` (server vs client, perf SEO) | À faire | Évaluer extraction header/footer | @@ -49,3 +49,6 @@ Document vivant : ajuster les statuts et dates au fil du travail. | 2026-04-22 | **Fix phase 1b** : font par défaut du `body` dans `app/globals.css` passée de `Arial, Helvetica, sans-serif` à `var(--font-newsreader), Georgia, serif`. Tout élément sans classe typo explicite hérite désormais de Newsreader (corps éditorial Stitch) au lieu d'Arial. **Fix phase 2 (Material Symbols)** : `@import url(...)` Material Symbols retiré de `app/globals.css` (strippé), remplacé par un triplet ` + ` injecté dans le `` de `app/layout.tsx` — contourne le pipeline PostCSS tout en conservant le CDN Google pour la font-icon (usage très ponctuel : 5 icônes sur la home, 2 sur le burger, 1 sur la modale). Validé en production : icônes + fonts OK sur Chrome desktop. | | 2026-04-22 | **Correctif Chrome Auto-Translate mobile** (retour utilisateur : layout décalé + icônes redevenues du texte littéral sur Chrome mobile quand la traduction auto s'active). Cause : les icônes Material Symbols fonctionnent via ligatures de font (`psychology` → glyphe cerveau) ; Chrome traduit le texte `psychology` en `psychologie`, la ligature ne match plus, l'icône redevient texte. Fix : `translate="no"` ajouté sur les 8 `` (5 dans `app/page.tsx`, 2 dans `app/layout.tsx`, 1 dans `app/components/ModalGlossaire.tsx`) + sur le titre header (nom propre `Portfolio Gras-Calvet Fernand`). La traduction automatique reste globalement active pour le contenu éditorial (CV, compétences, projets) — seuls les éléments que la traduction casse sont protégés. | | 2026-04-22 | **Correctif séparateurs `
` du hero** (retour utilisateur : « trop d'espace entre les sections » en fait dû à des `
` quasi invisibles générés depuis les `---` Markdown du CV Strapi). Option B retenue : surcharge `prose-hr` sur le wrapper `ReactMarkdown` (`app/page.tsx`) pour transformer la règle en pastille Stitch centrée (`prose-hr:border-0 prose-hr:w-16 prose-hr:mx-auto prose-hr:bg-primary/30 prose-hr:h-0.5 prose-hr:rounded-full prose-hr:my-6`). Le séparateur redevient un signal visuel intentionnel et cohérent avec la palette, marges verticales réduites de 32 px à 24 px. Détail dans `REFONTE-VISUELLE.md` §4 sexies. | +| 2026-04-22 | **Correctif post-étape 6 : réintroduction du défilement auto en vignette**. L'arbitrage initial "carousel retiré des listes" retiré après retour utilisateur (*« j'ai perdu ma fonctionnalité précédente »*). Nouveau composant `app/components/VignetteCarousel.tsx` : Swiper allégé (modules `Autoplay` + `Pagination` uniquement), **sans flèches** (conflit de clic avec le `` englobant) et **sans lightbox** (réservée à la fiche détail). Pagination bullets teintée `primary` via surcharge inline des variables CSS Swiper (`--swiper-pagination-color: #26445d`) pour ne pas polluer `globals.css`. Autoplay 3500 ms, `loop` conditionnel (`length > 1` pour éviter le warning Swiper sur les entrées mono-image). Intégré dans `app/portfolio/page.jsx` et `app/competences/page.jsx` via `images.length > 1 ? : ` — comportement identique à l'ancien pour les mono-image, défilement retrouvé pour les multi-image. Les composants `Carousel.tsx` et `CarouselCompetences.tsx` existants restent intacts pour la fiche détail (étape 7). Détail dans `REFONTE-VISUELLE.md` §6 *"Correctif post-étape 6 — réintroduction du défilement automatique en vignette"*. | +| 2026-04-22 | **Correctif wallpaper sur-zoomé sur pages longues** (retour utilisateur post-étape 6 : incohérence visuelle entre la home et `/portfolio`). Cause : `.bg-wallpaper` en `absolute inset-0` à l'intérieur du conteneur grid `min-h-[100dvh]` — le conteneur s'étirant à la hauteur du contenu (≈ 2-3 viewports sur les listes), `background-size: cover` zoomait l'image pour couvrir toute cette hauteur. Fix : wallpaper sorti du grid, passé en `fixed inset-0 z-0 pointer-events-none` (calé sur le viewport, plus la page entière). Les cercles animés restent en `absolute` dans le grid. Impact transversal sur toutes les pages longues (fiches détail à venir, etc.). Détail dans `REFONTE-VISUELLE.md` §6. | +| 2026-04-22 | Refonte visuelle — **étape 6 : listes portfolio + compétences**. `app/portfolio/page.jsx` et `app/competences/page.jsx` entièrement réécrits. En-tête éditorial (kicker + titre Manrope extrabold + pitch Newsreader) cohérent avec le hero de la home. Grille **asymétrique 2/3 + 1/3** alternée (`md:grid-cols-6` + pattern de `col-span-4`/`col-span-2` sur modulo 4, `sm:grid-cols-2`, `grid-cols-1` mobile) — conforme DESIGN.md §6 "No-Grid-Lock". Cartes « feuillet vellum » alignées home : `rounded-sheet bg-surface-container-lowest/85 backdrop-blur-vellum shadow-ambient`, image `aspect-[4/3]` fixe avec `group-hover:scale-[1.03]`, titre `text-primary`, description `line-clamp-3` en Newsreader, CTA tertiaire « Découvrir → » / « Explorer → » avec Material Symbol `arrow_forward` qui se décale au hover (`translate="no"` appliqué). Hover : `hover:-translate-y-0.5 hover:shadow-jewel` (remplace le `scale-105` qui débordait). **`Swiper` retiré des vignettes de liste** (arbitrage acté § 2 : carousel réservé aux galeries intra-fiche) — une seule image par carte, `loading="lazy"`. États ajoutés : skeletons animés respectant la grille + état vide avec Material Symbol. Régressions corrigées au passage : largeur fixe `w-80` qui débordait sur S25 Ultra, `hover:scale-105` qui tapait sous le header, classes `bg-white/80 rounded-lg` remplacées par les tokens Stitch. Les composants `Carousel.tsx` et `CarouselCompetences.tsx` restent en place pour les fiches détail (étape 7). Détail dans `REFONTE-VISUELLE.md` §6. |