feat: passe expérience — motion system niveau Awwwards (DOM/SVG, zéro WebGL)
- IntroLoader : cérémonie d'entrée (flag-bar, monogramme, titrage), 1re visite, skippable - Lenis au layout (ticker GSAP, imports dynamiques) + ancres interceptées - Hero : titrage oversized en masques, filets drapeau en parallaxe, sortie scrubée (scroll-driven CSS) - Reveal syncopé gwoka (use:reveal) sur 83 éléments, flag chips, icônes pop - Marquee kréyòl (tagline + valeurs), pause au survol - Hover craft : boutons à balayage or, nav soulignée + section active, pochettes DJANGOKAM en wipe clip-path + tilt 3D (use:tilt) avec reflet or - Gate unique prefers-reduced-motion partout ; contenu complet sans JS - Lighthouse mobile 91/100/100/100 — JS initial 69 Ko gzip (budget 170 Ko)
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
/**
|
||||
* IntroLoader — cérémonie d'entrée (P5 : chaque état est un moment designé).
|
||||
*
|
||||
* Visibilité pilotée par la classe `intro-pending` posée par theme.js
|
||||
* (externe, CSP-safe) : première visite de la session, JS actif, pas de
|
||||
* reduced-motion. Sans ces conditions, l'overlay est `display: none` et
|
||||
* la page est complète en dessous. La chorégraphie est 100 % CSS
|
||||
* (keyframes à délais syncopés) ; le JS ne gère que la sortie et le skip.
|
||||
*/
|
||||
const DURATION_MS = 1500; // début de la sortie (keyframes CSS)
|
||||
const EXIT_MS = 480; // durée de la sortie (--dur-mesure)
|
||||
|
||||
function finish() {
|
||||
sessionStorage.setItem('oki-intro', '1');
|
||||
document.documentElement.classList.remove('intro-pending');
|
||||
}
|
||||
|
||||
function skip() {
|
||||
finish();
|
||||
}
|
||||
|
||||
function onKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') skip();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (!document.documentElement.classList.contains('intro-pending')) return;
|
||||
document.addEventListener('keydown', onKeydown);
|
||||
// Fin de séquence : la sortie CSS est déjà jouée, on nettoie l'état.
|
||||
const timer = setTimeout(finish, DURATION_MS + EXIT_MS + 100);
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
document.removeEventListener('keydown', onKeydown);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="intro-loader" role="dialog" aria-label="Introduction">
|
||||
<div class="intro-inner">
|
||||
<div class="intro-flags" aria-hidden="true">
|
||||
<span class="seg-noir"></span>
|
||||
<span class="seg-or"></span>
|
||||
<span class="seg-vert"></span>
|
||||
<span class="seg-rouge"></span>
|
||||
</div>
|
||||
<img src="/images/logo-512x512.png" alt="" class="intro-logo" width="96" height="96" />
|
||||
<p class="intro-title" aria-hidden="true">
|
||||
<span class="intro-word w1">ORGANISATION KA</span>
|
||||
<span class="intro-word w2">INTERNATIONALE</span>
|
||||
</p>
|
||||
<p class="intro-ka" lang="gcf">An nou maché</p>
|
||||
</div>
|
||||
<button type="button" class="intro-skip" onclick={skip}>Passer l'intro</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.intro-loader {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Chorégraphie : uniquement quand theme.js a posé intro-pending */
|
||||
:global(html.intro-pending) .intro-loader {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2000;
|
||||
background: var(--noir-oki);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: intro-out var(--dur-mesure) var(--ease-ka) 1.5s forwards;
|
||||
}
|
||||
|
||||
.intro-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
text-align: center;
|
||||
padding: 0 var(--space-4);
|
||||
}
|
||||
|
||||
.intro-flags {
|
||||
display: flex;
|
||||
width: min(420px, 70vw);
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.intro-flags span {
|
||||
flex: 1;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left center;
|
||||
animation: flag-draw 300ms var(--ease-ka) forwards;
|
||||
}
|
||||
|
||||
.intro-flags span:nth-child(2) {
|
||||
animation-delay: 100ms;
|
||||
}
|
||||
|
||||
.intro-flags span:nth-child(3) {
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
|
||||
.intro-flags span:nth-child(4) {
|
||||
animation-delay: 300ms;
|
||||
}
|
||||
|
||||
.seg-noir {
|
||||
background: #0d0d0d;
|
||||
outline: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.seg-or {
|
||||
background: var(--or-oki);
|
||||
}
|
||||
|
||||
.seg-vert {
|
||||
background: var(--vert-oki);
|
||||
}
|
||||
|
||||
.seg-rouge {
|
||||
background: var(--rouge-oki);
|
||||
}
|
||||
|
||||
.intro-logo {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
animation: rise var(--dur-mesure) var(--ease-ka) 450ms forwards;
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -0.01em;
|
||||
line-height: 1.05;
|
||||
font-size: clamp(1.6rem, 5vw, 3rem);
|
||||
}
|
||||
|
||||
.intro-word {
|
||||
display: block;
|
||||
opacity: 0;
|
||||
transform: translateY(110%);
|
||||
animation: rise var(--dur-mesure) var(--ease-ka) forwards;
|
||||
}
|
||||
|
||||
.w1 {
|
||||
animation-delay: 700ms;
|
||||
}
|
||||
|
||||
.w2 {
|
||||
color: var(--or-oki);
|
||||
animation-delay: 820ms;
|
||||
}
|
||||
|
||||
.intro-ka {
|
||||
color: var(--muted);
|
||||
font-size: 0.95rem;
|
||||
opacity: 0;
|
||||
animation: fade var(--dur-mesure) var(--ease-ka) 1100ms forwards;
|
||||
}
|
||||
|
||||
.intro-skip {
|
||||
position: absolute;
|
||||
bottom: var(--space-4);
|
||||
right: var(--space-4);
|
||||
background: none;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--muted);
|
||||
font: inherit;
|
||||
font-size: 0.85rem;
|
||||
padding: 0.4rem 0.9rem;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
animation: fade 300ms var(--ease-ka) 800ms forwards;
|
||||
transition:
|
||||
color var(--dur-tanbou) var(--ease-ka),
|
||||
border-color var(--dur-tanbou) var(--ease-ka);
|
||||
}
|
||||
|
||||
.intro-skip:hover {
|
||||
color: var(--or-oki);
|
||||
border-color: var(--or-oki);
|
||||
}
|
||||
|
||||
@keyframes flag-draw {
|
||||
to {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rise {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes intro-out {
|
||||
to {
|
||||
transform: translateY(-100%);
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user