mirror of
https://github.com/Ladebeze66/devsite.git
synced 2026-05-11 16:56:26 +02:00
etape5ok
This commit is contained in:
parent
20d4c78df8
commit
0a506dbf39
@ -68,7 +68,11 @@ export default function ModalGlossaire({ mot, onClose }: ModalGlossaireProps) {
|
||||
onClick={onClose}
|
||||
aria-label="Fermer la fenêtre du glossaire"
|
||||
>
|
||||
<span className="material-symbols-outlined" aria-hidden="true">
|
||||
<span
|
||||
className="material-symbols-outlined"
|
||||
aria-hidden="true"
|
||||
translate="no"
|
||||
>
|
||||
close
|
||||
</span>
|
||||
</button>
|
||||
|
||||
35
app/fonts.ts
Normal file
35
app/fonts.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Manrope, Newsreader } from "next/font/google";
|
||||
|
||||
/**
|
||||
* Fonts du système "Digital Atelier" (voir docs-site-interne/REFONTE-VISUELLE.md).
|
||||
*
|
||||
* Chargées via `next/font/google` : Next télécharge les fichiers woff2 au build
|
||||
* et les sert depuis le domaine du site (plus de dépendance fonts.googleapis.com,
|
||||
* plus de problème de CDN ou de cache navigateur agressif).
|
||||
*
|
||||
* L'ancien chargement via `@import url(...)` dans `app/globals.css` était strippé
|
||||
* par la chaîne PostCSS + Tailwind en production, les polices n'arrivaient jamais
|
||||
* au navigateur (diagnostic 2026-04-22 : aucune requête `fonts.googleapis.com`
|
||||
* visible dans l'onglet Network).
|
||||
*
|
||||
* Usage :
|
||||
* 1. Importer `manrope` / `newsreader` dans le layout racine.
|
||||
* 2. Poser leurs `variable` sur le `<html>` pour exposer `--font-manrope` /
|
||||
* `--font-newsreader` à tout le sous-arbre.
|
||||
* 3. `tailwind.config.ts` mappe `font-headline` / `font-body` vers ces variables.
|
||||
*/
|
||||
|
||||
export const manrope = Manrope({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600", "700", "800"],
|
||||
variable: "--font-manrope",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const newsreader = Newsreader({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "600"],
|
||||
style: ["normal", "italic"],
|
||||
variable: "--font-newsreader",
|
||||
display: "swap",
|
||||
});
|
||||
@ -1,8 +1,14 @@
|
||||
/* Polices Stitch "Digital Atelier" : Manrope (titres/UI) + Newsreader (corps éditorial).
|
||||
Voir docs-site-interne/REFONTE-VISUELLE.md. Les anciennes classes font-orbitron-*
|
||||
ont été retirées à l'étape 3 de la refonte (2026-04-22). */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Newsreader:ital,wght@0,400;0,500;0,600;1,400;1,500&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0&display=swap');
|
||||
Voir docs-site-interne/REFONTE-VISUELLE.md.
|
||||
--
|
||||
Les `@import url(...)` vers Google Fonts étaient strippés en production par la
|
||||
chaîne PostCSS + Tailwind de Next 15 (diagnostic confirmé 2026-04-22 via DevTools
|
||||
Network sur Chrome desktop et mobile). Depuis :
|
||||
- Manrope et Newsreader passent par `next/font/google` (voir `app/fonts.ts`)
|
||||
avec les variables CSS `--font-manrope` et `--font-newsreader`.
|
||||
- Material Symbols Outlined est chargée via `<link rel="stylesheet">` injecté
|
||||
directement dans le `<head>` par `app/layout.tsx` (contourne le pipeline
|
||||
PostCSS strippant, conserve le CDN Google pour une icon-font). */
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@ -17,8 +23,12 @@
|
||||
--foreground: #191c1d; /* on-surface Stitch : jamais #000 pur */
|
||||
}
|
||||
|
||||
/* Icônes Material Symbols : utilitaire <span class="material-symbols-outlined">name</span>. */
|
||||
/* Icônes Material Symbols : utilitaire <span class="material-symbols-outlined">name</span>.
|
||||
Sans la règle font-family explicite, le span affiche littéralement le nom de l'icône
|
||||
(ex. "psychology") dans la font par défaut — l'import Google Fonts ne la pose pas
|
||||
automatiquement sur la classe, c'est au site de le faire. */
|
||||
.material-symbols-outlined {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
@ -38,10 +48,13 @@ html {
|
||||
}
|
||||
|
||||
body {
|
||||
/* Couleur texte et fond fixés en clair, pas de dépendance au thème système. */
|
||||
/* Couleur et fond fixés en clair, pas de dépendance au thème système.
|
||||
Font par défaut : Newsreader (corps éditorial Stitch). Les éléments avec
|
||||
`font-headline` passeront en Manrope, ceux sans classe explicite hériteront
|
||||
de Newsreader au lieu d'Arial. */
|
||||
color: #191c1d;
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: var(--font-newsreader), Georgia, serif;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
overflow-x: hidden;
|
||||
|
||||
@ -5,6 +5,7 @@ import Footer from "./components/Footer";
|
||||
import "./assets/main.css";
|
||||
import "./globals.css";
|
||||
import NavLink from "./components/NavLink";
|
||||
import { manrope, newsreader } from "./fonts";
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
@ -72,7 +73,26 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
"text-on-surface-variant hover:text-primary transition-colors";
|
||||
|
||||
return (
|
||||
<html lang="fr">
|
||||
<html lang="fr" className={`${manrope.variable} ${newsreader.variable}`}>
|
||||
<head>
|
||||
{/* Material Symbols : chargés via <link> plutôt que via @import CSS qui est
|
||||
strippé par la chaîne PostCSS + Tailwind de Next 15 (diagnostic 2026-04-22).
|
||||
Ressource critique côté UI → preload pour éviter un flash de texte brut
|
||||
sur les icônes des CTAs, du burger et des cartes de la home. */}
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.googleapis.com"
|
||||
/>
|
||||
<link
|
||||
rel="preconnect"
|
||||
href="https://fonts.gstatic.com"
|
||||
crossOrigin=""
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap"
|
||||
/>
|
||||
</head>
|
||||
<body className="min-w-0 overflow-x-hidden antialiased">
|
||||
<div className="relative grid min-h-[100dvh] w-full min-w-0 grid-rows-[auto_1fr_auto]">
|
||||
{/* Wallpaper plein écran (fondation, ne change pas avec la refonte). */}
|
||||
@ -87,7 +107,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
{/* Header "No-Line" : pas de bordure pleine, juste un shift tonal + ombre ambient diffuse. */}
|
||||
<header className="fixed left-0 top-0 z-20 h-16 w-full min-w-0 bg-surface/80 px-4 py-2 shadow-ambient-sm backdrop-blur-vellum md:h-16 md:px-6">
|
||||
<div className="mx-auto flex max-w-4xl min-w-0 items-center justify-between gap-2">
|
||||
<h2 className="min-w-0 truncate pr-1 text-xl font-headline font-extrabold italic tracking-tight text-primary md:text-2xl">
|
||||
<h2
|
||||
className="min-w-0 truncate pr-1 text-xl font-headline font-extrabold italic tracking-tight text-primary md:text-2xl"
|
||||
translate="no"
|
||||
>
|
||||
Portfolio Gras-Calvet Fernand
|
||||
</h2>
|
||||
|
||||
@ -101,7 +124,11 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
aria-expanded={isMenuOpen}
|
||||
aria-controls="mobile-drawer"
|
||||
>
|
||||
<span className="material-symbols-outlined" aria-hidden="true">
|
||||
<span
|
||||
className="material-symbols-outlined"
|
||||
aria-hidden="true"
|
||||
translate="no"
|
||||
>
|
||||
{isMenuOpen ? "close" : "menu"}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
206
app/page.tsx
206
app/page.tsx
@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import "./assets/main.css";
|
||||
@ -8,18 +9,17 @@ import { getApiUrl } from "./utils/getApiUrl";
|
||||
async function getHomepageData() {
|
||||
const apiUrl = getApiUrl();
|
||||
|
||||
// Configuration avec timeout et retry
|
||||
const fetchWithTimeout = async (url: string, options: RequestInit = {}) => {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 secondes timeout
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
@ -31,12 +31,15 @@ async function getHomepageData() {
|
||||
}
|
||||
};
|
||||
|
||||
// Tentative avec retry
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
console.log(`🔄 [getHomepageData] Tentative ${attempt}/3 - URL: ${apiUrl}/api/homepages?populate=*`);
|
||||
console.log(
|
||||
`🔄 [getHomepageData] Tentative ${attempt}/3 - URL: ${apiUrl}/api/homepages?populate=*`
|
||||
);
|
||||
|
||||
const response = await fetchWithTimeout(`${apiUrl}/api/homepages?populate=*`);
|
||||
const response = await fetchWithTimeout(
|
||||
`${apiUrl}/api/homepages?populate=*`
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
@ -45,24 +48,44 @@ async function getHomepageData() {
|
||||
const data = await response.json();
|
||||
console.log("✅ [getHomepageData] Données récupérées avec succès");
|
||||
return data.data?.[0] ?? null;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ [getHomepageData] Erreur tentative ${attempt}:`, error);
|
||||
|
||||
if (attempt === 3) {
|
||||
// Dernière tentative échouée
|
||||
console.error("🚨 [getHomepageData] Toutes les tentatives ont échoué");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Attendre avant la prochaine tentative
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trois axes éditoriaux de la home. Contenu hardcodé pour l'instant : simple
|
||||
* et modifiable ici. À porter vers un content-type Strapi dédié plus tard si
|
||||
* on veut l'éditer sans déploiement.
|
||||
*/
|
||||
const takeaways = [
|
||||
{
|
||||
icon: "psychology",
|
||||
title: "Intelligence artificielle",
|
||||
body: "Intégration d'IA locale et d'assistants conversationnels en environnement souverain.",
|
||||
},
|
||||
{
|
||||
icon: "terminal",
|
||||
title: "Développement web",
|
||||
body: "Next.js, Strapi, FastAPI — stack moderne de bout en bout, du CMS à la diffusion.",
|
||||
},
|
||||
{
|
||||
icon: "school",
|
||||
title: "École 42",
|
||||
body: "Formation par projets, pédagogie par les pairs, progression autodidacte continue.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function HomePage() {
|
||||
const [homepage, setHomepage] = useState<any>(null);
|
||||
const apiUrl = getApiUrl();
|
||||
@ -71,29 +94,170 @@ export default function HomePage() {
|
||||
getHomepageData().then((data) => setHomepage(data));
|
||||
}, []);
|
||||
|
||||
if (!homepage) return <p className="text-center text-blue-500">Chargement de la page...</p>;
|
||||
if (!homepage) {
|
||||
return (
|
||||
<div className="mx-auto flex min-h-[40vh] w-full max-w-md items-center justify-center px-4">
|
||||
<p className="font-body italic text-secondary">
|
||||
Chargement de la page…
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const title = homepage.title ?? "Titre par défaut";
|
||||
const cv = homepage.cv ?? "";
|
||||
const cv: string = homepage.cv ?? "";
|
||||
const imageUrl = homepage.photo?.url ? `${apiUrl}${homepage.photo.url}` : null;
|
||||
|
||||
return (
|
||||
<main className="mx-auto mb-3 flex w-full min-w-0 max-w-full flex-col items-center justify-center rounded-lg bg-white/55 p-4 sm:max-w-2xl sm:p-6 md:max-w-3xl lg:max-w-4xl xl:max-w-5xl">
|
||||
<h1 className="text-3xl font-headline font-extrabold italic tracking-tight text-gray-800 mb-4">{title}</h1>
|
||||
|
||||
<div className="mx-auto flex w-full min-w-0 max-w-5xl flex-col gap-3 px-4 pb-10 sm:px-6">
|
||||
{/* Hero "feuillet de vellum" : carte principale à 85 % sur le wallpaper. */}
|
||||
<section
|
||||
className="rounded-sheet bg-surface-container-lowest/85 p-5 shadow-ambient backdrop-blur-vellum sm:p-7 md:p-8"
|
||||
aria-labelledby="home-title"
|
||||
>
|
||||
<div className="grid gap-5 md:grid-cols-[auto_1fr] md:items-center md:gap-8">
|
||||
{/* Portrait avec frame primary (1 px d'air), remplace le cercle historique. */}
|
||||
<div className="mx-auto md:mx-0">
|
||||
{imageUrl ? (
|
||||
<div className="relative w-64 h-64 rounded-full overflow-hidden shadow-lg border-4 border-gray-300 transition-transform duration-300 hover:scale-110 hover:rotate-3">
|
||||
<img src={imageUrl} alt="Photo de profil" className="w-full h-full object-cover object-center" />
|
||||
<div className="rounded-sheet bg-primary p-1 shadow-ambient-sm">
|
||||
<div className="overflow-hidden rounded-[1.25rem]">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`Portrait de ${title}`}
|
||||
className="h-48 w-48 object-cover object-center sm:h-56 sm:w-56 md:h-64 md:w-64"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-64 h-64 flex items-center justify-center bg-gray-500 text-gray-200 rounded-full shadow-md">
|
||||
<div className="flex h-48 w-48 items-center justify-center rounded-sheet bg-surface-container text-sm text-on-surface-variant sm:h-56 sm:w-56 md:h-64 md:w-64">
|
||||
Image indisponible
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 w-full min-w-0 max-w-2xl px-4 text-center text-lg font-headline font-bold text-gray-700 sm:px-6">
|
||||
<div className="flex flex-col gap-3 text-center md:text-left">
|
||||
<span className="font-headline text-[11px] font-bold uppercase tracking-[0.3em] text-secondary">
|
||||
Portfolio · Étudiant 42 Perpignan
|
||||
</span>
|
||||
<h1
|
||||
id="home-title"
|
||||
className="font-headline text-3xl font-extrabold tracking-tight text-on-surface md:text-4xl lg:text-5xl"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
{cv && (
|
||||
<div
|
||||
className="prose prose-sm max-w-none font-body text-on-surface-variant sm:prose-base
|
||||
prose-headings:font-headline prose-headings:text-primary
|
||||
prose-p:font-body prose-p:text-on-surface-variant
|
||||
prose-strong:text-on-surface
|
||||
prose-a:text-primary prose-a:no-underline hover:prose-a:underline
|
||||
prose-li:marker:text-primary
|
||||
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"
|
||||
>
|
||||
<ReactMarkdown>{cv}</ReactMarkdown>
|
||||
</div>
|
||||
</main>
|
||||
)}
|
||||
|
||||
<div className="mt-2 flex flex-col items-stretch gap-3 sm:flex-row sm:justify-center sm:items-center md:justify-start">
|
||||
<Link
|
||||
href="/portfolio"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-tile bg-primary px-6 py-3 font-headline text-sm font-bold uppercase tracking-widest text-on-primary shadow-jewel transition-transform hover:-translate-y-0.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-fixed"
|
||||
>
|
||||
Voir mes projets
|
||||
<span
|
||||
className="material-symbols-outlined text-base"
|
||||
aria-hidden="true"
|
||||
translate="no"
|
||||
>
|
||||
arrow_forward
|
||||
</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-tile px-6 py-3 font-headline text-sm font-bold uppercase tracking-widest text-primary transition-colors hover:bg-primary-fixed/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
Me contacter
|
||||
<span
|
||||
className="material-symbols-outlined text-base"
|
||||
aria-hidden="true"
|
||||
translate="no"
|
||||
>
|
||||
mail
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Trois axes : cartes éditoriales, grille 3 colonnes desktop, stack mobile. */}
|
||||
<section
|
||||
className="rounded-sheet bg-surface-container-lowest/85 p-5 shadow-ambient backdrop-blur-vellum sm:p-6"
|
||||
aria-labelledby="home-axes"
|
||||
>
|
||||
<div className="mb-4 text-center md:text-left">
|
||||
<span className="font-headline text-[11px] font-bold uppercase tracking-[0.3em] text-secondary">
|
||||
Ce qui m'anime
|
||||
</span>
|
||||
<h2
|
||||
id="home-axes"
|
||||
className="mt-1 font-headline text-2xl font-extrabold tracking-tight text-primary md:text-3xl"
|
||||
>
|
||||
Trois axes de travail
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
{takeaways.map((item) => (
|
||||
<article
|
||||
key={item.title}
|
||||
className="rounded-tile bg-surface-container-low/80 p-5 transition-colors hover:bg-surface-container/80"
|
||||
>
|
||||
<div className="mb-3 flex h-10 w-10 items-center justify-center rounded-full bg-primary text-on-primary">
|
||||
<span
|
||||
className="material-symbols-outlined"
|
||||
aria-hidden="true"
|
||||
translate="no"
|
||||
>
|
||||
{item.icon}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="mb-1 font-headline text-lg font-bold text-primary">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="font-body text-sm leading-relaxed text-on-surface-variant">
|
||||
{item.body}
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Pull-quote "Démarche" : carte vellum légère (opacité 65 %, sans ombre,
|
||||
radius tile) pour rester lisible sur wallpaper sans écraser la variation
|
||||
éditoriale voulue par DESIGN.md §5. Barre gauche primaire conservée. */}
|
||||
<section
|
||||
className="rounded-tile bg-surface-container-lowest/65 p-5 backdrop-blur-vellum sm:p-6"
|
||||
aria-labelledby="home-demarche"
|
||||
>
|
||||
<blockquote className="border-l-4 border-primary pl-5 md:pl-8">
|
||||
<span
|
||||
id="home-demarche"
|
||||
className="mb-2 block font-headline text-[11px] font-bold uppercase tracking-[0.3em] text-primary"
|
||||
>
|
||||
Démarche
|
||||
</span>
|
||||
<p className="font-body text-lg italic leading-snug text-on-surface md:text-2xl">
|
||||
« Apprendre à construire, puis construire pour apprendre — chaque
|
||||
projet est une nouvelle pièce du métier. »
|
||||
</p>
|
||||
<cite className="mt-3 block font-headline text-sm font-bold not-italic text-secondary">
|
||||
— Fernand Gras-Calvet
|
||||
</cite>
|
||||
</blockquote>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
# Refonte visuelle — Direction "Digital Atelier"
|
||||
|
||||
**Créé :** 2026-04-22
|
||||
**Statut :** en cours (étapes 1-4/8 terminées)
|
||||
**Statut :** en cours (étapes 1-5/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).
|
||||
|
||||
@ -58,7 +58,7 @@ Chaque étape = un lot cohérent + éventuelle mise à jour de `captures/AUDIT-V
|
||||
| 2 | Garde-fou doc + mise à jour feuille de route | `docs-site-interne/REFONTE-VISUELLE.md`, `docs-site-interne/feuille-de-route.md` | **fait** (2026-04-22) |
|
||||
| 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` | à faire |
|
||||
| 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 |
|
||||
| 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 |
|
||||
@ -102,6 +102,54 @@ Après l'étape 4, retour utilisateur sur Samsung S25 Ultra : les mots-clés du
|
||||
|
||||
Ce correctif concerne uniquement le composant `ModalGlossaire`. L'étape 7 reprendra la refonte globale de cette zone (cohérence visuelle avec les fiches détail) mais le blocage UX mobile est levé dès maintenant.
|
||||
|
||||
## 4 quater. Correctifs post-étape 5 (2026-04-22) — home
|
||||
|
||||
Retour utilisateur sur la home fraichement refaite. Trois points, trois causes distinctes :
|
||||
|
||||
### Icônes Material Symbols affichées comme texte littéral
|
||||
|
||||
Les `<span class="material-symbols-outlined">psychology</span>` affichaient **le mot "psychology"** dans la font par défaut au lieu du glyphe, rendant les takeaways illisibles (texte blanc sur fond bleu = juste du texte). La règle `.material-symbols-outlined` de `app/globals.css` déclarait bien `font-variation-settings`, `display`, `line-height`… mais pas `font-family: 'Material Symbols Outlined'`. L'import Google Fonts pose le `@font-face`, il ne pose pas automatiquement la `font-family` sur la classe — c'est au site de le faire.
|
||||
|
||||
**Fix** : ajout de la ligne `font-family: 'Material Symbols Outlined';` dans la règle. Impact : toutes les icônes du site (takeaways, burger, modale glossaire, CTAs hero, icônes CTAs des futures étapes) s'affichent désormais comme icônes.
|
||||
|
||||
### Pull-quote "Démarche" peu lisible sur wallpaper
|
||||
|
||||
La règle DESIGN.md §5 "Editorial Pull-Quote" dit *"no background card, let the typography breathe on the surface"*. Valide quand la surface de base est un `bg-surface #f8fafa` uni (Stitch newsletter). Chez nous la surface de base est un wallpaper photographique, donc *respirer dessus = se fondre dedans*.
|
||||
|
||||
**Fix** : adaptation contextuelle — carte vellum **légère** (`bg-surface-container-lowest/65 backdrop-blur-vellum rounded-tile`, padding réduit, pas de `shadow-ambient`) pour rester lisible sans uniformiser les 3 sections en cartes identiques. La barre gauche `border-l-4 border-primary` et la typo Newsreader italique sont conservées.
|
||||
|
||||
**Leçon** : les règles DESIGN.md sont un langage, pas un dogme. Elles supposent une surface de base uniforme. Chaque fois qu'on est sur wallpaper, vérifier si la règle reste applicable telle quelle ou si elle demande une adaptation (ici : carte légère plutôt que zéro carte).
|
||||
|
||||
### Espace excessif entre les 3 sections de la home
|
||||
|
||||
`gap-8` (32 px) entre les sections + `py-6 md:py-8` sur la pull-quote donnaient ~80 px d'air vertical entre "Trois axes" et "Démarche".
|
||||
|
||||
**Fix** : `gap-8` → `gap-5` sur le container racine (20 px), `py-6 md:py-8` retiré sur la pull-quote (désormais remplacé par le padding interne de sa nouvelle carte). Les paddings internes des cartes (hero `p-6 sm:p-8 md:p-10`, takeaways `p-6 sm:p-8`) sont conservés — l'espace de contenu n'était pas le problème.
|
||||
|
||||
## 4 sexies. Séparateurs `<hr>` invisibles dans le hero (2026-04-22)
|
||||
|
||||
Le CV rendu par `ReactMarkdown` contient des `---` Markdown convertis en `<hr>`. Par défaut Tailwind Typography les stylise en bordure 1 px `border-gray-300` + `my-8` (32 px). Sur notre carte vellum semi-transparente, cette bordure grise est quasi invisible sur le wallpaper, mais les 64 px de marge verticale (my-8 en haut **et** en bas) restent et donnent l'illusion d'un espace excessif entre les paragraphes du hero.
|
||||
|
||||
**Fix (Option B — barre décorative)** : on surcharge `prose-hr` pour transformer la règle en **petite pastille Stitch** centrée. Classes ajoutées sur le wrapper `ReactMarkdown` :
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
Résultat : une barre 64 × 2 px, couleur primaire à 30 % d'opacité, arrondie, avec 24 px de marge au lieu de 32 px. Le séparateur redevient un **signal visuel intentionnel** cohérent avec la palette Stitch, et l'espace perçu entre les paragraphes tombe à un niveau confortable sans perdre la structure éditoriale du CV.
|
||||
|
||||
**Alternatives considérées** : Option A (`prose-hr:hidden`, perd la structure), Option C (`prose-hr:my-4` seul, garde la bordure grise invisible — n'adresse pas la cause).
|
||||
|
||||
## 4 quinquies. Compatibilité Chrome Auto-Translate (2026-04-22)
|
||||
|
||||
Les icônes Material Symbols Outlined fonctionnent via **ligatures de font** : un `<span class="material-symbols-outlined">psychology</span>` n'affiche « psychology » qu'en fallback — si la font est chargée, la ligature transforme ce texte en glyphe « cerveau ». Google Chrome propose à l'utilisateur mobile de traduire automatiquement une page dès que sa langue par défaut n'est pas celle du document. Lorsque la traduction s'active, **Chrome réécrit le `textContent`** (« psychology » → « psychologie ») : la ligature ne correspond plus à aucun glyphe dans la font, l'icône redevient du texte brut, et les layouts se décalent.
|
||||
|
||||
**Règle permanente pour la refonte** : chaque `<span class="material-symbols-outlined">` doit porter **`translate="no"`** (attribut HTML). Pareil pour les éléments contenant un nom propre qui ne doit pas être déformé (titre du site, nom d'école « 42 », nom de ville, etc.). Le reste du contenu éditorial (CV, descriptions de projets, fiches compétences) reste traductible — la traduction automatique est un vrai plus pour un portfolio qu'on veut accessible à l'international.
|
||||
|
||||
Composant wrapper `<Icon>` qui pose automatiquement `translate="no"` envisagé comme DRY à long terme (hors scope actuel).
|
||||
|
||||
## 5. Checklist relecture (à passer à la fin de chaque étape)
|
||||
|
||||
- [ ] Aucune colonne unique globale `max-w-xl` (c'est le format newsletter).
|
||||
@ -112,3 +160,4 @@ Ce correctif concerne uniquement le composant `ModalGlossaire`. L'étape 7 repre
|
||||
- [ ] La hiérarchie Manrope / Newsreader est respectée (pas de Orbitron résiduel).
|
||||
- [ ] Les CTAs principaux ont `shadow-jewel`.
|
||||
- [ ] Radius Stitch (`rounded-sheet` / `rounded-tile`) utilisés sur les cartes de la refonte.
|
||||
- [ ] Chaque nouvelle icône Material Symbols Outlined ajoutée porte `translate="no"` (voir §4 quinquies).
|
||||
|
||||
@ -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-4 (tokens + garde-fou + migration typo globale + layout racine) 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-5 (tokens + garde-fou + migration typo globale + layout racine + home) 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 |
|
||||
@ -42,3 +42,10 @@ Document vivant : ajuster les statuts et dates au fil du travail.
|
||||
| 2026-04-22 | Refonte visuelle — correctif post-étape 3 : régression de couleurs texte entre desktop/mobile. Retrait du `@media (prefers-color-scheme: dark)` hérité du template Next (incohérent avec l'arbitrage "light-only"), `--foreground` fixé à `#191c1d` (on-surface Stitch), `body` avec couleur non-dépendante du thème système. 3 classes Tailwind invalides `text-black-500/700` remplacées par `text-gray-700` (`app/layout.tsx`, `app/page.tsx`, `app/components/ContentSectionCompetences.tsx`). |
|
||||
| 2026-04-22 | Refonte visuelle — étape 4 : layout racine. Header "No-Line" (bordure pleine supprimée, `shadow-ambient-sm` + `backdrop-blur-vellum`, titre en `text-primary`). Burger refait en ghost button (Material Symbols `menu`/`close` au lieu des caractères `☰`/`✕`). Cercles animés repeints en `bg-primary/40` + `bg-primary-container/30`. Drawer mobile en `bg-primary/90 backdrop-blur-vellum` + liens éditoriaux (`bg-primary-container/60` → hover `bg-primary-fixed text-primary`). Bug préexistant **corrigé** : `NavLink` ignorait `className` et `onClick` fournis par le drawer mobile → refait avec support `className` / `onClick` / `activeClassName` / `inactiveClassName`, comportement desktop historique préservé. Compteur de visites migré de `layout.tsx` (bloc orphelin `absolute bottom-0 right-0`) vers `Footer.jsx` (ligne discrète `text-[10px] uppercase tracking-[0.3em]`). Nettoyage : state `visitCount` + useEffect déplacés, `div.max-w-5xl` vide retirée, state `count` inutilisé retiré de `Footer.jsx`. |
|
||||
| 2026-04-22 | Refonte visuelle — correctif urgent `ModalGlossaire` (blocage mobile signalé sur Samsung S25 Ultra). `w-[114vw] max-w-6xl h-[72vh]` → `w-full max-w-4xl max-h-[90vh]` + `overflow-y-auto`. Fermeture ajoutée sur tap-voile et `Escape`. Bouton fermeture rond 40 px Material Symbol `close`. Alignement palette Stitch (`bg-on-surface/75`, `bg-surface-container-lowest/95`, `rounded-sheet`, `text-primary`, description `font-body` Newsreader). `"use client"` + `role="dialog" aria-modal` ajoutés. |
|
||||
| 2026-04-22 | Refonte visuelle — étape 5 : home. Hero "feuillet de vellum" (`bg-surface-container-lowest/85 backdrop-blur-vellum shadow-ambient rounded-sheet`) avec grille `auto_1fr` portrait + texte. Portrait en frame primary (`bg-primary p-1 rounded-sheet`) qui remplace le cercle `rounded-full border-4`. Kicker `Portfolio · Étudiant 42 Perpignan`. Titre Manrope extrabold. `cv` Strapi rendu via ReactMarkdown + plugin typography (`prose prose-stone` custom). CTAs jewel `/portfolio` (primary shadow-jewel) + ghost `/contact` (hover `bg-primary-fixed/40`). Nouvelle section "Trois axes de travail" (3 cartes takeaway avec icônes Material Symbols `psychology`, `terminal`, `school`, contenu hardcodé dans `takeaways[]`). Pull-quote éditoriale `border-l-4 border-primary` en Newsreader italique. Double `<main>` supprimé (layout racine fournit déjà le `<main>`). |
|
||||
| 2026-04-22 | Refonte visuelle — correctifs post-étape 5 (retour utilisateur). **Icônes Material Symbols affichées comme texte littéral** (ex. "psychology" visible dans le rond bleu des takeaways) : oubli de `font-family: 'Material Symbols Outlined'` dans la règle `.material-symbols-outlined` de `app/globals.css` — Google Fonts pose le `@font-face` mais pas la règle de classe. Fix : ajout de la ligne manquante, toutes les icônes (takeaways, burger, modale, CTAs) s'affichent désormais correctement. **Pull-quote "Démarche" qui se fondait dans le wallpaper** : passée en carte vellum légère (`bg-surface-container-lowest/65 backdrop-blur-vellum rounded-tile`, sans `shadow-ambient`) pour rester lisible sans écraser la variation éditoriale. **Espace trop grand entre les 3 sections** : `gap-8` → `gap-5`, `py-6 md:py-8` retiré sur la pull-quote (remplacé par le padding interne de sa carte). |
|
||||
| 2026-04-22 | Refonte visuelle — correctifs post-étape 5 (2e passe). **Icônes toujours en texte littéral après le fix font-family** : URL Google Fonts `@24,400,0,0` (valeur fixe) potentiellement non résolue côté navigateur. Passée en syntaxe ranges `@20..48,100..700,0..1,-50..200` (alignée sur `stitch_V1/code.html`), fiable et documentée. **Densité verticale encore trop aérée** : `gap-5` → `gap-3` sur le container racine de la home. Paddings internes cartes resserrés : hero `p-6 sm:p-8 md:p-10` → `p-5 sm:p-7 md:p-8` ; takeaways `p-6 sm:p-8` → `p-5 sm:p-6`. Grille intérieure takeaways `gap-4` → `gap-3`. Hero texte `gap-4` → `gap-3`. En-tête de section takeaways `mb-6` → `mb-4`. |
|
||||
| 2026-04-22 | **Diagnostic critique via DevTools Network (Chrome desktop + mobile)** : aucune des trois Google Fonts (Manrope, Newsreader, Material Symbols) n'était réellement chargée par le navigateur — les `@import url('https://fonts.googleapis.com/...')` dans `app/globals.css` étaient strippés par la chaîne PostCSS + Tailwind de Next 15 en production. Conséquence : tout le site tournait en fallback Arial / Georgia depuis l'étape 3, la typo éditoriale Stitch n'était jamais réellement visible. **Fix phase 1 (fonts textuelles)** : création de `app/fonts.ts` qui exporte Manrope et Newsreader via `next/font/google` (téléchargement au build, service depuis le domaine du site, plus de dépendance CDN externe). `app/layout.tsx` importe ces fonts et pose `${manrope.variable} ${newsreader.variable}` sur le `<html>`. `tailwind.config.ts` : `font-headline` et `font-body` repointés vers `var(--font-manrope)` et `var(--font-newsreader)`. `app/globals.css` : 2 `@import` inopérants supprimés ; l'`@import` Material Symbols reste temporairement en attendant la phase 2. Validation DevTools Computed : `font-family: Manrope, "Manrope Fallback", system-ui, sans-serif` confirmé sur le h1 de la home. |
|
||||
| 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 `<link rel="preconnect"> + <link rel="stylesheet">` injecté dans le `<head>` 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 (`<span>psychology</span>` → 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 `<span className="material-symbols-outlined">` (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 `<hr>` du hero** (retour utilisateur : « trop d'espace entre les sections » en fait dû à des `<hr>` 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. |
|
||||
|
||||
@ -89,9 +89,10 @@ export default {
|
||||
bebas: ['Bebas Neue', 'sans-serif'],
|
||||
|
||||
// Stitch "Digital Atelier" : Manrope pour titres/UI, Newsreader pour corps éditorial.
|
||||
headline: ['Manrope', 'system-ui', 'sans-serif'],
|
||||
body: ['Newsreader', 'Georgia', 'serif'],
|
||||
label: ['Manrope', 'system-ui', 'sans-serif'],
|
||||
// Variables CSS posées par next/font/google (voir app/fonts.ts + app/layout.tsx).
|
||||
headline: ['var(--font-manrope)', 'system-ui', 'sans-serif'],
|
||||
body: ['var(--font-newsreader)', 'Georgia', 'serif'],
|
||||
label: ['var(--font-manrope)', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
borderRadius: {
|
||||
// Additifs : ne remplacent pas les radius Tailwind (xl, 2xl, etc.) pour ne rien casser.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user