feat: migration complète 11ty → SvelteKit (v3)
- SvelteKit 2 + Svelte 5 runes + TS, adapter-static, paths.base '/gwada-sirius' (relative: false) - 5 pages × 3 locales (/, /en/, /ht/) : catalogues inlang repris tels quels, routeur i18n maison avec fallback ht→fr - Îlots → composants natifs avec fallback SSR (calculateur, carte Leaflet, globe) ; Leaflet en import dynamique - Dette corrigée : aria-current (bug legacy), polices en double, SEO complet (Seo, hreflang ×3, OG, Twitter, robots, sitemap 15 URLs) - PWA : manifest + service-worker v3 (stale-while-revalidate) - CI GitHub Pages mise à jour (build/), README et NOTES_PROJET réécrits - Lighthouse mobile : 98/95/100/100 — svelte-check 0/0, Leaflet vérifié (2 cartes, tuiles OSM), no-JS vérifié
This commit is contained in:
@@ -31,13 +31,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: npm run build
|
run: npm run build
|
||||||
env:
|
|
||||||
BASE_PATH: /${{ github.event.repository.name }}
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v3
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: ./_site
|
path: ./build
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -38,3 +38,6 @@ Thumbs.db
|
|||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
.cache/
|
.cache/
|
||||||
|
# Mémo local (infra, ne pas pousser)
|
||||||
|
GITEA.md
|
||||||
|
qa.tmp.mjs
|
||||||
|
|||||||
@@ -2,6 +2,22 @@
|
|||||||
|
|
||||||
## 📋 Vue d'ensemble
|
## 📋 Vue d'ensemble
|
||||||
|
|
||||||
|
**Version 3 (branche `svelte`)** : migration complète 11ty → **SvelteKit 2 + Svelte 5 + TypeScript**, pré-rendu statique intégral (`adapter-static`). Les îlots deviennent des composants natifs avec fallback SSR (fin du montage manuel par `mount.js`/IntersectionObserver). L'i18n Paraglide est remplacée par un routeur maison (`src/lib/i18n/index.ts`) qui consomme **les mêmes catalogues** `src/lib/i18n/{fr,en,ht}.json` (format inlang conservé, éditables avec les outils inlang) : FR à `/`, EN `/en/`, HT `/ht/`, fallback fr automatique. Le sous-répertoire GitHub Pages est géré nativement par `paths.base: '/gwada-sirius'` (les contournements v2 — filtre `withBase`, plugin `restorePassthroughCopy`, alias `/src` — ont disparu). La v2 (11ty) est décrite ci-dessous à titre historique.
|
||||||
|
|
||||||
|
**Ce qui change en v3 par rapport aux notes v2 :**
|
||||||
|
- §1 i18n : plus de compilation Paraglide — les catalogues JSON sont importés tels quels, `t(locale, key, vars?)` gère le fallback ht→fr.
|
||||||
|
- §2 alias `/src` : sans objet (plus de Vite-sur-sortie).
|
||||||
|
- §6 BASE_PATH : remplacé par `paths.base` en dur dans `svelte.config.js` ; liens internes via `resolve()` de `$app/paths`, assets via `%sveltekit.assets%`/`asset()`.
|
||||||
|
- Polices : déclarées une seule fois (`static/fonts/fonts.css`) — la duplication passthrough/bundle de la v2 est corrigée.
|
||||||
|
- `aria-current` : bug corrigé (comparaison pathname réel, voir `Nav.svelte`).
|
||||||
|
- Le piège UTC-4 (§3) reste d'actualité : il vit dans `src/lib/dates.ts` et `SiriusPredictions.svelte` — parse par composants, jamais `new Date("YYYY-MM-DD")`.
|
||||||
|
- §5 cache : la discipline d'incrémentation de `CACHE_NAME` s'applique à `src/service-worker.js` (actuellement `gwada-sirius-v3`).
|
||||||
|
- SEO ajouté en v3 : `Seo.svelte` (canonical, hreflang ×3, OG, Twitter), `static/robots.txt`, `static/sitemap.xml` (15 URLs).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Historique — v2 (11ty)
|
||||||
|
|
||||||
Refonte complète (v2) : passage d'une SPA Vite+Svelte 4 à un site **11ty** statique avec **Svelte 5 en îlots**, un design system CSS natif (oklch, `@layer`, container queries) et une i18n compilée (Paraglide/inlang). Conforme à la doctrine UI/UX OKI : nav à 5 entrées, fonctionne sans JavaScript pour tout le contenu éditorial, budget JS minimal.
|
Refonte complète (v2) : passage d'une SPA Vite+Svelte 4 à un site **11ty** statique avec **Svelte 5 en îlots**, un design system CSS natif (oklch, `@layer`, container queries) et une i18n compilée (Paraglide/inlang). Conforme à la doctrine UI/UX OKI : nav à 5 entrées, fonctionne sans JavaScript pour tout le contenu éditorial, budget JS minimal.
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## 🏗️ Architecture
|
||||||
|
|||||||
@@ -13,41 +13,45 @@ Ce site permet de :
|
|||||||
|
|
||||||
## 🚀 Stack technique
|
## 🚀 Stack technique
|
||||||
|
|
||||||
- **[11ty (Eleventy)](https://www.11ty.dev/)** — génération du site statique, contenu et navigation en HTML/CSS, fonctionnel sans JavaScript
|
Version 3, reconstruite avec **[SvelteKit 2 + Svelte 5 (runes) + TypeScript](https://svelte.dev/)**, entièrement pré-rendue en statique (`adapter-static`). L'ancienne version 11ty est conservée sur la branche `main`.
|
||||||
- **[Svelte 5](https://svelte.dev/)** en îlots — hydratation ciblée pour les 3 seuls widgets interactifs (calculateur de prédictions, carte d'observation, globe de la vague planétaire), via `@11ty/eleventy-plugin-vite`
|
|
||||||
- **[Paraglide (inlang)](https://paraglidejs.com/)** — i18n compilée, FR (défaut) / EN / Kreyòl Ayisyen, routée par URL (`/`, `/en/`, `/ht/`)
|
|
||||||
- **CSS natif** — tokens `oklch()`, cascade `@layer`, container queries, aucun framework CSS ni préprocesseur
|
|
||||||
- Polices auto-hébergées (Fraunces, Atkinson Hyperlegible), zéro dépendance à Google Fonts
|
|
||||||
|
|
||||||
Conforme à la doctrine UI/UX OKI (voir document de référence interne) : navigation à 5 entrées, cibles tactiles ≥ 48 px, contraste AA/AAA, `prefers-reduced-motion` respecté, budget JS minimal.
|
- **SvelteKit statique** — tout le contenu éditorial fonctionne sans JavaScript ; les 3 widgets (calculateur, carte, globe) sont des composants Svelte natifs avec fallback SSR (plus d'îlots montés à la main)
|
||||||
|
- **i18n maison** — les catalogues au format Paraglide/inlang (`src/lib/i18n/{fr,en,ht}.json`, éditables avec les outils inlang) consommés par un routeur par dossiers : FR à `/`, EN sur `/en/`, Kreyòl sur `/ht/` (retombe sur le fr pour le contenu long)
|
||||||
|
- **CSS natif** — tokens `oklch()`, cascade `@layer`, container queries, aucun framework
|
||||||
|
- **Leaflet** en import dynamique côté client uniquement (tuiles OSM — exception documentée à la règle zéro-tiers)
|
||||||
|
- Polices auto-hébergées (Fraunces, Atkinson Hyperlegible), zéro Google Fonts
|
||||||
|
- **PWA** : manifest + service worker stale-while-revalidate (`src/service-worker.js`, incrémenter `CACHE_NAME` à chaque évolution)
|
||||||
|
- **SEO** : canonical, hreflang ×3 + x-default, Open Graph, Twitter Cards (`src/lib/components/Seo.svelte`), `robots.txt` + `sitemap.xml` (15 URLs)
|
||||||
|
|
||||||
|
Conforme à la doctrine UI/UX OKI : navigation à 5 entrées, cibles tactiles ≥ 48 px, contraste AA/AAA, `prefers-reduced-motion` respecté, budget JS minimal.
|
||||||
|
|
||||||
## 📦 Installation
|
## 📦 Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
npm run dev # serveur de développement sur http://localhost:8080
|
npm run dev # http://localhost:5173/gwada-sirius/
|
||||||
npm run build # build de production dans _site/
|
npm run build # build de production dans build/
|
||||||
npm run preview # prévisualiser le build de production
|
npm run preview # prévisualiser le build
|
||||||
|
npm run check # types + accessibilité (svelte-check)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🗂️ Structure
|
## 🗂️ Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── _data/ # données globales (sites d'observation, navigation)
|
├── lib/
|
||||||
├── _includes/ # layout de base, partiels (nav, footer)
|
│ ├── i18n/ # catalogues fr/en/ht (format Paraglide) + routeur localisé
|
||||||
├── styles/ # design system CSS (tokens, reset, base, layouts, composants)
|
│ ├── data/ # sites d'observation, villes de la vague, prochain lever, nav
|
||||||
├── islands/ # composants Svelte 5 hydratés côté client (prédictions, carte, globe)
|
│ ├── styles/ # design system CSS (tokens, base, layouts, composants, thème oki)
|
||||||
├── paraglide/ # messages i18n compilés (généré, ignoré par git)
|
│ ├── components/ # Nav, Footer, Seo, pages/ (une par page), islands/ (3 widgets)
|
||||||
├── index.njk # accueil
|
│ └── dates.ts # formatage local des dates (piège UTC-4, voir NOTES_PROJET)
|
||||||
├── sirius-science.njk # astronomie + méthode de calcul
|
├── routes/ # / (fr), en/, ht/ — 5 pages par locale
|
||||||
├── culture-savoirs.njk# héritage culturel + savoir dogon
|
└── service-worker.js # PWA (stale-while-revalidate)
|
||||||
├── observer.njk # calculateur, carte, guide d'observation
|
static/ # favicon.svg, manifest.webmanifest, robots.txt, sitemap.xml, fonts/
|
||||||
└── associations.njk # associations qui fêtent le Wep Ronpet
|
|
||||||
messages/ # catalogues de traduction (fr/en/ht), source pour Paraglide
|
|
||||||
project.inlang/ # configuration du projet i18n
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Le déploiement GitHub Pages (sous-répertoire `/gwada-sirius/`) est géré par `paths.base` dans `svelte.config.js` + `.github/workflows/deploy.yml`.
|
||||||
|
|
||||||
## 👥 Crédits
|
## 👥 Crédits
|
||||||
|
|
||||||
Projet réalisé avec ❤️ par [cyber-mawonaj](https://github.com/cyber-mawonaj) et [OKI](https://o-k-i.net).
|
Projet réalisé avec ❤️ par [cyber-mawonaj](https://github.com/cyber-mawonaj) et [OKI](https://o-k-i.net).
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
import fs from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import EleventyVitePlugin from "@11ty/eleventy-plugin-vite";
|
|
||||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
|
||||||
import * as m from "./src/paraglide/messages.js";
|
|
||||||
import { locales, baseLocale } from "./src/paraglide/runtime.js";
|
|
||||||
|
|
||||||
const OUTPUT_DIR = "_site";
|
|
||||||
|
|
||||||
// GitHub Pages project sites are served under /<repo-name>/, not at the
|
|
||||||
// domain root — set BASE_PATH (no trailing slash, e.g. "/gwada-sirius")
|
|
||||||
// in CI to prefix internal links and Vite's own asset URLs accordingly.
|
|
||||||
// Left empty for local dev and for a future custom-domain deployment.
|
|
||||||
const BASE_PATH = (process.env.BASE_PATH || "").replace(/\/+$/, "");
|
|
||||||
|
|
||||||
// Vite's build wipes the output dir (emptyOutDir) after 11ty has already
|
|
||||||
// copied passthrough assets into it — re-copy them once Vite is done.
|
|
||||||
// https://github.com/11ty/eleventy-plugin-vite/issues/42
|
|
||||||
function restorePassthroughCopy(copies) {
|
|
||||||
return {
|
|
||||||
name: "restore-passthrough-copy",
|
|
||||||
apply: "build",
|
|
||||||
closeBundle() {
|
|
||||||
for (const [from, to] of copies) {
|
|
||||||
if (!fs.existsSync(from)) continue;
|
|
||||||
fs.cpSync(from, path.join(OUTPUT_DIR, to), {
|
|
||||||
recursive: true,
|
|
||||||
force: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (eleventyConfig) {
|
|
||||||
eleventyConfig.addPlugin(EleventyVitePlugin, {
|
|
||||||
viteOptions: {
|
|
||||||
appType: "mpa",
|
|
||||||
plugins: [
|
|
||||||
svelte(),
|
|
||||||
restorePassthroughCopy([
|
|
||||||
["src/assets", "assets"],
|
|
||||||
["public", "."],
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
"/src": path.resolve(".", "src"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
base: BASE_PATH ? `${BASE_PATH}/` : "/",
|
|
||||||
build: {
|
|
||||||
emptyOutDir: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
eleventyConfig.ignores.add("src/paraglide/**");
|
|
||||||
eleventyConfig.ignores.add("src/islands/**");
|
|
||||||
|
|
||||||
eleventyConfig.addPassthroughCopy("src/assets");
|
|
||||||
eleventyConfig.addPassthroughCopy({ public: "." });
|
|
||||||
|
|
||||||
// Trilingual message lookup for Nunjucks templates: {{ "hero_title1" | t(locale) }}
|
|
||||||
eleventyConfig.addFilter("t", (key, locale, params = {}) => {
|
|
||||||
const fn = m[key];
|
|
||||||
if (!fn) return key;
|
|
||||||
return fn(params, { locale });
|
|
||||||
});
|
|
||||||
|
|
||||||
eleventyConfig.addGlobalData("locales", locales);
|
|
||||||
eleventyConfig.addGlobalData("baseLocale", baseLocale);
|
|
||||||
eleventyConfig.addGlobalData("currentYear", () => new Date().getFullYear());
|
|
||||||
|
|
||||||
eleventyConfig.addGlobalData("basePath", BASE_PATH);
|
|
||||||
|
|
||||||
eleventyConfig.addFilter("localeHref", (pagePath, locale) => {
|
|
||||||
const clean = pagePath.replace(/^\/+/, "");
|
|
||||||
const localePath = locale === baseLocale ? `/${clean}` : `/${locale}/${clean}`;
|
|
||||||
return BASE_PATH + localePath;
|
|
||||||
});
|
|
||||||
|
|
||||||
// For static/passthrough assets referenced by absolute path outside of
|
|
||||||
// Vite's own bundling (favicon, manifest, service worker, font preloads —
|
|
||||||
// anything marked vite-ignore in layouts/base.njk).
|
|
||||||
eleventyConfig.addFilter("withBase", (assetPath) => BASE_PATH + assetPath);
|
|
||||||
|
|
||||||
eleventyConfig.addFilter("dump", (value) => JSON.stringify(value).replace(/</g, "\\u003c"));
|
|
||||||
|
|
||||||
eleventyConfig.addFilter("formatDate", (isoDate, locale, options = {}) => {
|
|
||||||
// new Date("YYYY-MM-DD") parses as UTC midnight, which can roll back a
|
|
||||||
// day once formatted in a timezone behind UTC (e.g. Guadeloupe, UTC-4).
|
|
||||||
// Build the date from its local components instead.
|
|
||||||
const [year, month, day] = isoDate.split("-").map(Number);
|
|
||||||
return new Date(year, month - 1, day).toLocaleDateString(
|
|
||||||
locale === "ht" ? "fr" : locale,
|
|
||||||
{ day: "numeric", month: "long", year: "numeric", ...options }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
dir: {
|
|
||||||
input: "src",
|
|
||||||
output: OUTPUT_DIR,
|
|
||||||
includes: "_includes",
|
|
||||||
data: "_data",
|
|
||||||
},
|
|
||||||
templateFormats: ["njk", "md"],
|
|
||||||
markdownTemplateEngine: "njk",
|
|
||||||
htmlTemplateEngine: "njk",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Generated
+333
-2483
File diff suppressed because it is too large
Load Diff
+15
-15
@@ -1,24 +1,24 @@
|
|||||||
{
|
{
|
||||||
"name": "gwada-sirius",
|
"name": "gwada-sirius",
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"messages": "paraglide-js compile --project ./project.inlang --outdir ./src/paraglide",
|
"dev": "vite dev",
|
||||||
"dev": "npm run messages && eleventy --serve",
|
"build": "vite build",
|
||||||
"build": "npm run messages && eleventy",
|
"preview": "vite preview",
|
||||||
"preview": "npx serve _site",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"deploy": "npm run build && gh-pages -d _site"
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@11ty/eleventy": "^3.1.6",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
"@11ty/eleventy-plugin-vite": "^8.0.0",
|
"@sveltejs/kit": "^2.70.1",
|
||||||
"@fontsource-variable/fraunces": "^5.2.9",
|
"@sveltejs/vite-plugin-svelte": "^7.2.0",
|
||||||
"@fontsource/atkinson-hyperlegible": "^5.2.8",
|
"@types/leaflet": "^1.9.21",
|
||||||
"@inlang/paraglide-js": "^2.20.2",
|
"svelte": "^5.56.7",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.1.4",
|
"svelte-check": "^4.7.3",
|
||||||
"gh-pages": "^6.1.1",
|
"typescript": "^6.0.3",
|
||||||
"svelte": "^5.56.4",
|
"vite": "^8.1.5"
|
||||||
"vite": "^8.1.3"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"leaflet": "^1.9.4"
|
"leaflet": "^1.9.4"
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/project-settings",
|
|
||||||
"baseLocale": "fr",
|
|
||||||
"locales": ["fr", "en", "ht"],
|
|
||||||
"modules": [
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js"
|
|
||||||
],
|
|
||||||
"plugin.inlang.messageFormat": {
|
|
||||||
"pathPattern": "./messages/{locale}.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Sirius en Guadeloupe — Wep Ronpet",
|
|
||||||
"short_name": "Sirius Gwada",
|
|
||||||
"description": "Lever héliaque de Sirius en Guadeloupe : astronomie, culture et observation.",
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"background_color": "#141a33",
|
|
||||||
"theme_color": "#141a33",
|
|
||||||
"icons": [
|
|
||||||
{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// Service worker minimal : résilience réseau (3G/4G irrégulier) et usage
|
|
||||||
// hors-ligne des pages déjà visitées. Stratégie stale-while-revalidate.
|
|
||||||
const CACHE_NAME = "gwada-sirius-v2";
|
|
||||||
|
|
||||||
self.addEventListener("install", (event) => {
|
|
||||||
self.skipWaiting();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener("activate", (event) => {
|
|
||||||
event.waitUntil(
|
|
||||||
caches.keys().then((keys) =>
|
|
||||||
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
self.clients.claim();
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener("fetch", (event) => {
|
|
||||||
if (event.request.method !== "GET") return;
|
|
||||||
const url = new URL(event.request.url);
|
|
||||||
if (url.origin !== self.location.origin) return;
|
|
||||||
|
|
||||||
event.respondWith(
|
|
||||||
caches.open(CACHE_NAME).then(async (cache) => {
|
|
||||||
const cached = await cache.match(event.request);
|
|
||||||
const network = fetch(event.request)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.ok) cache.put(event.request, response.clone());
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch(() => cached);
|
|
||||||
|
|
||||||
return cached || network;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="{{ locale }}" data-theme="oki">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>{{ pageTitle }} — {{ "meta_title" | t(locale) }}</title>
|
|
||||||
<meta name="description" content="{{ 'meta_description' | t(locale) }}">
|
|
||||||
<link vite-ignore rel="icon" href="{{ '/favicon.svg' | withBase }}" type="image/svg+xml">
|
|
||||||
<link vite-ignore rel="manifest" href="{{ '/manifest.webmanifest' | withBase }}">
|
|
||||||
|
|
||||||
{% for loc in locales %}
|
|
||||||
<link vite-ignore rel="alternate" hreflang="{{ loc }}" href="{{ slug | localeHref(loc) }}">
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<link vite-ignore rel="preload" href="{{ '/assets/fonts/fraunces-variable.woff2' | withBase }}" as="font" type="font/woff2" crossorigin>
|
|
||||||
<link vite-ignore rel="preload" href="{{ '/assets/fonts/atkinson-400.woff2' | withBase }}" as="font" type="font/woff2" crossorigin>
|
|
||||||
<link rel="stylesheet" href="/src/styles/app.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<a class="skip-link" href="#main-content">{{ "skip_to_content" | t(locale) }}</a>
|
|
||||||
|
|
||||||
{% include "partials/nav.njk" %}
|
|
||||||
|
|
||||||
<main id="main-content">
|
|
||||||
{{ content | safe }}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{% include "partials/footer.njk" %}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
if ("serviceWorker" in navigator) {
|
|
||||||
window.addEventListener("load", () => navigator.serviceWorker.register("{{ '/sw.js' | withBase }}"));
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<footer class="site-footer">
|
|
||||||
<div class="container footer-grid">
|
|
||||||
<div class="footer-section">
|
|
||||||
<h2>Sirius Gwada</h2>
|
|
||||||
<p>{{ "footer_tagline" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-section">
|
|
||||||
<h3>{{ "footer_links_title" | t(locale) }}</h3>
|
|
||||||
<ul>
|
|
||||||
{% for item in nav %}
|
|
||||||
<li><a href="{{ item.path | localeHref(locale) }}">{{ item.key | t(locale) }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-section">
|
|
||||||
<h3>{{ "footer_resources_title" | t(locale) }}</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://stellarium.org" target="_blank" rel="noopener">{{ "footer_resources_stellarium" | t(locale) }}</a></li>
|
|
||||||
<li><a href="{{ '/observer/' | localeHref(locale) }}">{{ "footer_resources_guide" | t(locale) }}</a></li>
|
|
||||||
<li><a href="{{ '/sirius-science/' | localeHref(locale) }}">{{ "footer_resources_references" | t(locale) }}</a></li>
|
|
||||||
<li><a href="mailto:contact@o-k-i.net">{{ "footer_resources_contact" | t(locale) }}</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer-section">
|
|
||||||
<h3>{{ "footer_next_title" | t(locale) }}</h3>
|
|
||||||
<div class="countdown">
|
|
||||||
<div class="countdown-item">
|
|
||||||
<span class="countdown-value">{{ nextRising.date | formatDate(locale, { day: "numeric", month: "long" }) }}</span>
|
|
||||||
<span class="countdown-label">{{ nextRising.time }} · {{ nextRising.year }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container footer-bottom">
|
|
||||||
<p class="pattern-strip kente" aria-hidden="true"></p>
|
|
||||||
<p>© {{ currentYear }} {{ "footer_rights" | t(locale) }}</p>
|
|
||||||
<p class="footer-credits">
|
|
||||||
{{ "footer_copyright_prefix" | t(locale) }}
|
|
||||||
<a href="https://github.com/cyber-mawonaj" target="_blank" rel="noopener">cyber-mawonaj</a>
|
|
||||||
{{ "footer_and" | t(locale) }}
|
|
||||||
<a href="https://o-k-i.net" target="_blank" rel="noopener">OKI</a>
|
|
||||||
</p>
|
|
||||||
<p class="footer-quote">{{ "footer_quote" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<header class="nav">
|
|
||||||
<div class="container nav-inner">
|
|
||||||
<a class="nav-brand" href="{{ '' | localeHref(locale) }}">
|
|
||||||
<span aria-hidden="true">☥</span> Sirius Gwada
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<details class="nav-disclosure">
|
|
||||||
<summary class="nav-toggle">
|
|
||||||
<span class="visually-hidden">Menu</span>
|
|
||||||
<span aria-hidden="true" class="nav-toggle-icon"></span>
|
|
||||||
</summary>
|
|
||||||
<ul class="nav-menu">
|
|
||||||
{% for item in nav %}
|
|
||||||
<li>
|
|
||||||
<a href="{{ item.path | localeHref(locale) }}"
|
|
||||||
{% if pageKey == item.key %}aria-current="page"{% endif %}>
|
|
||||||
{{ item.key | t(locale) }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<ul class="nav-lang">
|
|
||||||
{% for loc in locales %}
|
|
||||||
<li>
|
|
||||||
<a href="{{ slug | localeHref(loc) }}"
|
|
||||||
lang="{{ loc }}"
|
|
||||||
{% if loc == locale %}aria-current="true" class="active"{% endif %}>
|
|
||||||
{{ ("lang_name_" + loc) | t(locale) }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||||
|
declare global {
|
||||||
|
namespace App {
|
||||||
|
// interface Error {}
|
||||||
|
// interface Locals {}
|
||||||
|
// interface PageData {}
|
||||||
|
// interface PageState {}
|
||||||
|
// interface Platform {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="%lang%" data-theme="oki">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="%sveltekit.assets%/favicon.svg" />
|
||||||
|
<link rel="manifest" href="%sveltekit.assets%/manifest.webmanifest" />
|
||||||
|
<link
|
||||||
|
rel="preload"
|
||||||
|
href="%sveltekit.assets%/fonts/fraunces-variable.woff2"
|
||||||
|
as="font"
|
||||||
|
type="font/woff2"
|
||||||
|
crossorigin
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="%sveltekit.assets%/fonts/fonts.css" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
---
|
|
||||||
pagination:
|
|
||||||
data: locales
|
|
||||||
size: 1
|
|
||||||
alias: locale
|
|
||||||
permalink: "{% if locale == baseLocale %}/associations/index.html{% else %}/{{ locale }}/associations/index.html{% endif %}"
|
|
||||||
layout: layouts/base.njk
|
|
||||||
pageKey: associations
|
|
||||||
slug: "associations/"
|
|
||||||
eleventyComputed:
|
|
||||||
pageTitle: "{{ 'associations_title' | t(locale) }}"
|
|
||||||
---
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<p class="section-eyebrow">Wep Ronpet</p>
|
|
||||||
<h1>{{ "associations_title" | t(locale) }}</h1>
|
|
||||||
<p class="pattern-strip bogolan" aria-hidden="true" style="margin-inline:auto"></p>
|
|
||||||
<p class="section-subtitle">{{ "associations_subtitle" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid-auto">
|
|
||||||
<div class="card">
|
|
||||||
<h2>{{ "associations_events_title" | t(locale) }}</h2>
|
|
||||||
<div class="stack" style="--stack-space: 0">
|
|
||||||
<a class="event-link" href="https://my.weezevent.com/nouvel-an-kamite" target="_blank" rel="noopener noreferrer">
|
|
||||||
<span class="event-icon" aria-hidden="true">🎊</span>
|
|
||||||
<span class="event-info">
|
|
||||||
<span class="event-title">{{ "associations_event1_title" | t(locale) }}</span>
|
|
||||||
<span class="event-platform">{{ "associations_event1_organizer" | t(locale) }}</span>
|
|
||||||
<span>{{ "associations_event1_desc" | t(locale) }}</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a class="event-link" href="https://my.weezevent.com/nouvel-an-kamit" target="_blank" rel="noopener noreferrer">
|
|
||||||
<span class="event-icon" aria-hidden="true">✨</span>
|
|
||||||
<span class="event-info">
|
|
||||||
<span class="event-title">{{ "associations_event2_title" | t(locale) }}</span>
|
|
||||||
<span class="event-platform">{{ "associations_event2_organizer" | t(locale) }}</span>
|
|
||||||
<span>{{ "associations_event2_desc" | t(locale) }}</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h2>{{ "associations_community_title" | t(locale) }}</h2>
|
|
||||||
<div class="stack" style="--stack-space: 0">
|
|
||||||
<a class="community-link" href="https://14eme-membre.net/site-web/#événements" target="_blank" rel="noopener noreferrer">
|
|
||||||
<span class="community-icon" aria-hidden="true">🌍</span>
|
|
||||||
<span class="community-info">
|
|
||||||
<span class="community-title">{{ "associations_link1_title" | t(locale) }}</span>
|
|
||||||
<span>{{ "associations_link1_desc" | t(locale) }}</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
<a class="community-link" href="https://www.facebook.com/people/Mouvement-Politique-Spirituel-MUN" target="_blank" rel="noopener noreferrer">
|
|
||||||
<span class="community-icon" aria-hidden="true">📘</span>
|
|
||||||
<span class="community-info">
|
|
||||||
<span class="community-title">{{ "associations_link2_title" | t(locale) }}</span>
|
|
||||||
<span>{{ "associations_link2_desc" | t(locale) }}</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="center" style="margin-top: var(--space-7)">{{ "associations_video_title" | t(locale) }}</h2>
|
|
||||||
<div class="video-frame card" style="margin-top: var(--space-4)">
|
|
||||||
<iframe
|
|
||||||
src="https://www.youtube.com/embed/0_8kWlDTf94"
|
|
||||||
title="{{ 'associations_video_title' | t(locale) }}"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
||||||
allowfullscreen
|
|
||||||
loading="lazy"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="associations-note center">{{ "associations_note" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
---
|
|
||||||
pagination:
|
|
||||||
data: locales
|
|
||||||
size: 1
|
|
||||||
alias: locale
|
|
||||||
permalink: "{% if locale == baseLocale %}/culture-savoirs/index.html{% else %}/{{ locale }}/culture-savoirs/index.html{% endif %}"
|
|
||||||
layout: layouts/base.njk
|
|
||||||
pageKey: culture
|
|
||||||
slug: "culture-savoirs/"
|
|
||||||
eleventyComputed:
|
|
||||||
pageTitle: "{{ 'culture_title' | t(locale) }}"
|
|
||||||
---
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<p class="section-eyebrow">Sirius Gwada</p>
|
|
||||||
<h1>{{ "culture_title" | t(locale) }}</h1>
|
|
||||||
<p class="pattern-strip adinkra" aria-hidden="true" style="margin-inline:auto"></p>
|
|
||||||
<p class="section-subtitle">{{ "culture_subtitle" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid-auto">
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">🏺</div>
|
|
||||||
<h3>{{ "culture_egypt_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "culture_egypt_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">🌍</div>
|
|
||||||
<h3>{{ "culture_dogon_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "culture_dogon_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">⛵</div>
|
|
||||||
<h3>{{ "culture_caribbean_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "culture_caribbean_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="center" style="margin-top: var(--space-7)">{{ "culture_timeline_title" | t(locale) }}</h2>
|
|
||||||
<div class="timeline-v" style="margin-top: var(--space-5)">
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "culture_timeline_1_date" | t(locale) }}</p>
|
|
||||||
<h4>{{ "culture_timeline_1_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "culture_timeline_1_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "culture_timeline_2_date" | t(locale) }}</p>
|
|
||||||
<h4>{{ "culture_timeline_2_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "culture_timeline_2_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "culture_timeline_3_date" | t(locale) }}</p>
|
|
||||||
<h4>{{ "culture_timeline_3_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "culture_timeline_3_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "culture_timeline_4_date" | t(locale) }}</p>
|
|
||||||
<h4>{{ "culture_timeline_4_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "culture_timeline_4_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" style="background: var(--surface-sunk)">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<h2>{{ "dogon_title" | t(locale) }}</h2>
|
|
||||||
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
|
|
||||||
<p class="section-subtitle">{{ "dogon_intro" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset class="tabs">
|
|
||||||
<legend class="visually-hidden">{{ "dogon_title" | t(locale) }}</legend>
|
|
||||||
|
|
||||||
<input type="radio" name="dogon-tab" id="dogon-tab-cosmogony" class="tab-input" checked>
|
|
||||||
<input type="radio" name="dogon-tab" id="dogon-tab-system" class="tab-input">
|
|
||||||
<input type="radio" name="dogon-tab" id="dogon-tab-astronomy" class="tab-input">
|
|
||||||
<input type="radio" name="dogon-tab" id="dogon-tab-heritage" class="tab-input">
|
|
||||||
|
|
||||||
<div class="tab-list" role="tablist">
|
|
||||||
<label class="tab-label" for="dogon-tab-cosmogony">{{ "dogon_tab_cosmogony" | t(locale) }}</label>
|
|
||||||
<label class="tab-label" for="dogon-tab-system">{{ "dogon_tab_system" | t(locale) }}</label>
|
|
||||||
<label class="tab-label" for="dogon-tab-astronomy">{{ "dogon_tab_astronomy" | t(locale) }}</label>
|
|
||||||
<label class="tab-label" for="dogon-tab-heritage">{{ "dogon_tab_heritage" | t(locale) }}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panels">
|
|
||||||
<div class="tab-panel prose center">
|
|
||||||
<p>{{ "dogon_cosmogony_p1" | t(locale) }}</p>
|
|
||||||
<p>{{ "dogon_cosmogony_p2" | t(locale) }}</p>
|
|
||||||
<div class="card">
|
|
||||||
<h3>{{ "dogon_cosmogony_nommo_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_cosmogony_nommo_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panel">
|
|
||||||
<div class="grid-auto">
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_system_sigutolo_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_system_sigutolo_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_system_potolo_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_system_potolo_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_system_emmeya_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_system_emmeya_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card prose center" style="margin-top: var(--space-4)">
|
|
||||||
<h3>{{ "dogon_mystery_title" | t(locale) }}</h3>
|
|
||||||
<div class="grid-auto">
|
|
||||||
<div>
|
|
||||||
<h4>{{ "dogon_mystery_theory1_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "dogon_mystery_theory1_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>{{ "dogon_mystery_theory2_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "dogon_mystery_theory2_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>{{ "dogon_mystery_theory3_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "dogon_mystery_theory3_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card prose center" style="margin-top: var(--space-4)">
|
|
||||||
<h3>{{ "dogon_calc_example_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_calc_example_text" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panel">
|
|
||||||
<div class="grid-auto">
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_astronomy_geometry_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_astronomy_geometry_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_astronomy_calendar_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_astronomy_calendar_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_astronomy_planets_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_astronomy_planets_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "dogon_astronomy_universe_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_astronomy_universe_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panel">
|
|
||||||
<div class="timeline-v">
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "dogon_heritage_1_date" | t(locale) }}</p>
|
|
||||||
<p>{{ "dogon_heritage_1_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "dogon_heritage_2_date" | t(locale) }}</p>
|
|
||||||
<p>{{ "dogon_heritage_2_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "dogon_heritage_3_date" | t(locale) }}</p>
|
|
||||||
<p>{{ "dogon_heritage_3_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-v-item">
|
|
||||||
<p class="timeline-v-date">{{ "dogon_heritage_4_date" | t(locale) }}</p>
|
|
||||||
<p>{{ "dogon_heritage_4_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card prose center" style="margin-top: var(--space-5)">
|
|
||||||
<h3>{{ "dogon_heritage_diaspora_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "dogon_heritage_diaspora_desc" | t(locale) }}</p>
|
|
||||||
<div class="grid-auto" style="margin-top: var(--space-3); text-align: left">
|
|
||||||
<p>🇭🇹 {{ "dogon_heritage_diaspora_haiti" | t(locale) }}</p>
|
|
||||||
<p>🇬🇵 {{ "dogon_heritage_diaspora_guadeloupe" | t(locale) }}</p>
|
|
||||||
<p>🇲🇶 {{ "dogon_heritage_diaspora_martinique" | t(locale) }}</p>
|
|
||||||
<p>🇯🇲 {{ "dogon_heritage_diaspora_jamaica" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { base } from '$app/paths';
|
||||||
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
const LANGS = ['fr', 'en', 'ht'] as const;
|
||||||
|
|
||||||
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
// event.url.pathname inclut le base path (/gwada-sirius/…) — le retirer d'abord
|
||||||
|
const stripped = event.url.pathname.slice(base.length);
|
||||||
|
const segment = stripped.split('/').filter(Boolean)[0];
|
||||||
|
const lang = LANGS.includes(segment as (typeof LANGS)[number]) ? segment : 'fr';
|
||||||
|
return resolve(event, {
|
||||||
|
transformPageChunk: ({ html }) => html.replaceAll('%lang%', lang)
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
---
|
|
||||||
pagination:
|
|
||||||
data: locales
|
|
||||||
size: 1
|
|
||||||
alias: locale
|
|
||||||
permalink: "{% if locale == baseLocale %}/index.html{% else %}/{{ locale }}/index.html{% endif %}"
|
|
||||||
layout: layouts/base.njk
|
|
||||||
pageKey: home
|
|
||||||
slug: ""
|
|
||||||
eleventyComputed:
|
|
||||||
pageTitle: "{{ 'hero_title1' | t(locale) }} {{ 'hero_title2' | t(locale) }}"
|
|
||||||
---
|
|
||||||
<section class="hero">
|
|
||||||
<div class="hero-orbits" aria-hidden="true">
|
|
||||||
<div class="sun"></div>
|
|
||||||
<div class="orbit" style="width:5rem;height:5rem;animation-duration:6s"><span></span></div>
|
|
||||||
<div class="orbit" style="width:8rem;height:8rem;animation-duration:10s"><span></span></div>
|
|
||||||
<div class="orbit" style="width:11rem;height:11rem;animation-duration:14s"><span></span></div>
|
|
||||||
<div class="orbit" style="width:14rem;height:14rem;animation-duration:18s"><span></span></div>
|
|
||||||
<div class="orbit sirius" style="width:18rem;height:18rem;animation-duration:26s"><span></span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container center hero-content">
|
|
||||||
<p class="section-eyebrow">{{ "hero_eyebrow" | t(locale) }}</p>
|
|
||||||
<h1 class="hero-title">
|
|
||||||
<span>{{ "hero_title1" | t(locale) }}</span>
|
|
||||||
<span class="accent">{{ "hero_title2" | t(locale) }}</span>
|
|
||||||
</h1>
|
|
||||||
<p class="hero-subtitle">{{ "hero_subtitle" | t(locale) }}</p>
|
|
||||||
<p class="hero-description">{{ "hero_description" | t(locale) }}</p>
|
|
||||||
<p class="hero-date">
|
|
||||||
{{ "hero_date_label" | t(locale) }}
|
|
||||||
<strong>{{ nextRising.date | formatDate(locale) }} · {{ nextRising.time }}</strong>
|
|
||||||
</p>
|
|
||||||
<div class="cluster hero-cta">
|
|
||||||
<a class="btn btn-primary comet-ring" href="{{ '/observer/' | localeHref(locale) }}">{{ "hero_cta_predictions" | t(locale) }}</a>
|
|
||||||
<a class="btn btn-outline" href="{{ '/sirius-science/' | localeHref(locale) }}">{{ "hero_cta_learn" | t(locale) }}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" id="intro">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<h2>{{ "home_intro_title" | t(locale) }}</h2>
|
|
||||||
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
|
|
||||||
<p class="section-subtitle">{{ "home_intro_subtitle" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="prose center stack" style="--stack-space: var(--space-3)">
|
|
||||||
<p>{{ "home_intro_p1" | t(locale) | safe }}</p>
|
|
||||||
<p>{{ "home_intro_p2" | t(locale) }}</p>
|
|
||||||
<p>{{ "home_intro_p3" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid-auto" style="margin-top: var(--space-6)">
|
|
||||||
<div class="feature-card">
|
|
||||||
<h3>{{ "feature_brightest_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "feature_brightest_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<h3>{{ "feature_rising_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "feature_rising_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<h3>{{ "feature_heritage_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "feature_heritage_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" style="background: var(--surface-sunk)">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<h2>{{ "wave_showcase_title" | t(locale) }}</h2>
|
|
||||||
<p class="section-subtitle">{{ "wave_showcase_subtitle" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="wave-badges">
|
|
||||||
{% for city in waveCities %}
|
|
||||||
<li class="wave-badge wave-badge-{{ loop.index0 % 3 }}">
|
|
||||||
<span class="wave-badge-name">{{ city.name }}</span>
|
|
||||||
<span class="wave-badge-date">{{ city.date }}</span>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p class="center" style="margin-top: var(--space-5)">
|
|
||||||
<a class="btn btn-outline" href="{{ '/observer/' | localeHref(locale) }}">{{ "wave_showcase_cta" | t(locale) }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { onMount, onDestroy } from "svelte";
|
|
||||||
import L from "leaflet";
|
|
||||||
import "leaflet/dist/leaflet.css";
|
|
||||||
import { waveCities } from "./world.js";
|
|
||||||
|
|
||||||
let mapContainer = $state();
|
|
||||||
let map;
|
|
||||||
|
|
||||||
function markerIcon(active) {
|
|
||||||
return L.divIcon({
|
|
||||||
className: "wave-marker" + (active ? " wave-marker-active" : ""),
|
|
||||||
iconSize: active ? [20, 20] : [14, 14],
|
|
||||||
iconAnchor: active ? [10, 10] : [7, 7],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
map = L.map(mapContainer, { scrollWheelZoom: false });
|
|
||||||
|
|
||||||
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
||||||
maxZoom: 18,
|
|
||||||
}).addTo(map);
|
|
||||||
|
|
||||||
const markers = waveCities.map((city) => {
|
|
||||||
const isCaribbean = city.name === "Guadeloupe" || city.name === "Martinique";
|
|
||||||
const marker = L.marker([city.lat, city.lon], {
|
|
||||||
icon: markerIcon(isCaribbean),
|
|
||||||
}).addTo(map);
|
|
||||||
marker.bindPopup(`<strong>${city.name}</strong><br>${city.date}`);
|
|
||||||
marker.on("popupopen", () => marker.setIcon(markerIcon(true)));
|
|
||||||
marker.on("popupclose", () => marker.setIcon(markerIcon(isCaribbean)));
|
|
||||||
return marker;
|
|
||||||
});
|
|
||||||
|
|
||||||
const group = L.featureGroup(markers);
|
|
||||||
map.fitBounds(group.getBounds().pad(0.15));
|
|
||||||
});
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
map?.remove();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={mapContainer}
|
|
||||||
class="wave-map"
|
|
||||||
role="group"
|
|
||||||
aria-label="Carte de la progression du lever héliaque à travers l'Afrique et les Caraïbes"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.wave-map {
|
|
||||||
width: 100%;
|
|
||||||
height: 22rem;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.wave-marker) {
|
|
||||||
background: var(--caribbean-teal);
|
|
||||||
border: 2px solid oklch(20% 0.03 265);
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.wave-marker-active) {
|
|
||||||
background: var(--gold-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.leaflet-popup-content-wrapper) {
|
|
||||||
background: var(--surface-raised);
|
|
||||||
color: var(--ink);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.leaflet-popup-tip) {
|
|
||||||
background: var(--surface-raised);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
const container = document.querySelector('[data-island="globe"]');
|
|
||||||
|
|
||||||
if (container && "IntersectionObserver" in window) {
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (!entry.isIntersecting) continue;
|
|
||||||
observer.disconnect();
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
import("svelte"),
|
|
||||||
import("./SiriusGlobe.svelte"),
|
|
||||||
]).then(([{ mount }, { default: SiriusGlobe }]) => {
|
|
||||||
const target = container.querySelector(".island-app");
|
|
||||||
mount(SiriusGlobe, { target });
|
|
||||||
container.classList.add("is-enhanced");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ rootMargin: "200px" }
|
|
||||||
);
|
|
||||||
|
|
||||||
observer.observe(container);
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { default as waveCities } from "../../_data/waveCities.js";
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { onMount, onDestroy } from "svelte";
|
|
||||||
import L from "leaflet";
|
|
||||||
import "leaflet/dist/leaflet.css";
|
|
||||||
|
|
||||||
let { sites, locale } = $props();
|
|
||||||
|
|
||||||
let mapContainer = $state();
|
|
||||||
let map;
|
|
||||||
|
|
||||||
function markerIcon(active) {
|
|
||||||
return L.divIcon({
|
|
||||||
className: "site-marker" + (active ? " site-marker-active" : ""),
|
|
||||||
iconSize: active ? [22, 22] : [16, 16],
|
|
||||||
iconAnchor: active ? [11, 11] : [8, 8],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
map = L.map(mapContainer, {
|
|
||||||
scrollWheelZoom: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
||||||
maxZoom: 18,
|
|
||||||
}).addTo(map);
|
|
||||||
|
|
||||||
const markers = sites.map((site) => {
|
|
||||||
const marker = L.marker([site.lat, site.lon], { icon: markerIcon(false) }).addTo(map);
|
|
||||||
const desc = site[`description_${locale}`] ?? site.description_fr;
|
|
||||||
marker.bindPopup(
|
|
||||||
`<strong>${site.name}</strong><br>${desc}<br><small style="color: var(--ink-dim)">${site.lat}°N, ${site.lon}°O — ${site.alt} m</small>`
|
|
||||||
);
|
|
||||||
marker.on("popupopen", () => marker.setIcon(markerIcon(true)));
|
|
||||||
marker.on("popupclose", () => marker.setIcon(markerIcon(false)));
|
|
||||||
return marker;
|
|
||||||
});
|
|
||||||
|
|
||||||
const group = L.featureGroup(markers);
|
|
||||||
map.fitBounds(group.getBounds().pad(0.2));
|
|
||||||
});
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
map?.remove();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="observation-map">
|
|
||||||
<div bind:this={mapContainer} class="leaflet-container-host" role="group" aria-label="Carte des sites d'observation de Sirius en Guadeloupe"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.leaflet-container-host {
|
|
||||||
width: 100%;
|
|
||||||
height: 22rem;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.site-marker) {
|
|
||||||
background: var(--gold-surface);
|
|
||||||
border: 2px solid oklch(20% 0.03 265);
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.site-marker-active) {
|
|
||||||
background: var(--caribbean-teal);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.leaflet-popup-content-wrapper) {
|
|
||||||
background: var(--surface-raised);
|
|
||||||
color: var(--ink);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(.leaflet-popup-tip) {
|
|
||||||
background: var(--surface-raised);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
const container = document.querySelector('[data-island="map"]');
|
|
||||||
|
|
||||||
if (container && "IntersectionObserver" in window) {
|
|
||||||
const observer = new IntersectionObserver(
|
|
||||||
(entries) => {
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (!entry.isIntersecting) continue;
|
|
||||||
observer.disconnect();
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
import("svelte"),
|
|
||||||
import("./ObservationMap.svelte"),
|
|
||||||
]).then(([{ mount }, { default: ObservationMap }]) => {
|
|
||||||
const target = container.querySelector(".island-app");
|
|
||||||
const sitesData = JSON.parse(document.getElementById("sites-data").textContent);
|
|
||||||
const locale = document.documentElement.lang;
|
|
||||||
|
|
||||||
mount(ObservationMap, {
|
|
||||||
target,
|
|
||||||
props: { sites: sitesData, locale },
|
|
||||||
});
|
|
||||||
|
|
||||||
container.classList.add("is-enhanced");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ rootMargin: "200px" }
|
|
||||||
);
|
|
||||||
|
|
||||||
observer.observe(container);
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { untrack } from "svelte";
|
|
||||||
|
|
||||||
let { sites, year, locale, labels } = $props();
|
|
||||||
|
|
||||||
let selectedId = $state(untrack(() => sites[0].id));
|
|
||||||
|
|
||||||
let selected = $derived(sites.find((s) => s.id === selectedId));
|
|
||||||
let prediction = $derived(selected.predictions[year]);
|
|
||||||
|
|
||||||
function formatDate(iso) {
|
|
||||||
const [y, m, d] = iso.split("-").map(Number);
|
|
||||||
return new Date(y, m - 1, d).toLocaleDateString(locale === "ht" ? "fr" : locale, {
|
|
||||||
day: "numeric",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function windowLabel(iso) {
|
|
||||||
const [y, m, d] = iso.split("-").map(Number);
|
|
||||||
const start = new Date(y, m - 1, d - 2);
|
|
||||||
const end = new Date(y, m - 1, d + 2);
|
|
||||||
const fmt = (date) => date.toLocaleDateString(locale === "ht" ? "fr" : locale, { day: "numeric", month: "short" });
|
|
||||||
return `${fmt(start)} – ${fmt(end)}`;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="calculator card">
|
|
||||||
<label class="calculator-label" for="site-select">{labels.select}</label>
|
|
||||||
<select id="site-select" bind:value={selectedId} class="calculator-select">
|
|
||||||
{#each sites as site}
|
|
||||||
<option value={site.id}>{site.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
{#if prediction}
|
|
||||||
<div class="calculator-result">
|
|
||||||
<p class="calculator-result-label">{labels.heliacal}</p>
|
|
||||||
<p class="calculator-result-value">{formatDate(prediction.date)} · {prediction.time}</p>
|
|
||||||
<p class="calculator-result-label">{labels.window}</p>
|
|
||||||
<p class="calculator-result-window">{windowLabel(prediction.date)}</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.calculator {
|
|
||||||
max-width: 28rem;
|
|
||||||
margin-inline: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calculator-label {
|
|
||||||
display: block;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.calculator-select {
|
|
||||||
width: 100%;
|
|
||||||
min-height: var(--tap-target);
|
|
||||||
padding-inline: var(--space-3);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
border: 2px solid var(--border);
|
|
||||||
background: var(--surface);
|
|
||||||
color: var(--ink);
|
|
||||||
font-size: var(--text-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.calculator-result {
|
|
||||||
margin-top: var(--space-4);
|
|
||||||
padding-top: var(--space-4);
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.calculator-result-label {
|
|
||||||
font-size: var(--text-0);
|
|
||||||
color: var(--ink-soft);
|
|
||||||
margin-top: var(--space-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.calculator-result-value {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: var(--text-3);
|
|
||||||
color: var(--gold);
|
|
||||||
}
|
|
||||||
|
|
||||||
.calculator-result-window {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { mount } from "svelte";
|
|
||||||
import PredictionsCalculator from "./PredictionsCalculator.svelte";
|
|
||||||
|
|
||||||
const container = document.querySelector('[data-island="predictions"]');
|
|
||||||
if (container) {
|
|
||||||
const target = container.querySelector(".island-app");
|
|
||||||
const sitesData = JSON.parse(document.getElementById("sites-data").textContent);
|
|
||||||
|
|
||||||
mount(PredictionsCalculator, {
|
|
||||||
target,
|
|
||||||
props: {
|
|
||||||
sites: sitesData,
|
|
||||||
year: Number(target.dataset.year),
|
|
||||||
locale: target.dataset.locale,
|
|
||||||
labels: {
|
|
||||||
select: target.dataset.selectLabel,
|
|
||||||
heliacal: target.dataset.heliacalLabel,
|
|
||||||
window: target.dataset.windowLabel,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
container.classList.add("is-enhanced");
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
import nav from '$lib/data/nav.js';
|
||||||
|
import nextRising from '$lib/data/nextRising.js';
|
||||||
|
import { formatDate } from '$lib/dates';
|
||||||
|
import { localizedPath, t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
locale: Locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale }: Props = $props();
|
||||||
|
|
||||||
|
// Calculé au build (prerender) — voir NOTES_PROJET §3 pour la logique dates.
|
||||||
|
const rising = nextRising();
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<footer class="site-footer">
|
||||||
|
<div class="container footer-grid">
|
||||||
|
<div class="footer-section">
|
||||||
|
<h2>Sirius Gwada</h2>
|
||||||
|
<p>{t(locale, 'footer_tagline')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-section">
|
||||||
|
<h3>{t(locale, 'footer_links_title')}</h3>
|
||||||
|
<ul>
|
||||||
|
{#each nav as item (item.key)}
|
||||||
|
<li>
|
||||||
|
<a href={resolve(localizedPath(locale, item.path))}>{t(locale, item.key)}</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-section">
|
||||||
|
<h3>{t(locale, 'footer_resources_title')}</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://stellarium.org" target="_blank" rel="noopener"
|
||||||
|
>{t(locale, 'footer_resources_stellarium')}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href={resolve(localizedPath(locale, '/observer/'))}
|
||||||
|
>{t(locale, 'footer_resources_guide')}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href={resolve(localizedPath(locale, '/sirius-science/'))}
|
||||||
|
>{t(locale, 'footer_resources_references')}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="mailto:contact@o-k-i.net">{t(locale, 'footer_resources_contact')}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-section">
|
||||||
|
<h3>{t(locale, 'footer_next_title')}</h3>
|
||||||
|
<div class="countdown">
|
||||||
|
<div class="countdown-item">
|
||||||
|
<span class="countdown-value"
|
||||||
|
>{formatDate(rising.date, locale, { day: 'numeric', month: 'long' })}</span
|
||||||
|
>
|
||||||
|
<span class="countdown-label">{rising.time} · {rising.year}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container footer-bottom">
|
||||||
|
<p class="pattern-strip kente" aria-hidden="true"></p>
|
||||||
|
<p>© {currentYear} {t(locale, 'footer_rights')}</p>
|
||||||
|
<p class="footer-credits">
|
||||||
|
{t(locale, 'footer_copyright_prefix')}
|
||||||
|
<a href="https://github.com/cyber-mawonaj" target="_blank" rel="noopener">cyber-mawonaj</a>
|
||||||
|
{t(locale, 'footer_and')}
|
||||||
|
<a href="https://o-k-i.net" target="_blank" rel="noopener">OKI</a>
|
||||||
|
</p>
|
||||||
|
<p class="footer-quote">{t(locale, 'footer_quote')}</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Liens dans le corps de texte : distinguables autrement que par la couleur (WCAG) */
|
||||||
|
.footer-credits a {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 0.15em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { base, resolve } from '$app/paths';
|
||||||
|
import { page } from '$app/state';
|
||||||
|
import nav from '$lib/data/nav.js';
|
||||||
|
import {
|
||||||
|
LOCALES,
|
||||||
|
localizedPath,
|
||||||
|
switchLocalePath,
|
||||||
|
t,
|
||||||
|
type Locale
|
||||||
|
} from '$lib/i18n';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
locale: Locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale }: Props = $props();
|
||||||
|
|
||||||
|
// Pathname courant sans le base path (/gwada-sirius), pour comparer aux
|
||||||
|
// chemins de nav et basculer de locale.
|
||||||
|
const pathname = $derived.by(() => {
|
||||||
|
const p = page.url.pathname.startsWith(base)
|
||||||
|
? page.url.pathname.slice(base.length)
|
||||||
|
: page.url.pathname;
|
||||||
|
return p || '/';
|
||||||
|
});
|
||||||
|
|
||||||
|
const normalize = (s: string) => s.replace(/\/+$/, '') || '/';
|
||||||
|
|
||||||
|
// Correction du bug aria-current du legacy : l'ancien partial comparait
|
||||||
|
// `pageKey` (« home ») avec `item.key` (« nav_home ») — toujours faux,
|
||||||
|
// l'entrée active n'était jamais marquée. On compare ici le pathname
|
||||||
|
// réel au chemin localisé de l'entrée.
|
||||||
|
const isCurrent = (path: string) =>
|
||||||
|
normalize(pathname) === normalize(localizedPath(locale, path));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<header class="nav">
|
||||||
|
<div class="container nav-inner">
|
||||||
|
<a class="nav-brand" href={resolve(localizedPath(locale, '/'))}>
|
||||||
|
<span aria-hidden="true">☥</span> Sirius Gwada
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<details class="nav-disclosure">
|
||||||
|
<summary class="nav-toggle">
|
||||||
|
<span class="visually-hidden">Menu</span>
|
||||||
|
<span aria-hidden="true" class="nav-toggle-icon"></span>
|
||||||
|
</summary>
|
||||||
|
<ul class="nav-menu">
|
||||||
|
{#each nav as item (item.key)}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={resolve(localizedPath(locale, item.path))}
|
||||||
|
aria-current={isCurrent(item.path) ? 'page' : undefined}
|
||||||
|
>
|
||||||
|
{t(locale, item.key)}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<ul class="nav-lang">
|
||||||
|
{#each LOCALES as loc (loc)}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={resolve(switchLocalePath(pathname, loc))}
|
||||||
|
lang={loc}
|
||||||
|
aria-current={loc === locale ? 'true' : undefined}
|
||||||
|
class:active={loc === locale}
|
||||||
|
>
|
||||||
|
{t(locale, `lang_name_${loc}`)}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { LOCALES, localizedPath, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Métadonnées SEO d'une page : title, description, canonical absolu,
|
||||||
|
* hreflang ×3 (+ x-default), Open Graph et Twitter summary.
|
||||||
|
* Pas d'og:image : le site n'a pas d'image de partage.
|
||||||
|
*/
|
||||||
|
interface Props {
|
||||||
|
locale: Locale;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
/** Chemin de la page sans locale, ex. `/observer/`. */
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale, title, description, path }: Props = $props();
|
||||||
|
|
||||||
|
// URL de production (GitHub Pages, inclut déjà le base path /gwada-sirius).
|
||||||
|
const SITE = 'https://cyber-mawonaj.github.io/gwada-sirius';
|
||||||
|
|
||||||
|
const OG_LOCALES: Record<Locale, string> = {
|
||||||
|
fr: 'fr_FR',
|
||||||
|
en: 'en_US',
|
||||||
|
ht: 'ht_HT'
|
||||||
|
};
|
||||||
|
|
||||||
|
const canonical = $derived(SITE + localizedPath(locale, path));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{title}</title>
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<link rel="canonical" href={canonical} />
|
||||||
|
|
||||||
|
{#each LOCALES as loc (loc)}
|
||||||
|
<link rel="alternate" hreflang={loc} href={SITE + localizedPath(loc, path)} />
|
||||||
|
{/each}
|
||||||
|
<link rel="alternate" hreflang="x-default" href={SITE + localizedPath('fr', path)} />
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content={canonical} />
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:locale" content={OG_LOCALES[locale]} />
|
||||||
|
<meta property="og:site_name" content="Sirius Gwada" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:title" content={title} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
</svelte:head>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
import type { Map as LeafletMap } from 'leaflet';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
import sites from '$lib/data/sites.js';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
|
||||||
|
let mapContainer: HTMLDivElement | undefined = $state();
|
||||||
|
let enhanced = $state(false);
|
||||||
|
|
||||||
|
function description(site: (typeof sites)[number]): string {
|
||||||
|
const desc = site as unknown as Record<string, string | undefined>;
|
||||||
|
return desc[`description_${locale}`] ?? site.description_fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
let map: LeafletMap | undefined;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Leaflet touche `window` à l'évaluation : import dynamique, jamais de SSR.
|
||||||
|
const L = await import('leaflet');
|
||||||
|
if (cancelled || !mapContainer) return;
|
||||||
|
|
||||||
|
const markerIcon = (active: boolean) =>
|
||||||
|
L.divIcon({
|
||||||
|
className: 'site-marker' + (active ? ' site-marker-active' : ''),
|
||||||
|
iconSize: active ? [22, 22] : [16, 16],
|
||||||
|
iconAnchor: active ? [11, 11] : [8, 8]
|
||||||
|
});
|
||||||
|
|
||||||
|
map = L.map(mapContainer, {
|
||||||
|
scrollWheelZoom: false
|
||||||
|
});
|
||||||
|
|
||||||
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution:
|
||||||
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||||
|
maxZoom: 18
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
const markers = sites.map((site) => {
|
||||||
|
const marker = L.marker([site.lat, site.lon], { icon: markerIcon(false) }).addTo(
|
||||||
|
map as LeafletMap
|
||||||
|
);
|
||||||
|
marker.bindPopup(
|
||||||
|
`<strong>${site.name}</strong><br>${description(site)}<br><small style="color: var(--ink-dim)">${site.lat}°N, ${site.lon}°O — ${site.alt} m</small>`
|
||||||
|
);
|
||||||
|
marker.on('popupopen', () => marker.setIcon(markerIcon(true)));
|
||||||
|
marker.on('popupclose', () => marker.setIcon(markerIcon(false)));
|
||||||
|
return marker;
|
||||||
|
});
|
||||||
|
|
||||||
|
const group = L.featureGroup(markers);
|
||||||
|
map.fitBounds(group.getBounds().pad(0.2));
|
||||||
|
enhanced = true;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
map?.remove();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="observation-map">
|
||||||
|
<div
|
||||||
|
bind:this={mapContainer}
|
||||||
|
class="leaflet-container-host"
|
||||||
|
role="group"
|
||||||
|
aria-label={t(locale, 'observer_map_title')}
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<!-- Repli no-JS : liste statique des sites, retirée une fois la carte montée. -->
|
||||||
|
{#if !enhanced}
|
||||||
|
<ul class="site-list grid-auto">
|
||||||
|
{#each sites as site (site.id)}
|
||||||
|
<li class="card">
|
||||||
|
<h3>{site.name}</h3>
|
||||||
|
<p>{description(site)}</p>
|
||||||
|
<p class="coords">{site.lat}°N, {site.lon}°O — {site.alt} m</p>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Hauteur fixe identique au legacy : réserve l'espace dès le SSR (zéro CLS). */
|
||||||
|
.leaflet-container-host {
|
||||||
|
width: 100%;
|
||||||
|
height: 22rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--surface-sunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-list {
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coords {
|
||||||
|
font-size: var(--text-0);
|
||||||
|
color: var(--ink-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.site-marker) {
|
||||||
|
background: var(--gold-surface);
|
||||||
|
border: 2px solid oklch(20% 0.03 265);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.site-marker-active) {
|
||||||
|
background: var(--caribbean-teal);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.leaflet-popup-content-wrapper) {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
color: var(--ink);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.leaflet-popup-tip) {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.observation-map :global(.leaflet-fade-anim),
|
||||||
|
.observation-map :global(.leaflet-popup),
|
||||||
|
.observation-map :global(.leaflet-zoom-anim) {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
import type { Map as LeafletMap } from 'leaflet';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
import waveCities from '$lib/data/waveCities.js';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
|
||||||
|
let mapContainer: HTMLDivElement | undefined = $state();
|
||||||
|
let enhanced = $state(false);
|
||||||
|
|
||||||
|
function isCaribbean(city: (typeof waveCities)[number]): boolean {
|
||||||
|
return city.name === 'Guadeloupe' || city.name === 'Martinique';
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
let map: LeafletMap | undefined;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Leaflet touche `window` à l'évaluation : import dynamique, jamais de SSR.
|
||||||
|
const L = await import('leaflet');
|
||||||
|
if (cancelled || !mapContainer) return;
|
||||||
|
|
||||||
|
const markerIcon = (active: boolean) =>
|
||||||
|
L.divIcon({
|
||||||
|
className: 'wave-marker' + (active ? ' wave-marker-active' : ''),
|
||||||
|
iconSize: active ? [20, 20] : [14, 14],
|
||||||
|
iconAnchor: active ? [10, 10] : [7, 7]
|
||||||
|
});
|
||||||
|
|
||||||
|
map = L.map(mapContainer, { scrollWheelZoom: false });
|
||||||
|
|
||||||
|
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution:
|
||||||
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||||
|
maxZoom: 18
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
const markers = waveCities.map((city) => {
|
||||||
|
const marker = L.marker([city.lat, city.lon], {
|
||||||
|
icon: markerIcon(isCaribbean(city))
|
||||||
|
}).addTo(map as LeafletMap);
|
||||||
|
marker.bindPopup(`<strong>${city.name}</strong><br>${city.date}`);
|
||||||
|
marker.on('popupopen', () => marker.setIcon(markerIcon(true)));
|
||||||
|
marker.on('popupclose', () => marker.setIcon(markerIcon(isCaribbean(city))));
|
||||||
|
return marker;
|
||||||
|
});
|
||||||
|
|
||||||
|
const group = L.featureGroup(markers);
|
||||||
|
map.fitBounds(group.getBounds().pad(0.15));
|
||||||
|
enhanced = true;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
map?.remove();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="sirius-globe">
|
||||||
|
<div
|
||||||
|
bind:this={mapContainer}
|
||||||
|
class="wave-map"
|
||||||
|
role="group"
|
||||||
|
aria-label={t(locale, 'globe_title')}
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<!-- Repli no-JS : liste statique de la vague (ville — date), retirée une
|
||||||
|
fois la carte montée. -->
|
||||||
|
{#if !enhanced}
|
||||||
|
<ul class="wave-cities grid-auto">
|
||||||
|
{#each waveCities as city (city.name)}
|
||||||
|
<li class="card">
|
||||||
|
<h3>{city.name}</h3>
|
||||||
|
<p class="wave-date">{city.date}</p>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Hauteur fixe identique au legacy : réserve l'espace dès le SSR (zéro CLS). */
|
||||||
|
.wave-map {
|
||||||
|
width: 100%;
|
||||||
|
height: 22rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--surface-sunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-cities {
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wave-date {
|
||||||
|
font-size: var(--text-0);
|
||||||
|
color: var(--ink-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.wave-marker) {
|
||||||
|
background: var(--caribbean-teal);
|
||||||
|
border: 2px solid oklch(20% 0.03 265);
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.wave-marker-active) {
|
||||||
|
background: var(--gold-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.leaflet-popup-content-wrapper) {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
color: var(--ink);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.leaflet-popup-tip) {
|
||||||
|
background: var(--surface-raised);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.sirius-globe :global(.leaflet-fade-anim),
|
||||||
|
.sirius-globe :global(.leaflet-popup),
|
||||||
|
.sirius-globe :global(.leaflet-zoom-anim) {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
import sites from '$lib/data/sites.js';
|
||||||
|
import nextRising from '$lib/data/nextRising.js';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
|
||||||
|
type Prediction = { date: string; time: string };
|
||||||
|
|
||||||
|
// Année du prochain lever héliaque (calculée au build, comme le 11ty legacy).
|
||||||
|
const { year } = nextRising();
|
||||||
|
|
||||||
|
let selectedId = $state(sites[0].id);
|
||||||
|
|
||||||
|
let selected = $derived(sites.find((s) => s.id === selectedId) ?? sites[0]);
|
||||||
|
let prediction = $derived(
|
||||||
|
(selected.predictions as unknown as Record<number, Prediction>)[year]
|
||||||
|
);
|
||||||
|
|
||||||
|
function predictionOf(site: (typeof sites)[number]): Prediction | undefined {
|
||||||
|
return (site.predictions as unknown as Record<number, Prediction>)[year];
|
||||||
|
}
|
||||||
|
|
||||||
|
function description(site: (typeof sites)[number]): string {
|
||||||
|
const desc = site as unknown as Record<string, string | undefined>;
|
||||||
|
return desc[`description_${locale}`] ?? site.description_fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Piège UTC-4 (NOTES_PROJET §3) : une date ISO sans heure est interprétée en
|
||||||
|
// UTC et peut reculer d'un jour une fois formatée en heure de Guadeloupe.
|
||||||
|
// Toujours parser les composants et construire la date en heure locale.
|
||||||
|
function formatDate(iso: string): string {
|
||||||
|
const [y, m, d] = iso.split('-').map(Number);
|
||||||
|
return new Date(y, m - 1, d).toLocaleDateString(locale === 'ht' ? 'fr' : locale, {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function windowLabel(iso: string): string {
|
||||||
|
const [y, m, d] = iso.split('-').map(Number);
|
||||||
|
const start = new Date(y, m - 1, d - 2);
|
||||||
|
const end = new Date(y, m - 1, d + 2);
|
||||||
|
const fmt = (date: Date) =>
|
||||||
|
date.toLocaleDateString(locale === 'ht' ? 'fr' : locale, {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'short'
|
||||||
|
});
|
||||||
|
return `${fmt(start)} – ${fmt(end)}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="calculator card">
|
||||||
|
<label class="calculator-label" for="site-select"
|
||||||
|
>{t(locale, 'observer_calculator_select_label')}</label
|
||||||
|
>
|
||||||
|
<select id="site-select" bind:value={selectedId} class="calculator-select">
|
||||||
|
{#each sites as site (site.id)}
|
||||||
|
<option value={site.id}>{site.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
{#if prediction}
|
||||||
|
<div class="calculator-result" aria-live="polite">
|
||||||
|
<p class="calculator-result-label">{t(locale, 'observer_calculator_result_heliacal')}</p>
|
||||||
|
<p class="calculator-result-value">{formatDate(prediction.date)} · {prediction.time}</p>
|
||||||
|
<p class="calculator-result-label">{t(locale, 'observer_calculator_result_window')}</p>
|
||||||
|
<p class="calculator-result-window">{windowLabel(prediction.date)}</p>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Table complète : toujours rendue (SSR), c'est le repli no-JS. À
|
||||||
|
l'hydratation, la ligne du site sélectionné est mise en valeur. -->
|
||||||
|
<div class="predictions-table card">
|
||||||
|
<table>
|
||||||
|
<caption class="visually-hidden">{t(locale, 'observer_calculator_title')}</caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">{t(locale, 'observer_calculator_table_site')}</th>
|
||||||
|
<th scope="col">{t(locale, 'observer_calculator_table_date')}</th>
|
||||||
|
<th scope="col">{t(locale, 'observer_calculator_table_time')}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each sites as site (site.id)}
|
||||||
|
{@const p = predictionOf(site)}
|
||||||
|
<tr class:selected={site.id === selectedId}>
|
||||||
|
<th scope="row">
|
||||||
|
{site.name}
|
||||||
|
<div class="site-desc">{description(site)}</div>
|
||||||
|
</th>
|
||||||
|
{#if p}
|
||||||
|
<td>{formatDate(p.date)}</td>
|
||||||
|
<td>{p.time}</td>
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.calculator {
|
||||||
|
max-width: 28rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-label {
|
||||||
|
display: block;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-select {
|
||||||
|
width: 100%;
|
||||||
|
min-height: var(--tap-target);
|
||||||
|
padding-inline: var(--space-3);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
background: var(--surface);
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: var(--text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-result {
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
padding-top: var(--space-4);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-result-label {
|
||||||
|
font-size: var(--text-0);
|
||||||
|
color: var(--ink-soft);
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-result-value {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: var(--text-3);
|
||||||
|
color: var(--gold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-result-window {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.predictions-table {
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr {
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 2px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr.selected {
|
||||||
|
background: var(--gold-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody th {
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-desc {
|
||||||
|
font-size: var(--text-0);
|
||||||
|
color: var(--ink-soft);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
tbody tr {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<p class="section-eyebrow">Wep Ronpet</p>
|
||||||
|
<h1>{t(locale, 'associations_title')}</h1>
|
||||||
|
<p class="pattern-strip bogolan" aria-hidden="true" style="margin-inline:auto"></p>
|
||||||
|
<p class="section-subtitle">{t(locale, 'associations_subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-auto">
|
||||||
|
<div class="card">
|
||||||
|
<h2>{t(locale, 'associations_events_title')}</h2>
|
||||||
|
<div class="stack" style="--stack-space: 0">
|
||||||
|
<a
|
||||||
|
class="event-link"
|
||||||
|
href="https://my.weezevent.com/nouvel-an-kamite"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="event-icon" aria-hidden="true">🎊</span>
|
||||||
|
<span class="event-info">
|
||||||
|
<span class="event-title">{t(locale, 'associations_event1_title')}</span>
|
||||||
|
<span class="event-platform">{t(locale, 'associations_event1_organizer')}</span>
|
||||||
|
<span>{t(locale, 'associations_event1_desc')}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="event-link"
|
||||||
|
href="https://my.weezevent.com/nouvel-an-kamit"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="event-icon" aria-hidden="true">✨</span>
|
||||||
|
<span class="event-info">
|
||||||
|
<span class="event-title">{t(locale, 'associations_event2_title')}</span>
|
||||||
|
<span class="event-platform">{t(locale, 'associations_event2_organizer')}</span>
|
||||||
|
<span>{t(locale, 'associations_event2_desc')}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>{t(locale, 'associations_community_title')}</h2>
|
||||||
|
<div class="stack" style="--stack-space: 0">
|
||||||
|
<a
|
||||||
|
class="community-link"
|
||||||
|
href="https://14eme-membre.net/site-web/#événements"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="community-icon" aria-hidden="true">🌍</span>
|
||||||
|
<span class="community-info">
|
||||||
|
<span class="community-title">{t(locale, 'associations_link1_title')}</span>
|
||||||
|
<span>{t(locale, 'associations_link1_desc')}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="community-link"
|
||||||
|
href="https://www.facebook.com/people/Mouvement-Politique-Spirituel-MUN"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<span class="community-icon" aria-hidden="true">📘</span>
|
||||||
|
<span class="community-info">
|
||||||
|
<span class="community-title">{t(locale, 'associations_link2_title')}</span>
|
||||||
|
<span>{t(locale, 'associations_link2_desc')}</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="center" style="margin-top: var(--space-7)">{t(locale, 'associations_video_title')}</h2>
|
||||||
|
<div class="video-frame card" style="margin-top: var(--space-4)">
|
||||||
|
<iframe
|
||||||
|
src="https://www.youtube.com/embed/0_8kWlDTf94"
|
||||||
|
title={t(locale, 'associations_video_title')}
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowfullscreen
|
||||||
|
loading="lazy"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="associations-note center">{t(locale, 'associations_note')}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<p class="section-eyebrow">Sirius Gwada</p>
|
||||||
|
<h1>{t(locale, 'culture_title')}</h1>
|
||||||
|
<p class="pattern-strip adinkra" aria-hidden="true" style="margin-inline:auto"></p>
|
||||||
|
<p class="section-subtitle">{t(locale, 'culture_subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-auto">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">🏺</div>
|
||||||
|
<h3>{t(locale, 'culture_egypt_title')}</h3>
|
||||||
|
<p>{t(locale, 'culture_egypt_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">🌍</div>
|
||||||
|
<h3>{t(locale, 'culture_dogon_title')}</h3>
|
||||||
|
<p>{t(locale, 'culture_dogon_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">⛵</div>
|
||||||
|
<h3>{t(locale, 'culture_caribbean_title')}</h3>
|
||||||
|
<p>{t(locale, 'culture_caribbean_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="center" style="margin-top: var(--space-7)">{t(locale, 'culture_timeline_title')}</h2>
|
||||||
|
<div class="timeline-v" style="margin-top: var(--space-5)">
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'culture_timeline_1_date')}</p>
|
||||||
|
<h4>{t(locale, 'culture_timeline_1_title')}</h4>
|
||||||
|
<p>{t(locale, 'culture_timeline_1_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'culture_timeline_2_date')}</p>
|
||||||
|
<h4>{t(locale, 'culture_timeline_2_title')}</h4>
|
||||||
|
<p>{t(locale, 'culture_timeline_2_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'culture_timeline_3_date')}</p>
|
||||||
|
<h4>{t(locale, 'culture_timeline_3_title')}</h4>
|
||||||
|
<p>{t(locale, 'culture_timeline_3_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'culture_timeline_4_date')}</p>
|
||||||
|
<h4>{t(locale, 'culture_timeline_4_title')}</h4>
|
||||||
|
<p>{t(locale, 'culture_timeline_4_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" style="background: var(--surface-sunk)">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<h2>{t(locale, 'dogon_title')}</h2>
|
||||||
|
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
|
||||||
|
<p class="section-subtitle">{t(locale, 'dogon_intro')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset class="tabs">
|
||||||
|
<legend class="visually-hidden">{t(locale, 'dogon_title')}</legend>
|
||||||
|
|
||||||
|
<input type="radio" name="dogon-tab" id="dogon-tab-cosmogony" class="tab-input" checked />
|
||||||
|
<input type="radio" name="dogon-tab" id="dogon-tab-system" class="tab-input" />
|
||||||
|
<input type="radio" name="dogon-tab" id="dogon-tab-astronomy" class="tab-input" />
|
||||||
|
<input type="radio" name="dogon-tab" id="dogon-tab-heritage" class="tab-input" />
|
||||||
|
|
||||||
|
<div class="tab-list" role="tablist">
|
||||||
|
<label class="tab-label" for="dogon-tab-cosmogony">{t(locale, 'dogon_tab_cosmogony')}</label>
|
||||||
|
<label class="tab-label" for="dogon-tab-system">{t(locale, 'dogon_tab_system')}</label>
|
||||||
|
<label class="tab-label" for="dogon-tab-astronomy">{t(locale, 'dogon_tab_astronomy')}</label>
|
||||||
|
<label class="tab-label" for="dogon-tab-heritage">{t(locale, 'dogon_tab_heritage')}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panels">
|
||||||
|
<div class="tab-panel prose center">
|
||||||
|
<p>{t(locale, 'dogon_cosmogony_p1')}</p>
|
||||||
|
<p>{t(locale, 'dogon_cosmogony_p2')}</p>
|
||||||
|
<div class="card">
|
||||||
|
<h3>{t(locale, 'dogon_cosmogony_nommo_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_cosmogony_nommo_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel">
|
||||||
|
<div class="grid-auto">
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_system_sigutolo_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_system_sigutolo_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_system_potolo_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_system_potolo_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_system_emmeya_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_system_emmeya_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card prose center" style="margin-top: var(--space-4)">
|
||||||
|
<h3>{t(locale, 'dogon_mystery_title')}</h3>
|
||||||
|
<div class="grid-auto">
|
||||||
|
<div>
|
||||||
|
<h4>{t(locale, 'dogon_mystery_theory1_title')}</h4>
|
||||||
|
<p>{t(locale, 'dogon_mystery_theory1_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>{t(locale, 'dogon_mystery_theory2_title')}</h4>
|
||||||
|
<p>{t(locale, 'dogon_mystery_theory2_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4>{t(locale, 'dogon_mystery_theory3_title')}</h4>
|
||||||
|
<p>{t(locale, 'dogon_mystery_theory3_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card prose center" style="margin-top: var(--space-4)">
|
||||||
|
<h3>{t(locale, 'dogon_calc_example_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_calc_example_text')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel">
|
||||||
|
<div class="grid-auto">
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_astronomy_geometry_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_astronomy_geometry_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_astronomy_calendar_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_astronomy_calendar_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_astronomy_planets_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_astronomy_planets_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'dogon_astronomy_universe_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_astronomy_universe_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel">
|
||||||
|
<div class="timeline-v">
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'dogon_heritage_1_date')}</p>
|
||||||
|
<p>{t(locale, 'dogon_heritage_1_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'dogon_heritage_2_date')}</p>
|
||||||
|
<p>{t(locale, 'dogon_heritage_2_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'dogon_heritage_3_date')}</p>
|
||||||
|
<p>{t(locale, 'dogon_heritage_3_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-v-item">
|
||||||
|
<p class="timeline-v-date">{t(locale, 'dogon_heritage_4_date')}</p>
|
||||||
|
<p>{t(locale, 'dogon_heritage_4_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card prose center" style="margin-top: var(--space-5)">
|
||||||
|
<h3>{t(locale, 'dogon_heritage_diaspora_title')}</h3>
|
||||||
|
<p>{t(locale, 'dogon_heritage_diaspora_desc')}</p>
|
||||||
|
<div class="grid-auto" style="margin-top: var(--space-3); text-align: left">
|
||||||
|
<p>🇭🇹 {t(locale, 'dogon_heritage_diaspora_haiti')}</p>
|
||||||
|
<p>🇬🇵 {t(locale, 'dogon_heritage_diaspora_guadeloupe')}</p>
|
||||||
|
<p>🇲🇶 {t(locale, 'dogon_heritage_diaspora_martinique')}</p>
|
||||||
|
<p>🇯🇲 {t(locale, 'dogon_heritage_diaspora_jamaica')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { resolve } from '$app/paths';
|
||||||
|
import nextRising from '$lib/data/nextRising.js';
|
||||||
|
import waveCities from '$lib/data/waveCities.js';
|
||||||
|
import { formatDate } from '$lib/dates';
|
||||||
|
import { localizedPath, t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page d'accueil — portage de legacy/src/index.njk. Factorisée ici ;
|
||||||
|
* les routes `/`, `/en/` et `/ht/` ne sont que des coquilles qui
|
||||||
|
* passent leur locale (et leurs métadonnées via <Seo>).
|
||||||
|
*/
|
||||||
|
interface Props {
|
||||||
|
locale: Locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale }: Props = $props();
|
||||||
|
|
||||||
|
// Calculé au build (prerender) — logique identique à l'ancien site.
|
||||||
|
const rising = nextRising();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-orbits" aria-hidden="true">
|
||||||
|
<div class="sun"></div>
|
||||||
|
<div class="orbit" style="width:5rem;height:5rem;animation-duration:6s"><span></span></div>
|
||||||
|
<div class="orbit" style="width:8rem;height:8rem;animation-duration:10s"><span></span></div>
|
||||||
|
<div class="orbit" style="width:11rem;height:11rem;animation-duration:14s"><span></span></div>
|
||||||
|
<div class="orbit" style="width:14rem;height:14rem;animation-duration:18s"><span></span></div>
|
||||||
|
<div class="orbit sirius" style="width:18rem;height:18rem;animation-duration:26s">
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container center hero-content">
|
||||||
|
<p class="section-eyebrow">{t(locale, 'hero_eyebrow')}</p>
|
||||||
|
<h1 class="hero-title">
|
||||||
|
<span>{t(locale, 'hero_title1')}</span>
|
||||||
|
<span class="accent">{t(locale, 'hero_title2')}</span>
|
||||||
|
</h1>
|
||||||
|
<p class="hero-subtitle">{t(locale, 'hero_subtitle')}</p>
|
||||||
|
<p class="hero-description">{t(locale, 'hero_description')}</p>
|
||||||
|
<p class="hero-date">
|
||||||
|
{t(locale, 'hero_date_label')}
|
||||||
|
<strong>{formatDate(rising.date, locale)} · {rising.time}</strong>
|
||||||
|
</p>
|
||||||
|
<div class="cluster hero-cta">
|
||||||
|
<a class="btn btn-primary comet-ring" href={resolve(localizedPath(locale, '/observer/'))}
|
||||||
|
>{t(locale, 'hero_cta_predictions')}</a
|
||||||
|
>
|
||||||
|
<a class="btn btn-outline" href={resolve(localizedPath(locale, '/sirius-science/'))}
|
||||||
|
>{t(locale, 'hero_cta_learn')}</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" id="intro">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<h2>{t(locale, 'home_intro_title')}</h2>
|
||||||
|
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
|
||||||
|
<p class="section-subtitle">{t(locale, 'home_intro_subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="prose center stack" style="--stack-space: var(--space-3)">
|
||||||
|
<!-- home_intro_p1 contient du <strong> (| safe dans le legacy) -->
|
||||||
|
<p>{@html t(locale, 'home_intro_p1')}</p>
|
||||||
|
<p>{t(locale, 'home_intro_p2')}</p>
|
||||||
|
<p>{t(locale, 'home_intro_p3')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-auto" style="margin-top: var(--space-6)">
|
||||||
|
<div class="feature-card reveal">
|
||||||
|
<h3>{t(locale, 'feature_brightest_title')}</h3>
|
||||||
|
<p>{t(locale, 'feature_brightest_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card reveal">
|
||||||
|
<h3>{t(locale, 'feature_rising_title')}</h3>
|
||||||
|
<p>{t(locale, 'feature_rising_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card reveal">
|
||||||
|
<h3>{t(locale, 'feature_heritage_title')}</h3>
|
||||||
|
<p>{t(locale, 'feature_heritage_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" style="background: var(--surface-sunk)">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<h2>{t(locale, 'wave_showcase_title')}</h2>
|
||||||
|
<p class="section-subtitle">{t(locale, 'wave_showcase_subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="wave-badges">
|
||||||
|
{#each waveCities as city, i (city.name)}
|
||||||
|
<li class="wave-badge wave-badge-{i % 3}">
|
||||||
|
<span class="wave-badge-name">{city.name}</span>
|
||||||
|
<span class="wave-badge-date">{city.date}</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class="center" style="margin-top: var(--space-5)">
|
||||||
|
<a class="btn btn-outline" href={resolve(localizedPath(locale, '/observer/'))}
|
||||||
|
>{t(locale, 'wave_showcase_cta')}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Reveal d'entrée au scroll, CSS pur : sans effet si animation-timeline
|
||||||
|
n'est pas supporté, désactivé en reduced-motion. */
|
||||||
|
@supports (animation-timeline: view()) {
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.reveal {
|
||||||
|
animation: reveal-rise linear both;
|
||||||
|
animation-timeline: view();
|
||||||
|
animation-range: entry 0% entry 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes reveal-rise {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(1.5rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
import SiriusPredictions from '$lib/components/islands/SiriusPredictions.svelte';
|
||||||
|
import ObservationMap from '$lib/components/islands/ObservationMap.svelte';
|
||||||
|
import SiriusGlobe from '$lib/components/islands/SiriusGlobe.svelte';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<p class="section-eyebrow">Sirius Gwada</p>
|
||||||
|
<h1>{t(locale, 'observer_title')}</h1>
|
||||||
|
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
|
||||||
|
<p class="section-subtitle">{t(locale, 'observer_subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="predictions-island">
|
||||||
|
<h2 class="center">{t(locale, 'observer_calculator_title')}</h2>
|
||||||
|
<p class="section-subtitle center">{t(locale, 'observer_calculator_subtitle')}</p>
|
||||||
|
|
||||||
|
<div style="margin-top: var(--space-4)">
|
||||||
|
<SiriusPredictions {locale} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="map-island" style="margin-top: var(--space-7)">
|
||||||
|
<h2 class="center">{t(locale, 'observer_map_title')}</h2>
|
||||||
|
<p class="section-subtitle center">{t(locale, 'observer_map_subtitle')}</p>
|
||||||
|
|
||||||
|
<div style="margin-top: var(--space-4)">
|
||||||
|
<ObservationMap {locale} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="center" style="margin-top: var(--space-7)">{t(locale, 'observer_timeline_title')}</h2>
|
||||||
|
<div class="timeline-h card" style="margin-top: var(--space-4)">
|
||||||
|
<div class="timeline-h-step">
|
||||||
|
<div class="timeline-h-icon" aria-hidden="true">🌙</div>
|
||||||
|
<p class="timeline-h-time">{t(locale, 'observer_phase1_time')}</p>
|
||||||
|
<h4>{t(locale, 'observer_phase1_title')}</h4>
|
||||||
|
<p>{t(locale, 'observer_phase1_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-h-step">
|
||||||
|
<div class="timeline-h-icon" aria-hidden="true">🔭</div>
|
||||||
|
<p class="timeline-h-time">{t(locale, 'observer_phase2_time')}</p>
|
||||||
|
<h4>{t(locale, 'observer_phase2_title')}</h4>
|
||||||
|
<p>{t(locale, 'observer_phase2_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-h-step">
|
||||||
|
<div class="timeline-h-icon" aria-hidden="true">⭐</div>
|
||||||
|
<p class="timeline-h-time">{t(locale, 'observer_phase3_time')}</p>
|
||||||
|
<h4>{t(locale, 'observer_phase3_title')}</h4>
|
||||||
|
<p>{t(locale, 'observer_phase3_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-h-step">
|
||||||
|
<div class="timeline-h-icon" aria-hidden="true">✨</div>
|
||||||
|
<p class="timeline-h-time">{t(locale, 'observer_phase4_time')}</p>
|
||||||
|
<h4>{t(locale, 'observer_phase4_title')}</h4>
|
||||||
|
<p>{t(locale, 'observer_phase4_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="center" style="margin-top: var(--space-7)">{t(locale, 'observer_equipment_title')}</h2>
|
||||||
|
<div class="grid-auto" style="margin-top: var(--space-4)">
|
||||||
|
<div class="card info-card essential">
|
||||||
|
<div class="info-card-header">
|
||||||
|
<h3>{t(locale, 'observer_equipment_lamp_name')}</h3>
|
||||||
|
<span class="badge">{t(locale, 'observer_equipment_essential_badge')}</span>
|
||||||
|
</div>
|
||||||
|
<p>{t(locale, 'observer_equipment_lamp_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card info-card essential">
|
||||||
|
<div class="info-card-header">
|
||||||
|
<h3>{t(locale, 'observer_equipment_compass_name')}</h3>
|
||||||
|
<span class="badge">{t(locale, 'observer_equipment_essential_badge')}</span>
|
||||||
|
</div>
|
||||||
|
<p>{t(locale, 'observer_equipment_compass_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card info-card essential">
|
||||||
|
<div class="info-card-header">
|
||||||
|
<h3>{t(locale, 'observer_equipment_clothes_name')}</h3>
|
||||||
|
<span class="badge">{t(locale, 'observer_equipment_essential_badge')}</span>
|
||||||
|
</div>
|
||||||
|
<p>{t(locale, 'observer_equipment_clothes_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card info-card">
|
||||||
|
<h3>{t(locale, 'observer_equipment_binoculars_name')}</h3>
|
||||||
|
<p>{t(locale, 'observer_equipment_binoculars_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card info-card">
|
||||||
|
<h3>{t(locale, 'observer_equipment_notebook_name')}</h3>
|
||||||
|
<p>{t(locale, 'observer_equipment_notebook_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="center" style="margin-top: var(--space-7)">{t(locale, 'observer_tips_title')}</h2>
|
||||||
|
<div class="grid-auto" style="margin-top: var(--space-4)">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">🌤️</div>
|
||||||
|
<h3>{t(locale, 'observer_tip_weather_title')}</h3>
|
||||||
|
<p>{t(locale, 'observer_tip_weather_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">🌑</div>
|
||||||
|
<h3>{t(locale, 'observer_tip_moon_title')}</h3>
|
||||||
|
<p>{t(locale, 'observer_tip_moon_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">📱</div>
|
||||||
|
<h3>{t(locale, 'observer_tip_apps_title')}</h3>
|
||||||
|
<p>{t(locale, 'observer_tip_apps_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div style="font-size: var(--text-4)" aria-hidden="true">👥</div>
|
||||||
|
<h3>{t(locale, 'observer_tip_group_title')}</h3>
|
||||||
|
<p>{t(locale, 'observer_tip_group_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section" id="globe-island" style="background: var(--surface-sunk)">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<h2>{t(locale, 'globe_title')}</h2>
|
||||||
|
<p class="section-subtitle">{t(locale, 'globe_p1')}</p>
|
||||||
|
</div>
|
||||||
|
<p class="prose center">{t(locale, 'globe_p2')}</p>
|
||||||
|
<div style="margin-top: var(--space-4)">
|
||||||
|
<SiriusGlobe {locale} />
|
||||||
|
</div>
|
||||||
|
<p class="center" style="color: var(--ink-soft); font-size: var(--text-0)">
|
||||||
|
{t(locale, 'globe_controls_hint')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
let { locale }: { locale: Locale } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-header center">
|
||||||
|
<p class="section-eyebrow">Sirius Gwada</p>
|
||||||
|
<h1>{t(locale, 'science_title')}</h1>
|
||||||
|
<p class="pattern-strip bogolan" aria-hidden="true" style="margin-inline:auto"></p>
|
||||||
|
<p class="section-subtitle">{t(locale, 'science_subtitle')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset class="tabs">
|
||||||
|
<legend class="visually-hidden">{t(locale, 'science_title')}</legend>
|
||||||
|
|
||||||
|
<input type="radio" name="science-tab" id="tab-mechanics" class="tab-input" checked />
|
||||||
|
<input type="radio" name="science-tab" id="tab-arcus" class="tab-input" />
|
||||||
|
<input type="radio" name="science-tab" id="tab-factors" class="tab-input" />
|
||||||
|
<input type="radio" name="science-tab" id="tab-calc" class="tab-input" />
|
||||||
|
|
||||||
|
<div class="tab-list" role="tablist">
|
||||||
|
<label class="tab-label" for="tab-mechanics">{t(locale, 'science_tab_mechanics')}</label>
|
||||||
|
<label class="tab-label" for="tab-arcus">{t(locale, 'science_tab_arcus')}</label>
|
||||||
|
<label class="tab-label" for="tab-factors">{t(locale, 'science_tab_factors')}</label>
|
||||||
|
<label class="tab-label" for="tab-calc">{t(locale, 'science_tab_calc')}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panels">
|
||||||
|
<div class="tab-panel prose center">
|
||||||
|
<h2>{t(locale, 'science_mechanics_title')}</h2>
|
||||||
|
<p>{t(locale, 'science_mechanics_p1')}</p>
|
||||||
|
<div class="diagram-orbit card" aria-hidden="true">
|
||||||
|
<div class="ring"></div>
|
||||||
|
<div class="body sun">☀️<span>{t(locale, 'science_diagram_sun')}</span></div>
|
||||||
|
<div class="body earth">🌍<span>{t(locale, 'science_diagram_earth')}</span></div>
|
||||||
|
<div class="body sirius">⭐<span>{t(locale, 'science_diagram_sirius')}</span></div>
|
||||||
|
</div>
|
||||||
|
<p>{t(locale, 'science_mechanics_p2')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel prose center">
|
||||||
|
<h2>{t(locale, 'science_arcus_title')}</h2>
|
||||||
|
<p>{t(locale, 'science_arcus_p1')}</p>
|
||||||
|
<div class="diagram-arcus card" aria-hidden="true">
|
||||||
|
<span class="horizon-label">{t(locale, 'science_arcus_horizon_label')}</span>
|
||||||
|
<div class="angle"></div>
|
||||||
|
<span class="angle-label">9-10°</span>
|
||||||
|
<span class="sirius-mark">⭐</span>
|
||||||
|
<span class="sun-mark">☀️</span>
|
||||||
|
</div>
|
||||||
|
<p>{t(locale, 'science_arcus_p2')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel">
|
||||||
|
<div class="grid-auto">
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'science_factor_magnitude_title')}</h3>
|
||||||
|
<p>{t(locale, 'science_factor_magnitude_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'science_factor_latitude_title')}</h3>
|
||||||
|
<p>{t(locale, 'science_factor_latitude_desc')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info-card">
|
||||||
|
<h3>{t(locale, 'science_factor_atmosphere_title')}</h3>
|
||||||
|
<p>{t(locale, 'science_factor_atmosphere_desc')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-panel prose center">
|
||||||
|
<h2>{t(locale, 'science_calc_title')}</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>{t(locale, 'science_calc_formula_label')}</h3>
|
||||||
|
<p style="font-family: ui-monospace, monospace">{t(locale, 'science_calc_formula')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top: var(--space-3)">
|
||||||
|
<h3>{t(locale, 'science_calc_step1_title')}</h3>
|
||||||
|
<p style="font-family: ui-monospace, monospace">{t(locale, 'science_calc_step1_value')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top: var(--space-3)">
|
||||||
|
<h3>{t(locale, 'science_calc_step2_title')}</h3>
|
||||||
|
<p style="font-family: ui-monospace, monospace">
|
||||||
|
{t(locale, 'science_calc_step2_ra')}<br />
|
||||||
|
{t(locale, 'science_calc_step2_dec')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top: var(--space-3)">
|
||||||
|
<h3>{t(locale, 'science_calc_arcus_title')}</h3>
|
||||||
|
<p>{t(locale, 'science_calc_arcus_desc')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-top: var(--space-3)">
|
||||||
|
<h3>{t(locale, 'science_calc_sources_title')}</h3>
|
||||||
|
<p>{t(locale, 'science_calc_sources_text')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -4,13 +4,15 @@ import sites from "./sites.js";
|
|||||||
// comme site de référence — les 7 sites ne divergent que de quelques minutes).
|
// comme site de référence — les 7 sites ne divergent que de quelques minutes).
|
||||||
export default function () {
|
export default function () {
|
||||||
const reference = sites[0];
|
const reference = sites[0];
|
||||||
|
/** @type {Record<number, { date: string; time: string }>} */
|
||||||
|
const predictions = reference.predictions;
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const years = Object.keys(reference.predictions)
|
const years = Object.keys(predictions)
|
||||||
.map(Number)
|
.map(Number)
|
||||||
.sort((a, b) => a - b);
|
.sort((a, b) => a - b);
|
||||||
|
|
||||||
for (const year of years) {
|
for (const year of years) {
|
||||||
const p = reference.predictions[year];
|
const p = predictions[year];
|
||||||
const when = new Date(`${p.date}T${p.time}:00`);
|
const when = new Date(`${p.date}T${p.time}:00`);
|
||||||
if (when >= today) {
|
if (when >= today) {
|
||||||
return { ...p, year };
|
return { ...p, year };
|
||||||
@@ -18,5 +20,5 @@ export default function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastYear = years[years.length - 1];
|
const lastYear = years[years.length - 1];
|
||||||
return { ...reference.predictions[lastYear], year: lastYear };
|
return { ...predictions[lastYear], year: lastYear };
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import type { Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formate une date ISO `YYYY-MM-DD` pour l'affichage.
|
||||||
|
*
|
||||||
|
* Piège documenté (NOTES_PROJET §3) : `new Date("2026-07-22")` est parsé en
|
||||||
|
* UTC minuit, ce qui peut reculer d'un jour une fois formaté dans un fuseau
|
||||||
|
* derrière UTC (Guadeloupe, UTC-4). On construit donc la date à partir de
|
||||||
|
* ses composants, en heure locale — logique reprise à l'identique du filtre
|
||||||
|
* `formatDate` de l'ancien eleventy.config.js.
|
||||||
|
*/
|
||||||
|
export function formatDate(
|
||||||
|
isoDate: string,
|
||||||
|
locale: Locale,
|
||||||
|
options: Intl.DateTimeFormatOptions = {}
|
||||||
|
): string {
|
||||||
|
const [year, month, day] = isoDate.split('-').map(Number);
|
||||||
|
return new Date(year, month - 1, day).toLocaleDateString(locale === 'ht' ? 'fr' : locale, {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
"meta_title": "Sirius in Guadeloupe — Wep Ronpet, the heliacal rising",
|
"meta_title": "Sirius in Guadeloupe — Wep Ronpet, the heliacal rising",
|
||||||
"meta_description": "Calculate and understand the heliacal rising of Sirius in Guadeloupe: astronomy, Egyptian and Dogon heritage, observation guide.",
|
"meta_description": "Calculate and understand the heliacal rising of Sirius in Guadeloupe: astronomy, Egyptian and Dogon heritage, observation guide.",
|
||||||
|
"meta_science_title": "Sirius & Science",
|
||||||
|
"meta_science_description": "The mechanics of Sirius's heliacal rising explained: Arcus Visionis, visibility factors and calculation method for Guadeloupe.",
|
||||||
|
"meta_culture_title": "Culture & Knowledge",
|
||||||
|
"meta_culture_description": "Sirius across cultures: ancient Egypt, Dogon astronomical knowledge and the Caribbean heritage of Wep Ronpet.",
|
||||||
|
"meta_observer_title": "Observe",
|
||||||
|
"meta_observer_description": "Dates and times of Sirius's heliacal rising for 7 observation sites in Guadeloupe, map, equipment and practical tips.",
|
||||||
|
"meta_associations_title": "Associations celebrating Wep Ronpet",
|
||||||
|
"meta_associations_description": "Events, communities and associations celebrating Wep Ronpet, the Kamit New Year, in Guadeloupe and beyond.",
|
||||||
|
|
||||||
"skip_to_content": "Skip to content",
|
"skip_to_content": "Skip to content",
|
||||||
"lang_name_fr": "Français",
|
"lang_name_fr": "Français",
|
||||||
@@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
"meta_title": "Sirius en Guadeloupe — Wep Ronpet, le lever héliaque",
|
"meta_title": "Sirius en Guadeloupe — Wep Ronpet, le lever héliaque",
|
||||||
"meta_description": "Calculez et comprenez le lever héliaque de Sirius en Guadeloupe : astronomie, héritage égyptien et dogon, guide d'observation.",
|
"meta_description": "Calculez et comprenez le lever héliaque de Sirius en Guadeloupe : astronomie, héritage égyptien et dogon, guide d'observation.",
|
||||||
|
"meta_science_title": "Sirius & Science",
|
||||||
|
"meta_science_description": "La mécanique du lever héliaque de Sirius expliquée : Arcus Visionis, facteurs de visibilité et méthode de calcul pour la Guadeloupe.",
|
||||||
|
"meta_culture_title": "Culture & Savoirs",
|
||||||
|
"meta_culture_description": "Sirius dans les cultures : Égypte antique, savoirs astronomiques dogon et héritage caribéen du Wep Ronpet.",
|
||||||
|
"meta_observer_title": "Observer",
|
||||||
|
"meta_observer_description": "Dates et heures du lever héliaque de Sirius pour 7 sites d'observation en Guadeloupe, carte, équipement et conseils pratiques.",
|
||||||
|
"meta_associations_title": "Associations qui fêtent le Wep Ronpet",
|
||||||
|
"meta_associations_description": "Événements, communautés et associations qui célèbrent le Wep Ronpet, le Nouvel An kamite, en Guadeloupe et au-delà.",
|
||||||
|
|
||||||
"skip_to_content": "Aller au contenu",
|
"skip_to_content": "Aller au contenu",
|
||||||
"lang_name_fr": "Français",
|
"lang_name_fr": "Français",
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* i18n — catalogues Paraglide (format message-format JSON) consommés
|
||||||
|
* directement, routage par dossiers : `/` = fr, `/en/`, `/ht/`.
|
||||||
|
* Le créole (ht) couvre le chrome ; le contenu long retombe sur le fr
|
||||||
|
* (fallback documenté dans NOTES_PROJET).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import en from './en.json';
|
||||||
|
import fr from './fr.json';
|
||||||
|
import ht from './ht.json';
|
||||||
|
import type { Pathname } from '$app/types';
|
||||||
|
|
||||||
|
export const LOCALES = ['fr', 'en', 'ht'] as const;
|
||||||
|
export type Locale = (typeof LOCALES)[number];
|
||||||
|
export const DEFAULT_LOCALE: Locale = 'fr';
|
||||||
|
|
||||||
|
type Messages = Record<string, string>;
|
||||||
|
|
||||||
|
const BUNDLES: Record<Locale, Messages> = {
|
||||||
|
fr: fr as Messages,
|
||||||
|
en: en as Messages,
|
||||||
|
ht: ht as Messages
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isLocale(value: string): value is Locale {
|
||||||
|
return (LOCALES as readonly string[]).includes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Locale déduite du chemin (`/en/...` → en, `/ht/...` → ht, sinon fr). */
|
||||||
|
export function localeFromPath(pathname: string): Locale {
|
||||||
|
const segment = pathname.split('/').filter(Boolean)[0] ?? '';
|
||||||
|
return isLocale(segment) ? segment : DEFAULT_LOCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMessages(locale: Locale): Messages {
|
||||||
|
return BUNDLES[locale] ?? BUNDLES[DEFAULT_LOCALE];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traduit une clé : locale demandée, fallback fr, fallback clé.
|
||||||
|
* Interpolation `{name}` via `vars`.
|
||||||
|
*/
|
||||||
|
export function t(locale: Locale, key: string, vars?: Record<string, string | number>): string {
|
||||||
|
const raw = BUNDLES[locale]?.[key] ?? BUNDLES[DEFAULT_LOCALE][key] ?? key;
|
||||||
|
if (!vars) return raw;
|
||||||
|
return raw.replace(/\{(\w+)\}/g, (_, name) => String(vars[name] ?? `{${name}}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Préfixe d'URL d'une locale (`''` pour fr, `/en`, `/ht`). */
|
||||||
|
export function localePrefix(locale: Locale): string {
|
||||||
|
return locale === DEFAULT_LOCALE ? '' : `/${locale}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Chemin localisé (`/observer/` + en → `/en/observer/`). */
|
||||||
|
export function localizedPath(locale: Locale, path: string): Pathname {
|
||||||
|
const prefix = localePrefix(locale);
|
||||||
|
return `${prefix}${path === '/' ? '/' : path}` as Pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Chemin de la même page dans une autre locale. */
|
||||||
|
export function switchLocalePath(pathname: string, target: Locale): Pathname {
|
||||||
|
const current = localeFromPath(pathname);
|
||||||
|
const prefix = localePrefix(current);
|
||||||
|
const bare = prefix ? pathname.slice(prefix.length) : pathname;
|
||||||
|
return (localePrefix(target) + (bare || '/')) as Pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Libellé natif d'une locale (lang_name_*). */
|
||||||
|
export function localeLabel(locale: Locale): string {
|
||||||
|
return t(locale, `lang_name_${locale}`);
|
||||||
|
}
|
||||||
@@ -1,26 +1,6 @@
|
|||||||
@font-face {
|
/* Les @font-face vivent dans /fonts/fonts.css (static/), chargé par app.html —
|
||||||
font-family: "Fraunces Variable";
|
ne pas les dupliquer ici (l'ancien build 11ty servait les polices en double,
|
||||||
src: url("/assets/fonts/fraunces-variable.woff2") format("woff2-variations");
|
sous /assets/fonts/, avec des URLs non préfixées par le base path). */
|
||||||
font-weight: 300 900;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Atkinson Hyperlegible";
|
|
||||||
src: url("/assets/fonts/atkinson-400.woff2") format("woff2");
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: "Atkinson Hyperlegible";
|
|
||||||
src: url("/assets/fonts/atkinson-700.woff2") format("woff2");
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
---
|
|
||||||
pagination:
|
|
||||||
data: locales
|
|
||||||
size: 1
|
|
||||||
alias: locale
|
|
||||||
permalink: "{% if locale == baseLocale %}/observer/index.html{% else %}/{{ locale }}/observer/index.html{% endif %}"
|
|
||||||
layout: layouts/base.njk
|
|
||||||
pageKey: observer
|
|
||||||
slug: "observer/"
|
|
||||||
eleventyComputed:
|
|
||||||
pageTitle: "{{ 'observer_title' | t(locale) }}"
|
|
||||||
---
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<p class="section-eyebrow">Sirius Gwada</p>
|
|
||||||
<h1>{{ "observer_title" | t(locale) }}</h1>
|
|
||||||
<p class="pattern-strip kente" aria-hidden="true" style="margin-inline:auto"></p>
|
|
||||||
<p class="section-subtitle">{{ "observer_subtitle" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="application/json" id="sites-data">{{ sites | dump | safe }}</script>
|
|
||||||
|
|
||||||
<div id="predictions-island" data-island="predictions">
|
|
||||||
<h2 class="center">{{ "observer_calculator_title" | t(locale) }}</h2>
|
|
||||||
<p class="section-subtitle center">{{ "observer_calculator_subtitle" | t(locale) }}</p>
|
|
||||||
|
|
||||||
<div class="island-app" data-year="{{ nextRising.year }}" data-locale="{{ locale }}"
|
|
||||||
data-select-label="{{ 'observer_calculator_select_label' | t(locale) }}"
|
|
||||||
data-heliacal-label="{{ 'observer_calculator_result_heliacal' | t(locale) }}"
|
|
||||||
data-window-label="{{ 'observer_calculator_result_window' | t(locale) }}"
|
|
||||||
style="margin-top: var(--space-4)"></div>
|
|
||||||
|
|
||||||
<div class="island-fallback card" style="overflow-x: auto; margin-top: var(--space-4)">
|
|
||||||
<table style="width: 100%; border-collapse: collapse">
|
|
||||||
<caption class="visually-hidden">{{ "observer_calculator_title" | t(locale) }}</caption>
|
|
||||||
<thead>
|
|
||||||
<tr style="text-align: left; border-bottom: 2px solid var(--border)">
|
|
||||||
<th style="padding: var(--space-2)" scope="col">{{ "observer_calculator_table_site" | t(locale) }}</th>
|
|
||||||
<th style="padding: var(--space-2)" scope="col">{{ "observer_calculator_table_date" | t(locale) }}</th>
|
|
||||||
<th style="padding: var(--space-2)" scope="col">{{ "observer_calculator_table_time" | t(locale) }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for site in sites %}
|
|
||||||
<tr style="border-bottom: 1px solid var(--border)">
|
|
||||||
<th scope="row" style="padding: var(--space-2); font-weight: 600; text-align: left">
|
|
||||||
{{ site.name }}
|
|
||||||
<div style="font-size: var(--text-0); color: var(--ink-soft); font-weight: 400">
|
|
||||||
{{ site["description_" + locale] or site.description_fr }}
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<td style="padding: var(--space-2)">{{ site.predictions[nextRising.year].date | formatDate(locale) }}</td>
|
|
||||||
<td style="padding: var(--space-2)">{{ site.predictions[nextRising.year].time }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="map-island" data-island="map" style="margin-top: var(--space-7)">
|
|
||||||
<h2 class="center">{{ "observer_map_title" | t(locale) }}</h2>
|
|
||||||
<p class="section-subtitle center">{{ "observer_map_subtitle" | t(locale) }}</p>
|
|
||||||
|
|
||||||
<div class="island-app" style="margin-top: var(--space-4)"></div>
|
|
||||||
|
|
||||||
<ul class="island-fallback grid-auto" style="margin-top: var(--space-4); list-style: none">
|
|
||||||
{% for site in sites %}
|
|
||||||
<li class="card">
|
|
||||||
<h3>{{ site.name }}</h3>
|
|
||||||
<p>{{ site["description_" + locale] or site.description_fr }}</p>
|
|
||||||
<p style="font-size: var(--text-0); color: var(--ink-dim)">{{ site.lat }}°N, {{ site.lon }}°O — {{ site.alt }} m</p>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="center" style="margin-top: var(--space-7)">{{ "observer_timeline_title" | t(locale) }}</h2>
|
|
||||||
<div class="timeline-h card" style="margin-top: var(--space-4)">
|
|
||||||
<div class="timeline-h-step">
|
|
||||||
<div class="timeline-h-icon" aria-hidden="true">🌙</div>
|
|
||||||
<p class="timeline-h-time">{{ "observer_phase1_time" | t(locale) }}</p>
|
|
||||||
<h4>{{ "observer_phase1_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "observer_phase1_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-h-step">
|
|
||||||
<div class="timeline-h-icon" aria-hidden="true">🔭</div>
|
|
||||||
<p class="timeline-h-time">{{ "observer_phase2_time" | t(locale) }}</p>
|
|
||||||
<h4>{{ "observer_phase2_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "observer_phase2_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-h-step">
|
|
||||||
<div class="timeline-h-icon" aria-hidden="true">⭐</div>
|
|
||||||
<p class="timeline-h-time">{{ "observer_phase3_time" | t(locale) }}</p>
|
|
||||||
<h4>{{ "observer_phase3_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "observer_phase3_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="timeline-h-step">
|
|
||||||
<div class="timeline-h-icon" aria-hidden="true">✨</div>
|
|
||||||
<p class="timeline-h-time">{{ "observer_phase4_time" | t(locale) }}</p>
|
|
||||||
<h4>{{ "observer_phase4_title" | t(locale) }}</h4>
|
|
||||||
<p>{{ "observer_phase4_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="center" style="margin-top: var(--space-7)">{{ "observer_equipment_title" | t(locale) }}</h2>
|
|
||||||
<div class="grid-auto" style="margin-top: var(--space-4)">
|
|
||||||
<div class="card info-card essential">
|
|
||||||
<div class="info-card-header">
|
|
||||||
<h3>{{ "observer_equipment_lamp_name" | t(locale) }}</h3>
|
|
||||||
<span class="badge">{{ "observer_equipment_essential_badge" | t(locale) }}</span>
|
|
||||||
</div>
|
|
||||||
<p>{{ "observer_equipment_lamp_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="card info-card essential">
|
|
||||||
<div class="info-card-header">
|
|
||||||
<h3>{{ "observer_equipment_compass_name" | t(locale) }}</h3>
|
|
||||||
<span class="badge">{{ "observer_equipment_essential_badge" | t(locale) }}</span>
|
|
||||||
</div>
|
|
||||||
<p>{{ "observer_equipment_compass_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="card info-card essential">
|
|
||||||
<div class="info-card-header">
|
|
||||||
<h3>{{ "observer_equipment_clothes_name" | t(locale) }}</h3>
|
|
||||||
<span class="badge">{{ "observer_equipment_essential_badge" | t(locale) }}</span>
|
|
||||||
</div>
|
|
||||||
<p>{{ "observer_equipment_clothes_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="card info-card">
|
|
||||||
<h3>{{ "observer_equipment_binoculars_name" | t(locale) }}</h3>
|
|
||||||
<p>{{ "observer_equipment_binoculars_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="card info-card">
|
|
||||||
<h3>{{ "observer_equipment_notebook_name" | t(locale) }}</h3>
|
|
||||||
<p>{{ "observer_equipment_notebook_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="center" style="margin-top: var(--space-7)">{{ "observer_tips_title" | t(locale) }}</h2>
|
|
||||||
<div class="grid-auto" style="margin-top: var(--space-4)">
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">🌤️</div>
|
|
||||||
<h3>{{ "observer_tip_weather_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "observer_tip_weather_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">🌑</div>
|
|
||||||
<h3>{{ "observer_tip_moon_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "observer_tip_moon_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">📱</div>
|
|
||||||
<h3>{{ "observer_tip_apps_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "observer_tip_apps_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<div style="font-size: var(--text-4)" aria-hidden="true">👥</div>
|
|
||||||
<h3>{{ "observer_tip_group_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "observer_tip_group_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" id="globe-island" data-island="globe" style="background: var(--surface-sunk)">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<h2>{{ "globe_title" | t(locale) }}</h2>
|
|
||||||
<p class="section-subtitle">{{ "globe_p1" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<p class="prose center">{{ "globe_p2" | t(locale) }}</p>
|
|
||||||
<div class="island-app" style="margin-top: var(--space-4)"></div>
|
|
||||||
<p class="center" style="color: var(--ink-soft); font-size: var(--text-0)">{{ "globe_controls_hint" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<script type="module" src="/src/islands/predictions/mount.js"></script>
|
|
||||||
<script type="module" src="/src/islands/map/mount.js"></script>
|
|
||||||
<script type="module" src="/src/islands/globe/mount.js"></script>
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import '$lib/styles/app.css';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
import { page } from '$app/state';
|
||||||
|
import Footer from '$lib/components/Footer.svelte';
|
||||||
|
import Nav from '$lib/components/Nav.svelte';
|
||||||
|
import { localeFromPath, t } from '$lib/i18n';
|
||||||
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: Snippet;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { children }: Props = $props();
|
||||||
|
|
||||||
|
// page.url.pathname inclut le base path (/gwada-sirius) : on le retire
|
||||||
|
// avant de déduire la locale du premier segment.
|
||||||
|
const locale = $derived(
|
||||||
|
localeFromPath(
|
||||||
|
page.url.pathname.startsWith(base)
|
||||||
|
? page.url.pathname.slice(base.length) || '/'
|
||||||
|
: page.url.pathname
|
||||||
|
)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a class="skip-link" href="#main-content">{t(locale, 'skip_to_content')}</a>
|
||||||
|
|
||||||
|
<Nav {locale} />
|
||||||
|
|
||||||
|
<main id="main-content">
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<Footer {locale} />
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const prerender = true;
|
||||||
|
export const trailingSlash = 'always';
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Home from '$lib/components/pages/Home.svelte';
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'fr';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title="{t(locale, 'hero_title1')} {t(locale, 'hero_title2')} — {t(locale, 'meta_title')}"
|
||||||
|
description={t(locale, 'meta_description')}
|
||||||
|
path="/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Home {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Associations from '$lib/components/pages/Associations.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'fr';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_associations_title')}
|
||||||
|
description={t(locale, 'meta_associations_description')}
|
||||||
|
path="/associations/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Associations {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Culture from '$lib/components/pages/Culture.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'fr';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_culture_title')}
|
||||||
|
description={t(locale, 'meta_culture_description')}
|
||||||
|
path="/culture-savoirs/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Culture {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Home from '$lib/components/pages/Home.svelte';
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'en';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title="{t(locale, 'hero_title1')} {t(locale, 'hero_title2')} — {t(locale, 'meta_title')}"
|
||||||
|
description={t(locale, 'meta_description')}
|
||||||
|
path="/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Home {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Associations from '$lib/components/pages/Associations.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'en';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_associations_title')}
|
||||||
|
description={t(locale, 'meta_associations_description')}
|
||||||
|
path="/associations/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Associations {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Culture from '$lib/components/pages/Culture.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'en';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_culture_title')}
|
||||||
|
description={t(locale, 'meta_culture_description')}
|
||||||
|
path="/culture-savoirs/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Culture {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Observer from '$lib/components/pages/Observer.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'en';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_observer_title')}
|
||||||
|
description={t(locale, 'meta_observer_description')}
|
||||||
|
path="/observer/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Observer {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Science from '$lib/components/pages/Science.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'en';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_science_title')}
|
||||||
|
description={t(locale, 'meta_science_description')}
|
||||||
|
path="/sirius-science/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Science {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Home from '$lib/components/pages/Home.svelte';
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'ht';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title="{t(locale, 'hero_title1')} {t(locale, 'hero_title2')} — {t(locale, 'meta_title')}"
|
||||||
|
description={t(locale, 'meta_description')}
|
||||||
|
path="/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Home {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Associations from '$lib/components/pages/Associations.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'ht';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_associations_title')}
|
||||||
|
description={t(locale, 'meta_associations_description')}
|
||||||
|
path="/associations/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Associations {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Culture from '$lib/components/pages/Culture.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'ht';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_culture_title')}
|
||||||
|
description={t(locale, 'meta_culture_description')}
|
||||||
|
path="/culture-savoirs/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Culture {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Observer from '$lib/components/pages/Observer.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'ht';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_observer_title')}
|
||||||
|
description={t(locale, 'meta_observer_description')}
|
||||||
|
path="/observer/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Observer {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Science from '$lib/components/pages/Science.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'ht';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_science_title')}
|
||||||
|
description={t(locale, 'meta_science_description')}
|
||||||
|
path="/sirius-science/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Science {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Observer from '$lib/components/pages/Observer.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'fr';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_observer_title')}
|
||||||
|
description={t(locale, 'meta_observer_description')}
|
||||||
|
path="/observer/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Observer {locale} />
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Seo from '$lib/components/Seo.svelte';
|
||||||
|
import Science from '$lib/components/pages/Science.svelte';
|
||||||
|
import { t, type Locale } from '$lib/i18n';
|
||||||
|
|
||||||
|
const locale: Locale = 'fr';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Seo
|
||||||
|
{locale}
|
||||||
|
title={t(locale, 'meta_science_title')}
|
||||||
|
description={t(locale, 'meta_science_description')}
|
||||||
|
path="/sirius-science/"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Science {locale} />
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Service worker minimal : résilience réseau (3G/4G irrégulier) et usage
|
||||||
|
// hors-ligne des pages déjà visitées. Stratégie stale-while-revalidate.
|
||||||
|
// NOTES_PROJET §5 : incrémenter CACHE_NAME à chaque évolution du SW.
|
||||||
|
const CACHE_NAME = "gwada-sirius-v3";
|
||||||
|
|
||||||
|
self.addEventListener("install", () => {
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("activate", (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches
|
||||||
|
.keys()
|
||||||
|
.then((keys) =>
|
||||||
|
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
self.clients.claim();
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener("fetch", (event) => {
|
||||||
|
if (event.request.method !== "GET") return;
|
||||||
|
const url = new URL(event.request.url);
|
||||||
|
if (url.origin !== self.location.origin) return;
|
||||||
|
|
||||||
|
event.respondWith(
|
||||||
|
caches.open(CACHE_NAME).then(async (cache) => {
|
||||||
|
const cached = await cache.match(event.request);
|
||||||
|
const network = fetch(event.request)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) cache.put(event.request, response.clone());
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(() => cached);
|
||||||
|
|
||||||
|
return cached || network;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
---
|
|
||||||
pagination:
|
|
||||||
data: locales
|
|
||||||
size: 1
|
|
||||||
alias: locale
|
|
||||||
permalink: "{% if locale == baseLocale %}/sirius-science/index.html{% else %}/{{ locale }}/sirius-science/index.html{% endif %}"
|
|
||||||
layout: layouts/base.njk
|
|
||||||
pageKey: science
|
|
||||||
slug: "sirius-science/"
|
|
||||||
eleventyComputed:
|
|
||||||
pageTitle: "{{ 'science_title' | t(locale) }}"
|
|
||||||
---
|
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="section-header center">
|
|
||||||
<p class="section-eyebrow">Sirius Gwada</p>
|
|
||||||
<h1>{{ "science_title" | t(locale) }}</h1>
|
|
||||||
<p class="pattern-strip bogolan" aria-hidden="true" style="margin-inline:auto"></p>
|
|
||||||
<p class="section-subtitle">{{ "science_subtitle" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset class="tabs">
|
|
||||||
<legend class="visually-hidden">{{ "science_title" | t(locale) }}</legend>
|
|
||||||
|
|
||||||
<input type="radio" name="science-tab" id="tab-mechanics" class="tab-input" checked>
|
|
||||||
<input type="radio" name="science-tab" id="tab-arcus" class="tab-input">
|
|
||||||
<input type="radio" name="science-tab" id="tab-factors" class="tab-input">
|
|
||||||
<input type="radio" name="science-tab" id="tab-calc" class="tab-input">
|
|
||||||
|
|
||||||
<div class="tab-list" role="tablist">
|
|
||||||
<label class="tab-label" for="tab-mechanics">{{ "science_tab_mechanics" | t(locale) }}</label>
|
|
||||||
<label class="tab-label" for="tab-arcus">{{ "science_tab_arcus" | t(locale) }}</label>
|
|
||||||
<label class="tab-label" for="tab-factors">{{ "science_tab_factors" | t(locale) }}</label>
|
|
||||||
<label class="tab-label" for="tab-calc">{{ "science_tab_calc" | t(locale) }}</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panels">
|
|
||||||
<div class="tab-panel prose center">
|
|
||||||
<h2>{{ "science_mechanics_title" | t(locale) }}</h2>
|
|
||||||
<p>{{ "science_mechanics_p1" | t(locale) }}</p>
|
|
||||||
<div class="diagram-orbit card" aria-hidden="true">
|
|
||||||
<div class="ring"></div>
|
|
||||||
<div class="body sun">☀️<span>{{ "science_diagram_sun" | t(locale) }}</span></div>
|
|
||||||
<div class="body earth">🌍<span>{{ "science_diagram_earth" | t(locale) }}</span></div>
|
|
||||||
<div class="body sirius">⭐<span>{{ "science_diagram_sirius" | t(locale) }}</span></div>
|
|
||||||
</div>
|
|
||||||
<p>{{ "science_mechanics_p2" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panel prose center">
|
|
||||||
<h2>{{ "science_arcus_title" | t(locale) }}</h2>
|
|
||||||
<p>{{ "science_arcus_p1" | t(locale) }}</p>
|
|
||||||
<div class="diagram-arcus card" aria-hidden="true">
|
|
||||||
<span class="horizon-label">{{ "science_arcus_horizon_label" | t(locale) }}</span>
|
|
||||||
<div class="angle"></div>
|
|
||||||
<span class="angle-label">9-10°</span>
|
|
||||||
<span class="sirius-mark">⭐</span>
|
|
||||||
<span class="sun-mark">☀️</span>
|
|
||||||
</div>
|
|
||||||
<p>{{ "science_arcus_p2" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panel">
|
|
||||||
<div class="grid-auto">
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "science_factor_magnitude_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "science_factor_magnitude_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "science_factor_latitude_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "science_factor_latitude_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="info-card">
|
|
||||||
<h3>{{ "science_factor_atmosphere_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "science_factor_atmosphere_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab-panel prose center">
|
|
||||||
<h2>{{ "science_calc_title" | t(locale) }}</h2>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h3>{{ "science_calc_formula_label" | t(locale) }}</h3>
|
|
||||||
<p style="font-family: ui-monospace, monospace">{{ "science_calc_formula" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card" style="margin-top: var(--space-3)">
|
|
||||||
<h3>{{ "science_calc_step1_title" | t(locale) }}</h3>
|
|
||||||
<p style="font-family: ui-monospace, monospace">{{ "science_calc_step1_value" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card" style="margin-top: var(--space-3)">
|
|
||||||
<h3>{{ "science_calc_step2_title" | t(locale) }}</h3>
|
|
||||||
<p style="font-family: ui-monospace, monospace">
|
|
||||||
{{ "science_calc_step2_ra" | t(locale) }}<br>
|
|
||||||
{{ "science_calc_step2_dec" | t(locale) }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card" style="margin-top: var(--space-3)">
|
|
||||||
<h3>{{ "science_calc_arcus_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "science_calc_arcus_desc" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card" style="margin-top: var(--space-3)">
|
|
||||||
<h3>{{ "science_calc_sources_title" | t(locale) }}</h3>
|
|
||||||
<p>{{ "science_calc_sources_text" | t(locale) }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@@ -9,12 +9,13 @@ if [ ! -d "node_modules" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🚀 Lancement du serveur de développement..."
|
echo "🚀 Lancement du serveur de développement..."
|
||||||
echo "📱 L'application sera accessible sur http://localhost:8080"
|
echo "📱 L'application sera accessible sur http://localhost:5173/gwada-sirius/"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Commandes disponibles:"
|
echo "Commandes disponibles:"
|
||||||
echo " - Ctrl+C : Arrêter le serveur"
|
echo " - Ctrl+C : Arrêter le serveur"
|
||||||
echo " - npm run build : Créer une version de production (11ty + Vite -> _site/)"
|
echo " - npm run build : Créer une version de production (SvelteKit -> build/)"
|
||||||
echo " - npm run preview : Prévisualiser la version de production"
|
echo " - npm run preview : Prévisualiser la version de production"
|
||||||
|
echo " - npm run check : Vérifier types et accessibilité"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 234 B After Width: | Height: | Size: 234 B |
@@ -0,0 +1,22 @@
|
|||||||
|
/* Fraunces variable (display, titres) + Atkinson Hyperlegible (corps) — auto-hébergées */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Fraunces';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/gwada-sirius/fonts/fraunces-variable.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Atkinson Hyperlegible';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/gwada-sirius/fonts/atkinson-400.woff2') format('woff2');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Atkinson Hyperlegible';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url('/gwada-sirius/fonts/atkinson-700.woff2') format('woff2');
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "Sirius en Guadeloupe — Wep Ronpet",
|
||||||
|
"short_name": "Sirius Gwada",
|
||||||
|
"description": "Lever héliaque de Sirius en Guadeloupe : astronomie, culture et observation.",
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#141a33",
|
||||||
|
"theme_color": "#141a33",
|
||||||
|
"icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: https://cyber-mawonaj.github.io/gwada-sirius/sitemap.xml
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/sirius-science/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/culture-savoirs/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/observer/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/associations/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/en/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/en/sirius-science/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/en/culture-savoirs/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/en/observer/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/en/associations/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/ht/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/ht/sirius-science/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/ht/culture-savoirs/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/ht/observer/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://cyber-mawonaj.github.io/gwada-sirius/ht/associations/</loc>
|
||||||
|
<lastmod>2026-07-21</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import adapter from '@sveltejs/adapter-static';
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
kit: {
|
||||||
|
adapter: adapter({
|
||||||
|
pages: 'build',
|
||||||
|
assets: 'build',
|
||||||
|
precompress: false,
|
||||||
|
strict: true
|
||||||
|
}),
|
||||||
|
// GitHub Pages : le site est servi sous /gwada-sirius/
|
||||||
|
// relative: false — chemins absolus partout (sinon $app/paths.base vaut ".."
|
||||||
|
// au prerender et les comparaisons de pathname se cassent).
|
||||||
|
paths: {
|
||||||
|
base: '/gwada-sirius',
|
||||||
|
relative: false
|
||||||
|
},
|
||||||
|
prerender: {
|
||||||
|
handleHttpError: 'warn',
|
||||||
|
handleMissingId: 'warn'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [sveltekit()]
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user