Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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` | `<head>`, 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
Expand Down Expand Up @@ -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=<id>` 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

Expand Down
300 changes: 180 additions & 120 deletions README.md

Large diffs are not rendered by default.

185 changes: 117 additions & 68 deletions THEMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,146 +5,195 @@ 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`

```json
{
"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 :

```json
"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
`<Halloween />`. 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 : `<Etoiles />` et `<Flocons />`.
- 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 `<head>`) |
| `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 `<head>`) |
| `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 |
41 changes: 41 additions & 0 deletions src/components/habillages/Automne.astro
Original file line number Diff line number Diff line change
@@ -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']);
---

<div class="decor decor-automne" aria-hidden="true">
<svg width="0" height="0" style="position:absolute" focusable="false">
<symbol id="au-leaf" viewBox="0 0 24 24">
<path d="M12 1.5C6.5 5.5 3.6 10.6 5.6 16.4 7 20.5 10 22 12 22s5-1.5 6.4-5.6C20.4 10.6 17.5 5.5 12 1.5Z" />
<path fill="none" stroke="#FFFFFF" stroke-opacity="0.45" stroke-width="1" stroke-linecap="round" d="M12 4.5V23M12 10l-3.4-2.6M12 10l3.4-2.6M12 14.5l-4.4-3M12 14.5l4.4-3M12 18.8l-3.6-2.4M12 18.8l3.6-2.4" />
</symbol>
</svg>
<div class="decor-jour">
{jour.map((f) => <svg class:list={['au-leaf x-fall', { 'is-wide': f.large }]} style={f.style} width={f.taille} height={f.taille} focusable="false"><use href="#au-leaf" /></svg>)}
</div>
<div class="decor-nuit">
<span class="au-moon"></span>
<span class="au-mist is-1"></span>
<span class="au-mist is-2"></span>
{nuit.map((f) => <svg class:list={['au-leaf x-fall', { 'is-wide': f.large }]} style={f.style} width={f.taille} height={f.taille} focusable="false"><use href="#au-leaf" /></svg>)}
</div>
</div>
Loading
Loading