diff --git a/src/admin/gallery/index.html b/src/admin/gallery/index.html index 3bd72d4..e709cc2 100644 --- a/src/admin/gallery/index.html +++ b/src/admin/gallery/index.html @@ -13,7 +13,8 @@ fichier front, à mettre en place lors du déploiement. --> - + + @@ -38,7 +39,11 @@ - Réessayer + + Réessayer diff --git a/src/admin/gallery/style.css b/src/admin/gallery/style.css index 44fe166..97eac82 100644 --- a/src/admin/gallery/style.css +++ b/src/admin/gallery/style.css @@ -5,7 +5,14 @@ que verront les prospects (photographes, wedding planners) lors d'une démo, donc soigné au même niveau que la page invité. Palette et police Fraunces alignées sur la passe 2 (2026-09-16) : voir src/e/style.css pour - la justification complète de la direction créative. */ + la justification complète de la direction créative. + + Passe 3 (2026-09-16) : mode sombre + finitions "type iPhone" (hover + desktop, entrées en fondu, halo de couleur en fond) alignés sur + src/e/style.css — voir ce fichier pour le détail complet des choix et + des vérifications de contraste. Cet écran est celui que verront le plus + souvent les prospects en démo depuis un ordinateur : les états hover + comptent ici au moins autant que sur la page invité. */ @font-face { font-family: "Fraunces"; @@ -18,7 +25,7 @@ } :root { - color-scheme: light; + color-scheme: light dark; --color-bg: #fff8f0; --color-surface: #ffffff; @@ -49,12 +56,47 @@ --shadow-md: 0 10px 28px rgba(163, 78, 30, 0.16); --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); + --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 — mêmes valeurs et même raisonnement que src/e/style.css + (fond brun quasi-noir, pas d'inversion, contrastes vérifiés WCAG AA). */ +@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-error-bg: #3a1712; + --color-error-text: #ff9b86; + --color-warning-bg: #3a2a12; + --color-warning-text: #ffd98a; + + --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.45); + --shadow-md: 0 14px 36px rgba(0, 0, 0, 0.55); + } + + body::before { + opacity: 0.35; + } +} + * { box-sizing: border-box; } @@ -64,6 +106,7 @@ html { } body { + position: relative; margin: 0; font-family: var(--font-sans); background: var(--color-bg); @@ -71,6 +114,20 @@ body { line-height: 1.45; } +/* Halo de couleur en fond — même principe que src/e/style.css : "plus de + couleur" en réutilisant la palette existante (corail + prune), jamais + une nouvelle teinte, purement décoratif (CSS pur, poids nul). */ +body::before { + content: ""; + position: fixed; + inset: 0; + z-index: -1; + pointer-events: none; + background: radial-gradient(55% 32% at 50% -8%, var(--color-accent-soft) 0%, transparent 72%), + radial-gradient(40% 26% at 100% 100%, var(--color-plum-soft) 0%, transparent 70%); + opacity: 0.7; +} + .page { max-width: 960px; margin: 0 auto; @@ -81,6 +138,18 @@ body { .page__header { text-align: center; margin-bottom: 1.5rem; + animation: fade-rise 0.5s var(--ease-standard) both; +} + +@keyframes fade-rise { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } } .brand { @@ -223,6 +292,18 @@ body { background: var(--color-plum-soft); } +@media (hover: hover) { + .btn:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: var(--shadow-md); + } + + .btn--secondary:hover:not(:disabled) { + background: var(--color-plum-soft); + box-shadow: none; + } +} + .btn:focus-visible, a:focus-visible { outline: 3px solid var(--color-plum); @@ -249,13 +330,28 @@ a:focus-visible { } .photo-card { + position: relative; background: var(--color-surface); border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column; box-shadow: var(--shadow-sm); - transition: box-shadow 0.15s var(--ease-bounce), transform 0.15s var(--ease-bounce); + transition: box-shadow 0.2s var(--ease-standard), transform 0.2s var(--ease-standard); +} + +/* Liseré de couleur en haut de carte : discret au repos, affirmé au survol + — encore de la "couleur" réutilisant la palette existante, appliquée à + un endroit où elle valorise le contenu (la photo) sans le concurrencer. */ +.photo-card::before { + content: ""; + position: absolute; + inset: 0 0 auto 0; + height: 3px; + background: linear-gradient(90deg, var(--color-accent), var(--color-plum)); + opacity: 0; + transition: opacity 0.2s var(--ease-standard); + z-index: 1; } @media (hover: hover) { @@ -263,6 +359,10 @@ a:focus-visible { box-shadow: var(--shadow-md); transform: translateY(-3px) rotate(-0.6deg); } + + .photo-card:hover::before { + opacity: 1; + } } .photo-card__thumb-wrap { @@ -342,7 +442,9 @@ a:focus-visible { @media (prefers-reduced-motion: reduce) { .spinner, .photo-card, - .btn { + .photo-card::before, + .btn, + .page__header { animation: none !important; transition: none !important; } diff --git a/src/admin/qrcode/index.html b/src/admin/qrcode/index.html index 3e02941..8b27343 100644 --- a/src/admin/qrcode/index.html +++ b/src/admin/qrcode/index.html @@ -5,15 +5,22 @@ Générateur de QR code — photobooth-qr (admin) + + diff --git a/src/e/index.html b/src/e/index.html index 2c91c74..c0ab4c6 100644 --- a/src/e/index.html +++ b/src/e/index.html @@ -12,7 +12,11 @@ lors du déploiement, hors scope de ce fichier front. --> - + + + @@ -96,7 +100,7 @@ - + diff --git a/src/e/style.css b/src/e/style.css index 7c39e6d..4eeb49f 100644 --- a/src/e/style.css +++ b/src/e/style.css @@ -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 ) 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; }