Design System
Work in progress. I'm adding components as I go, following patterns from what's already documented. Nothing here is set in stone yet.
Animations
anime.js timelines triggered by IntersectionObserver or the event bus. Elastic easing, staggered entrances.
RevealText
Atom
Composable text reveal that self-animates when its trigger event fires.
Wave (default)
<RevealText text="Expériences" trigger="my-event" /> Slide
Mes projets
<RevealText text="Mes projets" as="h2" animation="slide" trigger="my-event" /> Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | required | Text content to reveal |
as | string | "span" | HTML tag to render |
animation | "wave" | "slide" | "wave" | Animation variant |
trigger | string | - | Event name that starts the animation |
class | string | - | CSS class forwarded to the root element |
Header Animation
Foundation
Staggered entrance for header elements. Title, nav items, and menu slide up with elastic easing.
Preview
Timeline
| Target | Property | Value | Timing |
|---|---|---|---|
| .title-wrapper | translateY | -1.2em → 0 | delay: 0 |
| .navbar li | translateY | -1.2em → 0 | stagger: 30ms |
| .header-menu | opacity | 0 → 1 | with translateY |
Easing: outElastic(1, .5) · Duration: 800ms · Triggered after hero or immediately on non-hero pages.
Usage
import { animateHeader } from "@/lib/animations/headerAnime";
animateHeader();
animateHeader({ delay: 300 });Section Reveal
Foundation
IntersectionObserver-based fade-in for page sections. Fires a reveal event per section so child components can react.
Preview
Behavior
| Property | Value |
|---|---|
| Trigger | IntersectionObserver (10% threshold) |
| translateY | 20px → 0 |
| opacity | 0 → 1 |
| Duration | 600ms |
| Root margin (mobile) | 0px 0px 50px 0px |
| Root margin (desktop) | 0px 0px -100px 0px |
Each revealed section emits reveal:section:{id} via the event bus, letting child components (RevealText, tags) animate in response.
Usage
import { revealSections } from "@/lib/animations/revealSection";
const cleanup = revealSections();
// Mark sections in markup:
<section id="experiences" data-animate-section>...</section>Card Reveal
Foundation
IntersectionObserver-based card entrance with scale and staggered children. Adapts to mobile.
Preview
Desktop Timeline
| Step | Target | Properties | Timing |
|---|---|---|---|
| 1 | Card | opacity 0→1, translateY 50→0, scale 0.8→1 | 600ms elastic |
| 2 | [data-animate-elem] | opacity 0→1, translateX -20→0 | 800ms stagger 200ms |
Mobile Timeline
| Step | Target | Properties | Timing |
|---|---|---|---|
| 1 | Card | opacity 0→1, translateY 20→0 | 500ms cubic |
| 2 | [data-animate-elem] | opacity 0→1, translateY 10→0 | 400ms stagger 80ms |
Mobile breakpoint: 900px. Cards in viewport on load are triggered immediately to handle tall elements that don't meet the 10% threshold.
Usage
import { revealCards } from "@/lib/animations/revealCard";
const cleanup = revealCards();
// Mark cards and their animated children:
<div data-animate-card>
<h3 data-animate-elem>Title</h3>
<p data-animate-elem>Content</p>
</div>