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
30 changes: 26 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ Tout ce qui s'affiche vient de ces fichiers. On n'a jamais besoin de toucher au
| `src/data/certifications.json` | Licences et certifications |
| `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) |

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.
Expand All @@ -72,6 +74,22 @@ unique. L'ordre se calcule tout seul, du plus récent au plus ancien.
ou `none` (gris). Vider `statut.texte` masque la pastille.
- `liens.cv` : déposer `public/cv.pdf` et écrire `"cv": "cv.pdf"`. Un lien vide masque
son bouton.
- `langues` : un drapeau par langue à côté du lieu, avec `nom` et `niveau` dans une bulle
au survol ou au toucher. `code` vaut `fr`, `en` ou `es` (drapeaux dessinés dans
`src/components/Drapeau.astro`). Un tableau vide masque les drapeaux.

### Langues

Le site existe en français (à `/`, par défaut) et en anglais (à `/en/`) ; le bouton à
gauche du thème passe de l'un à l'autre en restant sur la même page.

Les fichiers de `src/data/en/` ne contiennent **que les textes à traduire** : le reste
(dates, logos, liens…) vient du fichier français du même nom. Une entrée se retrouve par
son `id` (expériences, formations, bénévolat, sections de `site.json`) ; les postes d'une
même entreprise et les langues de `profil.json` se suivent dans le même ordre qu'en
français. Un champ absent reste en français ; un champ ou un `id` inconnu fait échouer le
build avec un message qui le désigne. Les pages de projets (`src/content/projets/`) ne sont
écrites qu'en français.

### Dates

Expand Down Expand Up @@ -121,9 +139,10 @@ qui désigne le fichier et le champ.
à un service tiers.
- `src/styles/tokens.css` : couleurs (gris neutres, clair et sombre), verre, formes.
- `src/styles/global.css` : mise en page et composants.
- Le bouton en haut à droite passe du thème clair au sombre ; le choix est gardé sur
l'appareil. Sans choix, le site suit le réglage du système.
- 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).

## Structure

Expand All @@ -132,11 +151,14 @@ src/
├── data/ # réglages et contenu (JSON)
├── content/projets/ # un .md par projet
├── content.config.ts # vérification du contenu
├── lib/reglages.ts # vérification de site.json et profil.json
├── data/en/ # traductions anglaises (seulement les textes)
├── lib/reglages.ts # vérification de site.json et profil.json, par langue
├── lib/i18n.ts # langues, et pose des traductions sur le français
├── layouts/Base.astro # <head>, haut de page, pied de page
├── components/sections/ # une section par fichier
├── components/ # tuile projet, logo d'organisation, étiquettes, icônes
├── pages/ # accueil, page de projet, 404
├── vues/ # accueil et page de projet, communes aux deux langues
├── pages/ # adresses : / et /en/, projets, 404
├── scripts/ui.ts # thème, apparition, durées
└── styles/ # jetons et composants
```
150 changes: 150 additions & 0 deletions THEMES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Thèmes du site

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` |

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

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.

## Halloween

Actif **chaque année du 1er octobre à 0 h au 31 octobre à 23 h 59**. Sobre, pour rester pro.

Dans les deux versions :

- 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.

| | 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 |

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.

## 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"
}
]
}
```

| 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. |
| `nom` | Son nom, pour s'y retrouver. |
| `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. |
| `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.

### Exemples

**Désactiver Halloween** :

```json
"actif": false
```

**Changer la période** (ex. 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.

**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) :

```json
"forcer": "halloween"
```

## 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.

## 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.

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.

## Ajouter un thème (pour le code)

Un thème d'habillage, c'est quatre morceaux. Exemple pour un thème `noel` :

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"`.

Si deux thèmes actifs se chevauchent, le premier de la liste l'emporte.

## 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/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 |
Binary file modified public/img/orelink-400.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/img/orelink.png
Binary file not shown.
Binary file added public/logos/assomiagebordeaux.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/logos/lassoif.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/logos/maydayfestival.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/logos/miageconnection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/logos/miagistesalumnibordelais.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/components/Brand.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
---
// Le logo « ore_link » en snake_case : le séparateur en couleur d'accent (textes dans site.json → logo).
import { site } from '../lib/reglages';
import { reglages } from '../lib/reglages';
import { langueDe } from '../lib/i18n';

interface Props {
size?: 'lg' | 'sm';
}
const { size = 'sm' } = Astro.props;
const { avant, separateur, apres } = site.logo;
const { avant, separateur, apres } = reglages(langueDe(Astro.url)).site.logo;
---

<span class:list={['brand', `is-${size}`]} role="img" aria-label={`${avant}${separateur}${apres}`}>
Expand Down
4 changes: 3 additions & 1 deletion src/components/Description.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// Description d'une tuile, repliée à 3 lignes (la dernière s'estompe) avec « Voir plus » / « Voir moins ».
// Une ligne vide sépare deux paragraphes. Sans JavaScript, le texte s'affiche en entier ; le bouton
// n'apparaît que si le texte dépasse.
import { t } from '../lib/reglages';
import { reglages } from '../lib/reglages';
import { langueDe } from '../lib/i18n';

interface Props {
text: string;
}
const { text } = Astro.props;
const { t } = reglages(langueDe(Astro.url));
const paragraphes = text.split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean);
---

Expand Down
34 changes: 34 additions & 0 deletions src/components/Drapeau.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
// Drapeau d'une langue, dessiné en SVG (les émojis drapeaux ne s'affichent pas sous Windows).
// Tous sont cadrés en 3:2 ; le drapeau britannique, en 2:1, est recadré au centre.
interface Props {
code: 'fr' | 'en' | 'es';
}
const { code } = Astro.props;
// Un drapeau peut paraître plusieurs fois sur une page : identifiant propre à chaque exemplaire.
const clip = `drapeau-en-${Math.random().toString(36).slice(2, 8)}`;
---

{code === 'fr' && (
<svg class="flag" viewBox="0 0 3 2" aria-hidden="true" focusable="false">
<path fill="#002654" d="M0 0h1v2H0z" />
<path fill="#FFFFFF" d="M1 0h1v2H1z" />
<path fill="#CE1126" d="M2 0h1v2H2z" />
</svg>
)}
{code === 'en' && (
<svg class="flag" viewBox="0 0 60 30" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false">
<clipPath id={clip}><path d="M30 15h30v15zv15H0zH0V0zV0h30z" /></clipPath>
<path fill="#012169" d="M0 0h60v30H0z" />
<path stroke="#FFFFFF" stroke-width="6" d="M0 0l60 30m0-30L0 30" />
<path stroke="#C8102E" stroke-width="4" d="M0 0l60 30m0-30L0 30" clip-path={`url(#${clip})`} />
<path stroke="#FFFFFF" stroke-width="10" d="M30 0v30M0 15h60" />
<path stroke="#C8102E" stroke-width="6" d="M30 0v30M0 15h60" />
</svg>
)}
{code === 'es' && (
<svg class="flag" viewBox="0 0 3 2" aria-hidden="true" focusable="false">
<path fill="#AA151B" d="M0 0h3v2H0z" />
<path fill="#F1BF00" d="M0 .5h3v1H0z" />
</svg>
)}
3 changes: 2 additions & 1 deletion src/components/Duree.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Durée « 2 ans 8 mois », calculée au build et tenue à jour dans le navigateur pour ce qui est en
// cours (src/scripts/ui.ts), sans avoir à republier le site chaque mois.
import { duree } from '../lib/dates';
import { langueDe } from '../lib/i18n';

interface Props {
debut: string;
Expand All @@ -10,4 +11,4 @@ interface Props {
const { debut, fin } = Astro.props;
---

<span class="nowrap" data-duree-debut={fin === null ? debut : undefined}>{duree(debut, fin)}</span>
<span class="nowrap" data-duree-debut={fin === null ? debut : undefined}>{duree(debut, fin, langueDe(Astro.url))}</span>
1 change: 1 addition & 0 deletions src/components/Icons.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<symbol id="i-pin" viewBox="0 0 24 24"><path d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11Z"/><circle cx="12" cy="10" r="2.3"/></symbol>
<symbol id="i-mail" viewBox="0 0 24 24"><rect x="3.5" y="5.5" width="17" height="13" rx="2.5"/><path d="m4.5 7.5 7.5 5.5 7.5-5.5"/></symbol>
<symbol id="i-external" viewBox="0 0 24 24"><path d="M14 4h6v6M20 4l-8.5 8.5"/><path d="M18 14v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path d="m5 12.5 4.5 4.5L19 7.5"/></symbol>
<symbol id="i-arrow-right" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></symbol>
<symbol id="i-arrow-left" viewBox="0 0 24 24"><path d="M19 12H5M11 6l-6 6 6 6"/></symbol>
<symbol id="i-code" viewBox="0 0 24 24"><path d="m8.5 7-5 5 5 5M15.5 7l5 5-5 5M13.5 4.5l-3 15"/></symbol>
Expand Down
11 changes: 7 additions & 4 deletions src/components/ProjetCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
import type { CollectionEntry } from 'astro:content';
import Icon from './Icon.astro';
import Tags from './Tags.astro';
import { STATUTS } from '../lib/projets';
import { statuts } from '../lib/projets';
import { url } from '../lib/paths';
import { t } from '../lib/reglages';
import { reglages } from '../lib/reglages';
import { langueDe } from '../lib/i18n';

interface Props {
projet: CollectionEntry<'projets'>;
}
const { projet } = Astro.props;
const p = projet.data;
const statut = STATUTS[p.statut];
const langue = langueDe(Astro.url);
const { t } = reglages(langue);
const statut = statuts(t)[p.statut];
---

<article class="tile glass project">
Expand All @@ -25,7 +28,7 @@ const statut = STATUTS[p.statut];
<p class="project-text">{p.resume}</p>
<Tags items={p.stack} label={t.technologies} />
<div class="project-foot">
<a class="project-link" href={url(`projets/${projet.id}/`)}>
<a class="project-link" href={url(`projets/${projet.id}/`, langue)}>
{t.commentJeLaiFait}<span class="visually-hidden"> : {p.titre}</span>
<Icon name="arrow-right" class="icon-sm" />
</a>
Expand Down
21 changes: 21 additions & 0 deletions src/components/Repli.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
// Liste d'une section, repliée à une hauteur maximale (hauteurSectionRepliee dans site.json) : le bas
// s'estompe, avec « Tout afficher (n) » / « Voir moins ». Sans JavaScript, tout s'affiche ; le bouton
// n'apparaît que si la liste dépasse nettement.
import { reglages } from '../lib/reglages';
import { langueDe } from '../lib/i18n';

interface Props {
nombre: number;
}
const { nombre } = Astro.props;
const { site, t } = reglages(langueDe(Astro.url));
const toutAfficher = `${t.toutAfficher} (${nombre})`;
---

<div class="fold" data-fold style={`--fold-max: ${site.hauteurSectionRepliee}px`}>
<div class="fold-body">
<slot />
</div>
<button class="more-toggle fold-toggle" type="button" aria-expanded="false" data-label-more={toutAfficher} data-label-less={t.voirMoins} hidden>{toutAfficher}</button>
</div>
5 changes: 4 additions & 1 deletion src/components/Tags.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
import { reglages } from '../lib/reglages';
import { langueDe } from '../lib/i18n';

interface Props {
items: string[];
label?: string;
}
const { items, label = 'Compétences' } = Astro.props;
const { items, label = reglages(langueDe(Astro.url)).t.competences } = Astro.props;
---

{
Expand Down
Loading
Loading