From c3c2cd9d09238782a787e0ae2f7c2f62fba876a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Bertrand?= Date: Sun, 25 Oct 2026 00:11:22 +0200 Subject: [PATCH 1/2] feat: add seasonal themes and decorations for New Year, Spring, Summer, Autumn, Winter, Halloween, Christmas, and Music Festival - Implemented a new function `anneeQuiCommence` to determine the current or next year based on the date. - Created a new module `decor.ts` for random positioning of decorative elements. - Expanded the `HABILLAGES` constant to include multiple seasonal themes. - Added CSS styles for each seasonal theme, including animations and decorations. - Updated the UI script to handle New Year fireworks and year display. - Enhanced global styles to include all seasonal themes. --- AGENTS.md | 16 +- README.md | 5 +- THEMES.md | 185 +++++++++++++------- src/components/habillages/Automne.astro | 41 +++++ src/components/habillages/Ete.astro | 36 ++++ src/components/habillages/Etoiles.astro | 30 ++++ src/components/habillages/FeteMusique.astro | 40 +++++ src/components/habillages/Flocons.astro | 30 ++++ src/components/habillages/Hiver.astro | 25 +++ src/components/habillages/Noel.astro | 16 ++ src/components/habillages/NouvelAn.astro | 53 ++++++ src/components/habillages/Printemps.astro | 35 ++++ src/data/themes.json | 24 +-- src/layouts/Base.astro | 45 +++-- src/lib/dates.ts | 4 + src/lib/decor.ts | 28 +++ src/lib/reglages.ts | 36 ++-- src/scripts/ui.ts | 151 +++++++++++++++- src/styles/global.css | 8 + src/styles/habillages/automne.css | 32 ++++ src/styles/habillages/commun.css | 35 ++++ src/styles/habillages/ete.css | 51 ++++++ src/styles/habillages/fete-musique.css | 48 +++++ src/styles/habillages/hiver.css | 37 ++++ src/styles/habillages/noel.css | 8 + src/styles/habillages/nouvel-an.css | 33 ++++ src/styles/habillages/printemps.css | 34 ++++ 27 files changed, 971 insertions(+), 115 deletions(-) create mode 100644 src/components/habillages/Automne.astro create mode 100644 src/components/habillages/Ete.astro create mode 100644 src/components/habillages/Etoiles.astro create mode 100644 src/components/habillages/FeteMusique.astro create mode 100644 src/components/habillages/Flocons.astro create mode 100644 src/components/habillages/Hiver.astro create mode 100644 src/components/habillages/Noel.astro create mode 100644 src/components/habillages/NouvelAn.astro create mode 100644 src/components/habillages/Printemps.astro create mode 100644 src/lib/decor.ts create mode 100644 src/styles/habillages/automne.css create mode 100644 src/styles/habillages/commun.css create mode 100644 src/styles/habillages/ete.css create mode 100644 src/styles/habillages/fete-musique.css create mode 100644 src/styles/habillages/hiver.css create mode 100644 src/styles/habillages/noel.css create mode 100644 src/styles/habillages/nouvel-an.css create mode 100644 src/styles/habillages/printemps.css diff --git a/AGENTS.md b/AGENTS.md index f26bbe9..d0bdc3d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ Consignes pour les agents de code (et les humains) qui travaillent sur ce dépôt. Le fonctionnement du site pour son propriétaire est dans [README.md](README.md) ; les thèmes -saisonniers dans [THEMES.md](THEMES.md). +(saisons, fêtes, événements) dans [THEMES.md](THEMES.md). ## Le projet @@ -42,10 +42,11 @@ ordinateur **et** à ~390 px de large (pas de défilement horizontal). | `src/pages/` | Routes seulement : `/`, `/en/`, `/projets/[slug]/`, `/en/projets/[slug]/`, 404. Elles rendent une vue. | | `src/components/sections/` | Une section de l'accueil par fichier. | | `src/layouts/Base.astro` | ``, scripts d'avant-rendu (thème, habillage), haut de page, pied de page. | -| `src/scripts/ui.ts` | Tout le JS navigateur : thème, menu des langues, replis « Voir plus », bulles, liens externes. | +| `src/scripts/ui.ts` | Tout le JS navigateur : thème, menu des langues, replis « Voir plus », bulles, liens externes, feux d'artifice du Nouvel An. | | `src/styles/tokens.css` | Jetons de design (couleurs clair/sombre, verre, rayons). | | `src/styles/global.css` | Mise en page et composants. | -| `src/styles/habillages/`, `src/components/habillages/` | Thèmes saisonniers (Halloween…). | +| `src/styles/habillages/`, `src/components/habillages/` | Habillages (saisons, fêtes) : un CSS et un composant chacun, `commun.css`, `Etoiles` et `Flocons` partagés. | +| `src/lib/decor.ts` | Positions pseudo-aléatoires (graine fixe) des éléments de décor. | | `public/logos/` | Logos d'organisations, carrés (400 × 400 de préférence). | ## Conventions @@ -89,9 +90,12 @@ Deux axes indépendants, détaillés dans [THEMES.md](THEMES.md) : - **Apparence** clair/sombre : système par défaut, choix au bouton gardé pour la visite (`sessionStorage`). -- **Habillage** saisonnier (`src/data/themes.json`) : choisi dans le navigateur selon la date - du visiteur ; chaque habillage a une version jour (clair) et nuit (sombre). - Prévisualisation : `?theme=halloween` ou `?theme=aucun`. +- **Habillage** (`src/data/themes.json`) : choisi dans le navigateur selon la date du + visiteur, par priorité **spéciaux > récurrents > saisons** (une saison est toujours active). + Chaque habillage a une version jour (clair) et nuit (sombre), à concevoir séparément. + Prévisualisation : `?theme=` ou `?theme=aucun`. +- Tout décor est `aria-hidden`, passe derrière le contenu, s'allège sur téléphone (`is-wide`) + et s'immobilise si l'appareil demande moins de mouvement. ## Git diff --git a/README.md b/README.md index 58ff687..ee4ad63 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Tout ce qui s'affiche vient de ces fichiers. On n'a jamais besoin de toucher au | `src/data/benevolat.json` | Bénévolat | | `src/content/projets/*.md` | Projets : un fichier par projet (tuile de l'accueil + page de doc) | | `src/data/en/*.json` | Version anglaise (voir « Langues » plus bas) | -| `src/data/themes.json` | Thèmes d'habillage saisonniers (Halloween…) : voir [THEMES.md](THEMES.md) | +| `src/data/themes.json` | Thèmes d'habillage (saisons, fêtes, événements) : voir [THEMES.md](THEMES.md) | Pour ajouter une entrée, ajouter un objet dans le tableau JSON concerné, avec un `id` unique. L'ordre se calcule tout seul, du plus récent au plus ancien. @@ -142,7 +142,8 @@ qui désigne le fichier et le champ. - Le bouton en haut à droite passe du thème clair au sombre ; le choix vaut pour la visite en cours. Par défaut, et à chaque nouvelle visite, le site suit le réglage du système. - Les animations s'arrêtent si l'appareil demande moins de mouvement. -- Thèmes d'habillage saisonniers (Halloween en octobre) : voir [THEMES.md](THEMES.md). +- Thèmes d'habillage : une saison toujours active, et des fêtes par-dessus (Halloween, Noël, + Nouvel An, Fête de la musique) : voir [THEMES.md](THEMES.md). ## Structure diff --git a/THEMES.md b/THEMES.md index 7566cea..fa165e7 100644 --- a/THEMES.md +++ b/THEMES.md @@ -5,35 +5,62 @@ Le site a deux réglages visuels indépendants : | | Quoi | Qui décide | |---|---|---| | **Apparence** | Clair ou sombre | Le visiteur (bouton en haut à droite), sinon le réglage de son appareil | -| **Thème d'habillage** | Une ambiance saisonnière posée sur le site (Halloween…) | Le calendrier, d'après `src/data/themes.json` | +| **Thème d'habillage** | Une ambiance posée sur le site : saison, fête, grand événement | Le calendrier, d'après `src/data/themes.json` | Les deux se combinent : chaque thème d'habillage existe en **deux versions**, une version **jour** (apparence claire) et une version **nuit** (apparence sombre), chacune avec son décor et ses animations. Quand le visiteur passe du clair au sombre, le décor change avec un fondu. -## Le thème par défaut +## Trois niveaux de priorité -C'est le site tel qu'il est le reste de l'année : gris neutres, accent bleu (réglé dans -`src/data/site.json` → `couleurs`). Il s'affiche dès qu'aucun thème d'habillage n'est en -période, ou quand ils sont tous désactivés. Il n'a rien à régler ici. +| Niveau | Rôle | Dates | Priorité | +|---|---|---|---| +| **Spéciaux** | Grands événements ponctuels (Coupe du monde, Roland-Garros…) | Avec l'année : `AAAA-MM-JJ` | 1, l'emporte toujours | +| **Récurrents** | Fêtes qui reviennent chaque année | Chaque année : `MM-JJ` | 2 | +| **Saisons** | Fond permanent : les 4 couvrent toute l'année | Chaque année : `MM-JJ` | 3 | +| *Défaut* | Le site sans habillage | Seulement si aucun thème n'est en période (saisons désactivées) | dernier recours | -## Halloween +À une date donnée, le site prend **le premier thème actif en période**, en partant du haut. Par +exemple le 21 juin, la Fête de la musique passe avant l'Été ; le 15 octobre, Halloween passe +avant l'Automne. À l'intérieur d'un même niveau, le premier de la liste l'emporte. -Actif **chaque année du 1er octobre à 0 h au 31 octobre à 23 h 59**. Sobre, pour rester pro. +Le **thème par défaut** (gris neutres, accent bleu réglé dans `src/data/site.json` → +`couleurs`) ne s'affiche donc plus que si les saisons sont désactivées, ou avec +`"forcer": "aucun"`. -Dans les deux versions : +## Les thèmes -- l'accent passe au **orange citrouille** (boutons, liens, le « _ » du logo) ; -- le fond se réchauffe : un halo orange en haut à gauche, violet en bas à droite. +Le contenu, la mise en page et les textes ne changent jamais : le décor passe derrière. Les +animations s'arrêtent si l'appareil demande moins de mouvement (le décor reste affiché, +immobile), et les décors sont allégés sur téléphone. -| | Version jour (apparence claire) | Version nuit (apparence sombre) | -|---|---|---| -| Décor | Une **toile d'araignée** très pâle en haut à gauche, avec son **araignée** au bout d'un fil | Une **lune** en croissant en haut à droite, quelques **étoiles**, des **chauves-souris** | -| Animations | L'araignée monte et descend en se balançant ; des **feuilles d'automne** tombent lentement en tournoyant | La lune luit doucement, les étoiles scintillent, deux chauves-souris planent près de la lune en battant des ailes et une troisième **traverse le ciel** de temps en temps | -| Sur téléphone | 4 feuilles au lieu de 6 | 5 étoiles au lieu de 7, une chauve-souris qui plane au lieu de deux | +### Saisons (dates astronomiques) + +| Thème | Période | Jour (clair) | Nuit (sombre) | Accent | +|---|---|---|---|---| +| **Printemps** | 20 mars – 20 juin | Pétales de cerisier qui volent ; fond vert et rose | Lucioles qui clignotent, quelques étoiles | Vert | +| **Été** | 21 juin – 22 sept. | Halo de soleil, dunes de sable en bas | Voie lactée en diagonale, lueur chaude sur l'horizon, étoiles filantes | Turquoise | +| **Automne** | 23 sept. – 20 déc. | Feuilles qui tombent en tournoyant | Lune rousse, brume qui dérive, quelques feuilles sombres | Bordeaux | +| **Hiver** | 21 déc. – 19 mars | Neige et congères en bas | Aurore boréale qui ondule, étoiles, neige et congères | Bleu glacier | + +### Récurrents + +| Thème | Période | Jour (clair) | Nuit (sombre) | Accent | +|---|---|---|---|---| +| **Halloween** | 1er – 31 oct. | Toile d'araignée et son araignée au bout d'un fil, feuilles qui tombent | Lune en croissant, étoiles, chauves-souris (dont une qui traverse le ciel) | Orange citrouille | +| **Noël** | 1er – 26 déc. | Neige qui tombe sur un fond rouge et vert | Neige et ciel étoilé | Rouge | +| **Nouvel An** | 31 déc. – 2 janv. | Ballons dorés aux chiffres de la nouvelle année, confettis | Feux d'artifice : fusées, gerbes (pivoine, saule, anneau, crépitement) qui retombent | Doré | +| **Fête de la musique** | 21 juin | Notes qui s'envolent, égaliseur en bas | Notes et égaliseur en néon, deux projecteurs qui balaient la scène | Violet | + +L'année des ballons est calculée dans le navigateur : de juillet à décembre, c'est l'année +suivante ; de janvier à juin, l'année en cours. + +### Spéciaux -Les animations s'arrêtent si l'appareil demande moins de mouvement : le décor reste affiché, -immobile. Le contenu, la mise en page et les textes ne changent pas ; le décor passe derrière. +Aucun pour l'instant. Prévus : un habillage **Football** (Coupe du monde, Euro), **Rugby** +(Coupe du monde de rugby) et **Tennis** (Roland-Garros). Chaque événement ne sera qu'une +ligne dans `themes.json`, qui réutilise l'habillage de son sport (voir « Ajouter un +événement spécial »). ## Les réglages : `src/data/themes.json` @@ -41,50 +68,55 @@ immobile. Le contenu, la mise en page et les textes ne changent pas ; le décor { "forcer": "", - "themes": [ - { - "id": "halloween", - "nom": "Halloween", - "actif": true, - "debut": "10-01", - "fin": "10-31", - "accentClair": "#C2410C", - "accentSombre": "#FB923C" - } + "speciaux": [], + + "recurrents": [ + { "id": "halloween", "nom": "Halloween", "actif": true, "debut": "10-01", "fin": "10-31", "accentClair": "#C2410C", "accentSombre": "#FB923C" } + ], + + "saisons": [ + { "id": "printemps", "nom": "Printemps", "actif": true, "debut": "03-20", "fin": "06-20", "accentClair": "#2E7D46", "accentSombre": "#86D19C" } ] } ``` +(Extrait : le fichier contient les 4 récurrents et les 4 saisons.) + | Champ | Rôle | |---|---| -| `forcer` | `""` : chaque thème suit ses dates (normal). `"halloween"` : ce thème toute l'année. `"aucun"` : jamais de thème, le site reste au thème par défaut. | -| `id` | Le thème dessiné dans le code (aujourd'hui : `halloween`). Ne pas changer. | +| `forcer` | `""` : chaque thème suit ses dates (normal). L'`id` d'un thème (ex. `"halloween"`) : ce thème toute l'année. `"aucun"` : jamais d'habillage, le site reste au thème par défaut. | +| `id` | Saisons et récurrents : le thème dessiné dans le code, à ne pas changer. Spéciaux : un nom libre pour l'événement (ex. `"coupe-du-monde-2026"`). | | `nom` | Son nom, pour s'y retrouver. | +| `habillage` | Spéciaux seulement : le dessin à utiliser (ex. `"football"`). | | `actif` | `true` : il s'affiche pendant sa période. `false` : il est désactivé. | -| `debut` | Premier jour, au format `MM-JJ` (`"10-01"` = 1er octobre), **dès minuit**. | -| `fin` | Dernier jour, au format `MM-JJ`, **jusqu'à 23 h 59** inclus. | +| `debut` | Premier jour, **dès minuit**. `MM-JJ` (saisons, récurrents) ou `AAAA-MM-JJ` (spéciaux). | +| `fin` | Dernier jour, **jusqu'à 23 h 59** inclus, même format. | | `accentClair` / `accentSombre` | Couleur d'accent du thème, en apparence claire et sombre (`#RRGGBB`). | Ce fichier est vérifié au build : une date mal écrite (`"1-10"`, `"10-32"`), une couleur -invalide ou un `id` inconnu fait échouer `npm run build` avec un message qui désigne le champ. +invalide, un `id` en double ou un `forcer` inconnu fait échouer `npm run build` avec un +message qui désigne le champ. ### Exemples -**Désactiver Halloween** : +**Désactiver un thème** (ex. la Fête de la musique) : dans son entrée, ```json "actif": false ``` -**Changer la période** (ex. seulement la dernière semaine d'octobre) : +**Changer une période** (ex. Halloween seulement la dernière semaine d'octobre) : ```json "debut": "10-25", "fin": "10-31" ``` -Une période peut passer le 31 décembre : `"debut": "12-15"`, `"fin": "01-05"` couvre du -15 décembre au 5 janvier. +Une période annuelle peut passer le 31 décembre : l'Hiver (`"12-21"` → `"03-19"`) et le +Nouvel An (`"12-31"` → `"01-02"`) le font déjà. + +**Revenir au site sans habillage le reste de l'année** : désactiver les 4 saisons. Les fêtes +restent actives pendant leur période. **Désactiver tous les thèmes d'un coup**, sans toucher à chacun : @@ -92,59 +124,76 @@ Une période peut passer le 31 décembre : `"debut": "12-15"`, `"fin": "01-05"` "forcer": "aucun" ``` -**Garder Halloween en ligne hors saison** (ex. pour une démonstration) : +**Garder un thème en ligne hors période** (ex. pour une démonstration) : ```json -"forcer": "halloween" +"forcer": "nouvel-an" ``` ## Comment la date est choisie Le site est statique : le thème se décide **dans le navigateur du visiteur**, à l'ouverture -de la page, avec **sa date et son heure locales**. Il n'y a donc rien à republier le -1er octobre ni le 1er novembre : le site bascule tout seul. Le choix se fait avant -l'affichage, sans flash du thème par défaut. +de la page, avec **sa date et son heure locales**. Il n'y a donc rien à republier quand une +période commence ou se termine : le site bascule tout seul. Le choix se fait avant +l'affichage, sans flash d'un autre thème. ## Prévisualiser un thème Ajouter `?theme=` à l'adresse, à n'importe quelle date et sans rien modifier : -- `https://orelink.github.io/?theme=halloween` : le site en habillage Halloween ; -- `https://orelink.github.io/?theme=aucun` : le site au thème par défaut, même en octobre. +- `https://orelink.github.io/?theme=noel` : le site en habillage Noël (marche avec tous les + `id` : `printemps`, `ete`, `automne`, `hiver`, `halloween`, `noel`, `nouvel-an`, + `fete-musique`, ou l'`id` d'un événement spécial) ; +- `https://orelink.github.io/?theme=aucun` : le site au thème par défaut. -Fonctionne aussi en local (`npm run dev`, puis `http://localhost:4321/?theme=halloween`) et -sur les pages anglaises (`/en/?theme=halloween`). Cela ne vaut que pour la page ouverte. +Fonctionne aussi en local (`npm run dev`, puis `http://localhost:4321/?theme=noel`) et sur les +pages anglaises (`/en/?theme=noel`). Cela ne vaut que pour la page ouverte. Pour voir l'autre +version (jour ou nuit), utiliser le bouton d'apparence en haut à droite. -## Ajouter un thème (pour le code) +## Ajouter un événement spécial -Un thème d'habillage, c'est quatre morceaux. Exemple pour un thème `noel` : +Quand l'habillage de son sport existe déjà, une ligne suffit dans `speciaux` : -1. **Le déclarer** dans `src/lib/reglages.ts` : ajouter `'noel'` à `HABILLAGES`. -2. **Son décor** dans `src/components/habillages/Noel.astro`, sur le modèle de - `Halloween.astro` : un bloc `decor decor-noel` qui contient deux parties, - `decor-jour` (affichée en apparence claire) et `decor-nuit` (en apparence sombre). Le - site choisit tout seul la bonne partie ; le décor entier reste caché tant que le thème - n'est pas actif. L'ajouter dans le `.backdrop` de `src/layouts/Base.astro`, à côté de - ``. Penser à ce qui a du sens à chaque moment : pas de chauves-souris en - plein jour, pas de soleil la nuit… -3. **Ses couleurs et animations** dans `src/styles/habillages/noel.css`, sur le modèle de - `halloween.css` : les jetons du fond (`--light`, `--shade-1`, `--shade-2`) et du décor - (`--decor`) en clair et en sombre sous `:root[data-habillage="noel"]`, la règle - `[data-habillage="noel"] .decor-noel { display: block; }`, puis le style des éléments. - Mettre les animations dans `@media (prefers-reduced-motion: no-preference)`. L'importer - en tête de `src/styles/global.css`, à côté de `halloween.css`. -4. **Sa période** dans `src/data/themes.json` : une entrée avec `"id": "noel"`. +```json +{ "id": "coupe-du-monde-2026", "nom": "Coupe du monde 2026", "habillage": "football", "actif": true, "debut": "2026-06-11", "fin": "2026-07-19", "accentClair": "#15803D", "accentSombre": "#86EFAC" } +``` -Si deux thèmes actifs se chevauchent, le premier de la liste l'emporte. +Sinon, il faut d'abord dessiner l'habillage (section suivante). + +## Ajouter un habillage (pour le code) + +Un habillage, c'est quatre morceaux. Exemple pour `football` : + +1. **Le déclarer** dans `src/lib/reglages.ts` : ajouter `'football'` à `HABILLAGES`. +2. **Son décor** dans `src/components/habillages/Football.astro`, sur le modèle des autres : + un bloc `decor decor-football` qui contient deux parties, `decor-jour` (affichée en + apparence claire) et `decor-nuit` (en apparence sombre). Le site choisit tout seul la + bonne partie ; le décor entier reste caché tant que l'habillage n'est pas actif. L'ajouter + dans le `.backdrop` de `src/layouts/Base.astro`, avec les autres. Penser à ce qui a du sens + à chaque moment : pas de chauves-souris en plein jour, pas de soleil la nuit… + - Positions « au hasard » mais fixes : `hasard(graine)` de `src/lib/decor.ts`. + - Étoiles et neige toutes faites : `` et ``. + - Moins d'éléments sur téléphone : la classe `is-wide` les masque sous 36rem. +3. **Ses couleurs et animations** dans `src/styles/habillages/football.css`, sur le modèle des + autres : les jetons du fond (`--light`, `--shade-1`, `--shade-2`) en clair et en sombre + sous `:root[data-habillage="football"]`, la règle + `[data-habillage="football"] .decor-football { display: block; }`, puis le style des + éléments. Mettre les animations dans `@media (prefers-reduced-motion: no-preference)`. + L'importer en tête de `src/styles/global.css`, avec les autres. Les pièces communes + (scintillement, chutes, neige) sont dans `commun.css`. +4. **Sa période** dans `src/data/themes.json` : une entrée de saison ou de récurrent avec + `"id": "football"`, ou un événement spécial avec `"habillage": "football"`. ## Fichiers | Fichier | Contenu | |---|---| -| `src/data/themes.json` | Activation, dates et couleurs d'accent des thèmes | -| `src/lib/reglages.ts` | Liste des thèmes existants (`HABILLAGES`) et vérification de `themes.json` | -| `src/layouts/Base.astro` | Le petit script qui choisit le thème selon la date (dans ``) | -| `src/styles/habillages/halloween.css` | Fond, décor et animations d'Halloween | -| `src/components/habillages/Halloween.astro` | Décor jour (toile, araignée, feuilles) et nuit (lune, étoiles, chauves-souris) ; nombre, taille et rythme des feuilles et des étoiles en tête du fichier | +| `src/data/themes.json` | Activation, dates et couleurs d'accent de tous les thèmes | +| `src/lib/reglages.ts` | Liste des habillages existants (`HABILLAGES`) et vérification de `themes.json` | +| `src/layouts/Base.astro` | Le petit script qui choisit le thème selon la date et les priorités (dans ``) | +| `src/components/habillages/*.astro` | Le décor jour et nuit de chaque habillage, plus `Etoiles` et `Flocons` (partagés) | +| `src/styles/habillages/*.css` | Fond, décor et animations de chaque habillage ; `commun.css` pour les pièces partagées | +| `src/lib/decor.ts` | Positions pseudo-aléatoires (graine fixe) des éléments de décor | +| `src/scripts/ui.ts` | Feux d'artifice et année des ballons du Nouvel An | | `src/styles/global.css` | Bascule jour / nuit des décors (règles `.decor-jour` / `.decor-nuit`) | | `src/styles/tokens.css` | Le thème par défaut, en clair et en sombre | diff --git a/src/components/habillages/Automne.astro b/src/components/habillages/Automne.astro new file mode 100644 index 0000000..393bd4e --- /dev/null +++ b/src/components/habillages/Automne.astro @@ -0,0 +1,41 @@ +--- +// Décor Automne (voir THEMES.md), derrière le contenu et caché hors saison. +// Jour : feuilles qui tombent en tournoyant. Nuit : lune rousse, brume qui dérive, quelques +// feuilles sombres. +import { hasard, vars } from '../../lib/decor'; + +const h = hasard(5); +const feuilles = (nombre: number, larges: number, couleurs: string[]) => + Array.from({ length: nombre }, (_, i) => ({ + style: `${vars({ + x: `${h.entre(3, 95)}%`, + top: `${h.entre(5, 90)}vh`, + dur: `${h.entre(17, 26)}s`, + delay: `${h.entre(-26, 0)}s`, + drift: `${h.entre(-60, 60, 0)}px`, + spin: `${h.parmi([-1, 1]) * h.entre(240, 340, 0)}deg`, + })}; color: ${couleurs[i % couleurs.length]}`, + taille: h.entre(18, 30, 0), + large: i >= larges, + })); +const jour = feuilles(7, 4, ['#B45309', '#9F1239', '#9A3412', '#CA8A04']); +const nuit = feuilles(4, 3, ['#C2410C', '#BE123C']); +--- + + diff --git a/src/components/habillages/Ete.astro b/src/components/habillages/Ete.astro new file mode 100644 index 0000000..0e78cd1 --- /dev/null +++ b/src/components/habillages/Ete.astro @@ -0,0 +1,36 @@ +--- +// Décor Été (voir THEMES.md), derrière le contenu et caché hors saison. +// Jour : halo de soleil et dunes de sable. Nuit : Voie lactée en diagonale, lueur chaude sur +// l'horizon, étoiles et étoiles filantes. +import Etoiles from './Etoiles.astro'; +import { hasard, vars } from '../../lib/decor'; + +// La Voie lactée : de minuscules étoiles serrées autour d'une bande (une seule boîte, des ombres). +const h = hasard(3); +const poussiere = Array.from( + { length: 110 }, + () => `${h.entre(-62, 62)}vw ${h.normale(4.5).toFixed(1)}vh 0 ${h.parmi([0, 0, 0.4, 0.8])}px rgb(255 255 255 / ${h.entre(0.25, 0.8, 2)})`, +).join(', '); +const filantes = [ + { x: '12%', y: '9%', dur: '11s', delay: '-3s' }, + { x: '34%', y: '17%', dur: '16s', delay: '-9s' }, + { x: '58%', y: '6%', dur: '21s', delay: '-15s' }, +]; +--- + + diff --git a/src/components/habillages/Etoiles.astro b/src/components/habillages/Etoiles.astro new file mode 100644 index 0000000..cac7545 --- /dev/null +++ b/src/components/habillages/Etoiles.astro @@ -0,0 +1,30 @@ +--- +// Étoiles qui scintillent, pour les versions nuit des habillages. Au-delà de `larges`, elles ne +// s'affichent que sur grand écran ; `hauteur` : jusqu'où elles descendent (en % de l'écran). +import { hasard, vars } from '../../lib/decor'; + +interface Props { + nombre: number; + larges: number; + hauteur: number; + graine: number; +} +const { nombre, larges, hauteur, graine } = Astro.props; +const h = hasard(graine); +const etoiles = Array.from({ length: nombre }, () => ({ + x: h.entre(3, 97), + y: h.entre(3, hauteur), + taille: h.parmi([1.5, 2, 2.5]), + dur: h.entre(3, 6.5), + delay: h.entre(-6, 0), +})); +--- + +{ + etoiles.map((e, i) => ( + = larges }]} + style={`left: ${e.x}%; top: ${e.y}%; width: ${e.taille}px; ${vars({ dur: `${e.dur}s`, delay: `${e.delay}s` })}`} + /> + )) +} diff --git a/src/components/habillages/FeteMusique.astro b/src/components/habillages/FeteMusique.astro new file mode 100644 index 0000000..acc7a26 --- /dev/null +++ b/src/components/habillages/FeteMusique.astro @@ -0,0 +1,40 @@ +--- +// Décor Fête de la musique (voir THEMES.md), derrière le contenu et caché hors période. +// Jour : notes qui s'envolent, égaliseur en bas. Nuit : les mêmes en néon, et deux projecteurs +// qui balaient la scène. +import { hasard, vars } from '../../lib/decor'; + +const h = hasard(14); +const notes = () => + Array.from({ length: 10 }, (_, i) => ({ + signe: '♪♫♬♩'[i % 4], + large: i >= 6, + style: vars({ + x: `${h.entre(3, 95)}%`, + top: `${h.entre(15, 80)}%`, + s: `${h.entre(1.1, 2)}rem`, + dur: `${h.entre(12, 19)}s`, + delay: `${h.entre(-19, 0)}s`, + drift: `${h.entre(-40, 40, 0)}px`, + }), + })); +const barres = Array.from({ length: 36 }, (_, i) => ({ + large: i % 2 === 1, + style: vars({ h: `${h.entre(35, 100, 0)}%`, dur: `${h.entre(0.45, 1.1, 2)}s`, delay: `${h.entre(-1, 0, 2)}s` }), +})); +const jour = notes(); +const nuit = notes(); +--- + + diff --git a/src/components/habillages/Flocons.astro b/src/components/habillages/Flocons.astro new file mode 100644 index 0000000..9a2c916 --- /dev/null +++ b/src/components/habillages/Flocons.astro @@ -0,0 +1,30 @@ +--- +// Neige qui tombe (Hiver, Noël) ; la couleur des flocons (--flake) vient de l'habillage. Au-delà +// de `larges`, ils ne s'affichent que sur grand écran. +import { hasard, vars } from '../../lib/decor'; + +interface Props { + nombre: number; + larges: number; + graine: number; +} +const { nombre, larges, graine } = Astro.props; +const h = hasard(graine); +const flocons = Array.from({ length: nombre }, () => ({ + x: h.entre(2, 98), + top: h.entre(0, 95), + s: h.parmi([3, 4, 4, 5, 6]), + dur: h.entre(13, 24), + delay: h.entre(-24, 0), + drift: h.entre(-24, 24, 0), +})); +--- + +{ + flocons.map((f, i) => ( + = larges }]} + style={vars({ x: `${f.x}%`, top: `${f.top}vh`, s: `${f.s}px`, dur: `${f.dur}s`, delay: `${f.delay}s`, drift: `${f.drift}px` })} + /> + )) +} diff --git a/src/components/habillages/Hiver.astro b/src/components/habillages/Hiver.astro new file mode 100644 index 0000000..f9296cf --- /dev/null +++ b/src/components/habillages/Hiver.astro @@ -0,0 +1,25 @@ +--- +// Décor Hiver (voir THEMES.md), derrière le contenu et caché hors saison. +// Jour : neige et congères. Nuit : aurore boréale, étoiles, neige et congères. +import Etoiles from './Etoiles.astro'; +import Flocons from './Flocons.astro'; +--- + + diff --git a/src/components/habillages/Noel.astro b/src/components/habillages/Noel.astro new file mode 100644 index 0000000..7e36b85 --- /dev/null +++ b/src/components/habillages/Noel.astro @@ -0,0 +1,16 @@ +--- +// Décor Noël (voir THEMES.md), derrière le contenu et caché hors période. +// Jour : neige qui tombe sur un fond rouge et vert. Nuit : neige et ciel étoilé. +import Etoiles from './Etoiles.astro'; +import Flocons from './Flocons.astro'; +--- + + diff --git a/src/components/habillages/NouvelAn.astro b/src/components/habillages/NouvelAn.astro new file mode 100644 index 0000000..2dfabb1 --- /dev/null +++ b/src/components/habillages/NouvelAn.astro @@ -0,0 +1,53 @@ +--- +// Décor Nouvel An (voir THEMES.md), derrière le contenu et caché hors période. +// Jour : confettis qui tombent et ballons dorés aux chiffres de la nouvelle année (posés au build, +// puis tenus à jour par src/scripts/ui.ts). Nuit : étoiles et feux d'artifice (canvas, ui.ts). +import Etoiles from './Etoiles.astro'; +import { hasard, vars } from '../../lib/decor'; +import { anneeQuiCommence } from '../../lib/dates'; + +const h = hasard(12); +const couleurs = ['#EAB308', '#CBD5E1', '#F472B6', '#60A5FA', '#34D399']; +const confettis = Array.from({ length: 18 }, (_, i) => ({ + rond: i % 4 === 3, + large: i >= 10, + style: vars({ + x: `${h.entre(2, 98)}%`, + top: `${h.entre(0, 95)}vh`, + dur: `${h.entre(14, 22)}s`, + delay: `${h.entre(-22, 0)}s`, + drift: `${h.entre(-50, 50, 0)}px`, + spin: `${h.parmi([-1, 1]) * h.entre(160, 320, 0)}deg`, + c: couleurs[i % couleurs.length], + flip: `${h.entre(1.2, 2.6)}s`, + }), +})); +const chiffres = [...String(anneeQuiCommence())].map((c) => ({ c, style: vars({ dur: `${h.entre(2.6, 3.6)}s`, delay: `${h.entre(-3, 0)}s` }) })); +--- + + diff --git a/src/components/habillages/Printemps.astro b/src/components/habillages/Printemps.astro new file mode 100644 index 0000000..7678648 --- /dev/null +++ b/src/components/habillages/Printemps.astro @@ -0,0 +1,35 @@ +--- +// Décor Printemps (voir THEMES.md), derrière le contenu et caché hors saison. +// Jour : pétales de cerisier qui volent. Nuit : lucioles qui clignotent et quelques étoiles. +import Etoiles from './Etoiles.astro'; +import { hasard, vars } from '../../lib/decor'; + +const h = hasard(1); +const petales = Array.from({ length: 8 }, () => ({ + x: h.entre(2, 85), + top: h.entre(5, 90), + s: h.entre(8, 13, 0), + dur: h.entre(19, 27), + delay: h.entre(-27, 0), + drift: h.entre(40, 90, 0), + spin: h.parmi([-1, 1]) * h.entre(200, 320, 0), +})); +const lucioles = Array.from({ length: 12 }, () => ({ x: h.entre(4, 96), y: h.entre(45, 92), dur: h.entre(10, 16), delay: h.entre(-10, 0) })); +--- + + diff --git a/src/data/themes.json b/src/data/themes.json index 7ae8468..37f84e4 100644 --- a/src/data/themes.json +++ b/src/data/themes.json @@ -1,15 +1,19 @@ { "forcer": "", - "themes": [ - { - "id": "halloween", - "nom": "Halloween", - "actif": true, - "debut": "10-01", - "fin": "10-31", - "accentClair": "#C2410C", - "accentSombre": "#FB923C" - } + "speciaux": [], + + "recurrents": [ + { "id": "halloween", "nom": "Halloween", "actif": true, "debut": "10-01", "fin": "10-31", "accentClair": "#C2410C", "accentSombre": "#FB923C" }, + { "id": "noel", "nom": "Noël", "actif": true, "debut": "12-01", "fin": "12-26", "accentClair": "#B91C1C", "accentSombre": "#FCA5A5" }, + { "id": "nouvel-an", "nom": "Nouvel An", "actif": true, "debut": "12-31", "fin": "01-02", "accentClair": "#A16207", "accentSombre": "#FACC15" }, + { "id": "fete-musique", "nom": "Fête de la musique", "actif": true, "debut": "06-21", "fin": "06-21", "accentClair": "#7C3AED", "accentSombre": "#C4B5FD" } + ], + + "saisons": [ + { "id": "printemps", "nom": "Printemps", "actif": true, "debut": "03-20", "fin": "06-20", "accentClair": "#2E7D46", "accentSombre": "#86D19C" }, + { "id": "ete", "nom": "Été", "actif": true, "debut": "06-21", "fin": "09-22", "accentClair": "#0E7490", "accentSombre": "#5EC8D8" }, + { "id": "automne", "nom": "Automne", "actif": true, "debut": "09-23", "fin": "12-20", "accentClair": "#9F1239", "accentSombre": "#F08BA0" }, + { "id": "hiver", "nom": "Hiver", "actif": true, "debut": "12-21", "fin": "03-19", "accentClair": "#0369A1", "accentSombre": "#7DD3FC" } ] } diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index 477cc35..2c23ecc 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -4,7 +4,14 @@ import Icons from '../components/Icons.astro'; import Icon from '../components/Icon.astro'; import Brand from '../components/Brand.astro'; import Drapeau from '../components/Drapeau.astro'; +import Printemps from '../components/habillages/Printemps.astro'; +import Ete from '../components/habillages/Ete.astro'; +import Automne from '../components/habillages/Automne.astro'; +import Hiver from '../components/habillages/Hiver.astro'; import Halloween from '../components/habillages/Halloween.astro'; +import Noel from '../components/habillages/Noel.astro'; +import NouvelAn from '../components/habillages/NouvelAn.astro'; +import FeteMusique from '../components/habillages/FeteMusique.astro'; import { url, asset } from '../lib/paths'; import { reglages, themes } from '../lib/reglages'; import { LANGUES, cheminSansLangue, langueDe, type Langue } from '../lib/i18n'; @@ -22,8 +29,14 @@ const canonical = new URL(Astro.url.pathname, Astro.site); // La même page dans chaque langue (sélecteur de langue et liens hreflang). const chemin = cheminSansLangue(Astro.url); const versions = (Object.keys(LANGUES) as Langue[]).map((code) => ({ code, ...LANGUES[code], href: url(chemin, code) })); -// Thèmes d'habillage (src/data/themes.json, voir THEMES.md), choisis dans le navigateur d'après la date. -const habillages = { forcer: themes.forcer, themes: themes.themes.map(({ nom, ...t }) => t) }; +// Thèmes d'habillage (src/data/themes.json, voir THEMES.md), choisis dans le navigateur d'après la date, +// par ordre de priorité : spéciaux, puis récurrents, puis saisons. +const sansNom = ({ nom, ...t }: T) => t; +const habillages = { + forcer: themes.forcer, + speciaux: themes.speciaux.map(sansNom), + annuels: [...themes.recurrents, ...themes.saisons].map((t) => ({ ...sansNom(t), habillage: t.id })), +}; const accents = `--accent-light: ${site.couleurs.accentClair}; --accent-dark: ${site.couleurs.accentSombre};`; --- @@ -58,8 +71,9 @@ const accents = `--accent-light: ${site.couleurs.accentClair}; --accent-dark: ${ } catch {} - {/* Thème d'habillage du moment, posé avant le premier rendu. `?theme=` (ou `?theme=aucun`) - dans l'adresse permet de le prévisualiser à toute date. */} + {/* Thème d'habillage du moment, posé avant le premier rendu : le premier thème actif en période + parmi les spéciaux (dates AAAA-MM-JJ), puis les récurrents et les saisons (MM-JJ, chaque année). + `?theme=` (ou `?theme=aucun`) dans l'adresse permet d'en prévisualiser un à toute date. */}