Passe 3 DA : mode sombre + finition "premium type iPhone"

Vrai mode sombre via prefers-color-scheme (palette repensée, pas une
inversion — fond brun quasi-noir pour rester dans la famille chaude de la
marque, contrastes vérifiés WCAG AA). Répond aux deux demandes de Kévin en
une implémentation : mode sombre réel + rendu moins agressif en usage
réel (soirée en intérieur tamisé). Appliqué à la page upload, la galerie
admin et le générateur QR ; le carton imprimable reste volontairement en
clair uniquement (pas de mode sombre pertinent pour un PDF imprimé).

Finition : easing dédié aux transitions d'état (séparé du rebond ludique
existant), halo de couleur en fond de page, hover desktop enrichi,
focus ring teinté, entrées en fondu au chargement, modale de bienvenue
peaufinée.

Brainstorm documenté (voir rapport de tâche) : palette/typo confirmées
plutôt qu'étendues avec une 4e couleur ou une police supplémentaire, pour
rester fidèle à l'identité perso de Kévin plutôt que de la diluer.
This commit is contained in:
2026-09-16 16:10:23 +02:00
parent 45e0bd7bcc
commit 6d6be3ef99
5 changed files with 428 additions and 19 deletions
+6 -2
View File
@@ -12,7 +12,11 @@
lors du déploiement, hors scope de ce fichier front.
-->
<meta name="robots" content="noindex, nofollow">
<meta name="theme-color" content="#faf6f0">
<!-- Deux meta theme-color (au lieu d'une seule) : teinte la barre
navigateur/PWA selon le thème système, en cohérence avec le mode sombre
ajouté passe 3 (voir src/e/style.css). -->
<meta name="theme-color" content="#faf6f0" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#1b120b" media="(prefers-color-scheme: dark)">
<link rel="preload" href="/vendor/fonts/fraunces-latin-600-normal.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="/e/style.css">
</head>
@@ -96,7 +100,7 @@
<section id="success-block" class="state-block state-block--success" hidden role="status">
<div class="confetti" aria-hidden="true">
<span></span><span></span><span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
<div class="success-check" aria-hidden="true">
<svg viewBox="0 0 52 52" fill="none" focusable="false">
+261 -8
View File
@@ -22,7 +22,32 @@
un peu "pop") en plus de la pile système pour les titres/moments clés
— auto-hébergée en .woff2, 2 graisses (~41 Ko au total), voir
src/vendor/fonts/ et src/vendor/NOTICE.md. Le corps de texte reste
100% système (poids nul). */
100% système (poids nul).
Passe 3, 2026-09-16 — retour Kévin : "top ! rajoute des animations, de la
couleur [...] brainstorm sur le choix des couleurs et de la typo, ajoute
un mode sombre [...] fait moi quelque chose de vendeur et de friendly
(type iPhone)." Palette/typo déjà validées passe 2 : le brainstorm de
cette passe (voir rapport de passe pour le détail des 2 directions
alternatives écartées) a confirmé la famille corail/prune/doré + Fraunces
plutôt que d'ajouter de nouvelles teintes ou une police supplémentaire —
"plus de couleur" est obtenu en réutilisant plus largement cette même
palette (halo radial en fond, focus ring teinté, confettis étoffés)
plutôt qu'en important une 4e couleur qui romprait l'identité perso de
Kévin. Traduction concrète de cette passe :
- Mode sombre réel via prefers-color-scheme (pas d'inversion brute) :
fond brun quasi-noir (jamais bleu-nuit, pour rester dans la famille
chaude de la marque), texte/accents reclaircis et vérifiés au ratio de
contraste (WCAG AA, cf. rapport de passe pour le détail des calculs).
- Niveau de finition "type iPhone" au sens exécution, pas esthétique iOS
: pas de glassmorphism/blur ni de police système Apple. Concrètement :
easing dédié aux transitions d'état (--ease-standard, plus posé que le
--ease-bounce déjà en place et réservé aux pops/rebonds), halo de
couleur subtil en arrière-plan de page, focus ring teinté sur les
champs, micro-interactions de hover en plus du :active existant
(uniquement sous @media (hover: hover), donc jamais sur mobile tactile
— l'invité ne voit que les états focus/active déjà en place), entrées
en fondu/translation des sections principales au chargement. */
@font-face {
font-family: "Fraunces";
@@ -45,7 +70,13 @@
}
:root {
color-scheme: light;
/* "light dark" (et non "light" seul) : indique au navigateur que les deux
thèmes sont pris en charge, pour que les contrôles natifs (scrollbar,
éventuels <select>) s'adaptent aussi, pas seulement nos propres
couleurs. Les valeurs ci-dessous sont le thème clair ; le thème sombre
est redéfini intégralement dans le bloc @media (prefers-color-scheme:
dark) plus bas — jamais un simple filtre d'inversion. */
color-scheme: light dark;
/* Couleurs — famille chaude toujours ancrée dans l'identité de Kévin,
mais poussée en saturation pour un rendu festif plutôt que sobre. */
@@ -75,6 +106,14 @@
--color-limit-bg: #fbe9d8;
--color-limit-text: #8a4a10;
/* Bouton "danger" (Réessayer) : variable dédiée plutôt que de réutiliser
--color-error-text tel quel comme fond de bouton — nécessaire en thème
sombre où le texte d'erreur s'éclaircit (pour rester lisible sur fond
sombre) et ne fonctionne plus comme couleur de fond de bouton avec du
texte blanc (contraste vérifié, voir rapport de passe). */
--color-danger: #a3341c;
--color-danger-contrast: #fff;
--radius-sm: 12px;
--radius: 20px;
--radius-pill: 999px;
@@ -83,12 +122,81 @@
--shadow-md: 0 10px 28px rgba(163, 78, 30, 0.16);
--ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
/* Easing "posé" pour les transitions d'état/hover (fondus, lifts) — le
rebond de --ease-bounce reste réservé aux moments ludiques (pop-in,
coche de succès) pour ne pas le diluer. */
--ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
--font-display: "Fraunces", Georgia, "Times New Roman", serif;
}
/* -----------------------------------------------------------------------
Thème sombre — palette pensée pour l'obscurité (pas une inversion des
couleurs claires) : fond brun quasi-noir pour rester dans la famille
chaude de la marque plutôt qu'un noir bleuté générique, texte et accents
reclaircis et vérifiés au ratio de contraste WCAG AA (>= 4.5:1 pour le
texte courant, >= 3:1 pour le grand texte/les éléments d'UI ; calculs
détaillés dans le rapport de passe). Répond aux deux demandes de Kévin à
la fois : un vrai mode sombre ET un rendu "moins agressif" en usage réel
(salle tamisée en soirée). ----------------------------------------- */
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #1b120b;
--color-surface: #26190f;
--color-surface-muted: #32211a;
--color-text: #f7ead9;
--color-muted: #b39d84;
--color-border: #40291a;
--color-accent: #ff7a42;
--color-accent-hover: #ffa06e;
--color-accent-light: #ffab7a;
--color-accent-soft: rgba(255, 122, 66, 0.18);
--color-accent-contrast: #241206;
--color-plum: #f0b8d8;
--color-plum-soft: rgba(240, 184, 216, 0.14);
--color-gold: #ffcf6b;
--color-gold-soft: #3a2a12;
--color-error-bg: #3a1712;
--color-error-text: #ff9b86;
--color-success-bg: #2e2110;
--color-success-text: #f2d9b8;
--color-limit-bg: #3a2a12;
--color-limit-text: #ffd98a;
--color-danger: #ff6b52;
--color-danger-contrast: #2b0f08;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.45);
--shadow-md: 0 14px 36px rgba(0, 0, 0, 0.55);
}
/* Fonds d'icônes état erreur/limite : calculés à partir des couleurs
claires d'origine, non pilotables via une seule variable — surchargés
ici pour rester cohérents en sombre plutôt que de laisser un rouge/or
"clair" plaqué sur fond sombre. */
.state-block--error .state-icon--error {
background: rgba(255, 155, 134, 0.16);
}
.state-block--limit .state-icon--limit {
background: rgba(255, 217, 138, 0.18);
}
.welcome-modal {
background: rgba(0, 0, 0, 0.6);
}
body::before {
opacity: 0.4;
}
}
* {
box-sizing: border-box;
}
@@ -98,6 +206,7 @@ html {
}
body {
position: relative;
margin: 0;
font-family: var(--font-sans);
background: var(--color-bg);
@@ -105,6 +214,24 @@ body {
line-height: 1.45;
}
/* Halo de couleur en arrière-plan — "plus de couleur" sans ajouter de
teinte à la palette : deux dégradés radiaux réutilisant les couleurs
d'accent déjà en place (corail + doré), très doux, purement décoratifs.
Objectif "type iPhone" : une page peut être vivante sans être plate,
sans pour autant recourir à un vrai fond image (poids nul, CSS pur).
z-index négatif + fixed : reste derrière le contenu, ne capte aucun
événement (pointer-events: none), ne bouge pas au scroll. */
body::before {
content: "";
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
background: radial-gradient(60% 38% at 50% -8%, var(--color-accent-soft) 0%, transparent 72%),
radial-gradient(46% 30% at 100% 100%, var(--color-gold-soft) 0%, transparent 70%);
opacity: 0.7;
}
.page {
max-width: 480px;
margin: 0 auto;
@@ -115,6 +242,22 @@ body {
.page__header {
text-align: center;
margin-bottom: 1.6rem;
/* Entrée en fondu/translation au chargement — discrète, une seule fois
par affichage (rejoue si la page est masquée/réaffichée via l'attribut
hidden, ce qui reste cohérent : chaque "nouvel écran" mérite sa petite
entrée). */
animation: fade-rise 0.5s var(--ease-standard) both;
}
@keyframes fade-rise {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Bloc de marque — discret, rassure l'invité (outil "pro") et signe le
@@ -269,6 +412,7 @@ body {
display: flex;
flex-direction: column;
gap: 1.15rem;
animation: fade-rise 0.5s var(--ease-standard) 0.08s both;
}
.field {
@@ -310,12 +454,16 @@ textarea {
background: var(--color-surface);
color: inherit;
width: 100%;
transition: border-color 0.15s ease;
transition: border-color 0.18s var(--ease-standard), box-shadow 0.18s var(--ease-standard);
}
/* Anneau de focus teinté (en plus du changement de bordure) : détail de
finition "type iPhone" — un champ actif doit se voir sans ambiguïté,
avec une transition douce plutôt qu'un changement sec. */
input[type="text"]:focus,
textarea:focus {
border-color: var(--color-accent);
box-shadow: 0 0 0 4px var(--color-accent-soft);
}
.visually-hidden {
@@ -382,6 +530,22 @@ textarea:focus {
background: var(--color-plum-soft);
}
/* États hover — uniquement pour les pointeurs qui en ont un (souris/trackpad
d'un prospect qui consulte la page sur desktop lors d'une démo) : jamais
sur mobile tactile, où seuls :active/:focus-visible s'appliquent déjà.
Lift discret + easing posé (pas de rebond ici, réservé aux pops). */
@media (hover: hover) {
.btn-source--primary:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-source--secondary:hover {
transform: translateY(-1px);
background: var(--color-plum-soft);
}
}
/* Icônes SVG inline (aucune requête réseau) : remplacent les emoji des
boutons "Prendre une photo" / "Depuis la galerie". Le rendu des emoji
varie fortement selon l'OS (Android/iOS/Windows) — pas assez maîtrisé
@@ -464,6 +628,21 @@ button:focus-visible {
color: var(--color-muted);
}
@media (hover: hover) {
.legal-link:hover {
color: var(--color-plum);
}
.legal-link:hover .legal-notice__icon {
color: var(--color-plum);
}
#welcome-modal-dismiss:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
}
/* Mot de bienvenue + RGPD — overlay affiché une fois par événement,
au-dessus du formulaire déjà présent en arrière-plan (cf. upload.js). */
.welcome-modal {
@@ -479,6 +658,7 @@ button:focus-visible {
}
.welcome-modal__card {
position: relative;
width: 100%;
max-width: 480px;
max-height: 88vh;
@@ -493,6 +673,19 @@ button:focus-visible {
gap: 0.9rem;
}
/* Liseré dégradé en haut de la carte — même traitement que les cartes de la
galerie admin (src/admin/gallery/style.css), pour une petite touche de
couleur cohérente entre les deux écrans sur un élément de chrome (pas de
contenu), plutôt qu'un ajout ponctuel isolé. */
.welcome-modal__card::before {
content: "";
display: block;
height: 4px;
flex-shrink: 0;
margin: -1.5rem -1.25rem 1.1rem;
background: linear-gradient(90deg, var(--color-accent), var(--color-gold));
}
@media (min-width: 520px) {
.welcome-modal {
align-items: center;
@@ -506,13 +699,30 @@ button:focus-visible {
margin-bottom: 0;
}
/* Peaufinage passe 3 (la modale était fonctionnelle mais minimale) : la
marque prend la forme d'un badge rond coloré plutôt qu'une petite icône
nue — cohérent avec le ton "app premium" recherché, sans nouvel élément
HTML (juste du style appliqué au SVG existant). */
.welcome-modal .brand__mark {
width: 30px;
height: 30px;
padding: 7px;
border-radius: 50%;
background: var(--color-accent-soft);
color: var(--color-accent);
}
.welcome-modal__title {
font-family: var(--font-display);
font-weight: 600;
font-size: 1.5rem;
font-size: 1.6rem;
text-align: center;
margin: 0;
color: var(--color-accent);
background: linear-gradient(100deg, var(--color-accent) 20%, var(--color-gold) 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.welcome-modal__text {
@@ -522,11 +732,15 @@ button:focus-visible {
}
#welcome-modal-dismiss {
background: var(--color-accent);
background: linear-gradient(135deg, var(--color-accent-light), var(--color-accent));
color: var(--color-accent-contrast);
box-shadow: var(--shadow-sm);
}
#welcome-modal-dismiss:active {
background: linear-gradient(135deg, var(--color-accent), var(--color-accent-hover));
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
@@ -581,9 +795,28 @@ button {
box-shadow: none;
}
@media (hover: hover) {
#submit-btn:hover:not(:disabled),
#add-another-btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
/* Micro-nudge de l'icône avion au survol : détail qui n'existe que pour
les visiteurs desktop (donc surtout les prospects en démo), sans
dépendance JS supplémentaire. */
#submit-btn:hover:not(:disabled) .btn-icon {
transform: translate(2px, -2px);
}
}
#submit-btn .btn-icon {
transition: transform 0.18s var(--ease-standard);
}
#retry-btn {
background: var(--color-error-text);
color: #fff;
background: var(--color-danger);
color: var(--color-danger-contrast);
}
/* ---------------------------------------------------------------------
@@ -695,6 +928,19 @@ button {
border-radius: 50%;
animation-delay: 0.28s;
}
.confetti span:nth-child(7) {
left: 10%;
top: 0.3rem;
background: var(--color-gold);
border-radius: 50%;
animation-delay: 0.18s;
}
.confetti span:nth-child(8) {
left: 94%;
background: var(--color-accent);
border-radius: 2px;
animation-delay: 0.08s;
}
@keyframes confetti-fall {
0% {
@@ -732,7 +978,14 @@ button {
.preview,
.state-icon--limit,
button,
.btn-source {
.btn-source,
.page__header,
.upload-form,
.welcome-modal,
.welcome-modal__card,
input[type="text"],
textarea,
.btn-icon {
animation: none !important;
transition: none !important;
}