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:
+261
-8
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user