feat(session-1): socle SvelteKit et encyclopédie /zerbaj/ prérendue
SvelteKit 2 + Svelte 5 runes + TS strict, adapter-static en prérendu intégral, trailingSlash always, paths.relative false. Mesures de la porte de sortie - 56 pages HTML prérendues (13 fiches × 3 langues + pages de service) - 59 Ko de JS gzip (budget : 170) · build total 1,4 Mo (budget : 2 Mo) - 0 chargement tiers, vérifié par grep du build - fiche lisible sans JS : 1694 caractères de texte rendu - npm run check : 248 fichiers, 0 erreur, 0 warning - 0 erreur console au chargement Lighthouse non exécuté : porte encore ouverte, à vérifier avant la S6. Charte OKI Tokens partagés, thème sombre par défaut, Archivo et Inter self-hébergées (124 Ko de woff2), flag-bar à segments francs, bouton à balayage or, gate prefers-reduced-motion unique. CSP stricte en .htaccess et _headers, 404 statique autonome, PWA avec registerSW à chemin absolu (playbook 2.9). L'interface kréyòl n'est pas écrite par l'agent Le catalogue gcf ne contient que les termes venant des documents du porteur — 10 chaînes sur 132. Le reste vaut null et se replie sur le français ; check:i18n produit la liste des 122 phrases à traduire, par écran. <html lang> annonce la langue réellement écrite : fr tant que la couverture reste sous 60 %, avec les fragments kréyòl marqués un par un. Des tournures kréyòl que j'avais inventées ont été retirées. Corrections en cours de build - vite-plugin-pwa/sveltekit n'existe pas sans @vite-pwa/sveltekit : VitePWA en direct, comme le prescrivait déjà le playbook. - @sveltejs/vite-plugin-svelte v7 n'exporte plus sveltekit : import depuis @sveltejs/kit/vite, vérifié sur les exports réels. - Le sélecteur de langue fabriquait /fr/offline/, page inexistante. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# KI FWI SA YÉ ? — Apache / o2switch
|
||||
# Site statique intégralement prérendu. Aucun script inline, aucun domaine tiers :
|
||||
# la CSP peut donc rester stricte sans exception (playbook §2.10).
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; worker-src 'self'; manifest-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'"
|
||||
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
Header always set X-Frame-Options "DENY"
|
||||
Header always set X-Content-Type-Options "nosniff"
|
||||
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=(), interest-cohort=()"
|
||||
|
||||
# Le service worker ne doit jamais être servi depuis le cache HTTP,
|
||||
# sinon une mise à jour du jeu peut ne jamais atteindre l'appareil.
|
||||
<FilesMatch "sw\.js$">
|
||||
Header set Cache-Control "no-cache"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType font/woff2 "access plus 1 year"
|
||||
ExpiresByType image/avif "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType audio/ogg "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.(woff2|avif|webp|opus)$">
|
||||
Header set Cache-Control "public, max-age=31536000, immutable"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json image/svg+xml
|
||||
</IfModule>
|
||||
|
||||
AddType application/manifest+json .webmanifest
|
||||
AddType audio/ogg .opus
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
</IfModule>
|
||||
|
||||
# 404 statique et autonome : lisible même si tout le reste échoue.
|
||||
ErrorDocument 404 /404.html
|
||||
@@ -0,0 +1,80 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Page introuvable — KI FWI SA YÉ ?</title>
|
||||
<meta name="robots" content="noindex" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<style>
|
||||
/* Autonome : aucune dépendance, aucune police à charger. Cette page doit
|
||||
s'afficher même quand tout le reste du site est cassé. */
|
||||
:root {
|
||||
--noir: #0d0d0d;
|
||||
--creme: #fff8e7;
|
||||
--or: #fdb813;
|
||||
--vert: #00d66c;
|
||||
--rouge: #ff1654;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
gap: 1rem;
|
||||
padding: 2rem;
|
||||
background: var(--noir);
|
||||
color: var(--creme);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
.flag {
|
||||
height: 6px;
|
||||
width: 220px;
|
||||
margin: 0 auto;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
#0d0d0d 0 25%,
|
||||
var(--or) 25% 50%,
|
||||
var(--vert) 50% 75%,
|
||||
var(--rouge) 75% 100%
|
||||
);
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: clamp(1.6rem, 6vw, 2.6rem);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
opacity: 0.75;
|
||||
max-width: 44ch;
|
||||
}
|
||||
a {
|
||||
display: inline-block;
|
||||
margin-top: 0.5rem;
|
||||
min-height: 44px;
|
||||
padding: 0.7em 1.4em;
|
||||
color: var(--or);
|
||||
border: 2px solid var(--or);
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
a:hover,
|
||||
a:focus-visible {
|
||||
background: var(--or);
|
||||
color: var(--noir);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="flag"></div>
|
||||
<h1>Page introuvable</h1>
|
||||
<p>Cette page n’existe pas.</p>
|
||||
<a href="/">Retour à l’accueil</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; media-src 'self'; worker-src 'self'; manifest-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; object-src 'none'
|
||||
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
||||
X-Frame-Options: DENY
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=(), usb=(), interest-cohort=()
|
||||
|
||||
/_app/immutable/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/fonts/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
|
||||
/sw.js
|
||||
Cache-Control: no-cache
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="KI FWI SA YÉ ?">
|
||||
<rect width="64" height="64" fill="#0D0D0D" />
|
||||
<!-- Les quatre segments francs du drapeau panafricain, à la verticale. -->
|
||||
<rect x="0" y="52" width="16" height="12" fill="#0D0D0D" />
|
||||
<rect x="16" y="52" width="16" height="12" fill="#FDB813" />
|
||||
<rect x="32" y="52" width="16" height="12" fill="#00D66C" />
|
||||
<rect x="48" y="52" width="16" height="12" fill="#FF1654" />
|
||||
<!-- Arche or, angles nets : aucune forme organique (charte §1.4). -->
|
||||
<path d="M14 46V22a18 18 0 0 1 36 0v24h-9V22a9 9 0 0 0-18 0v24z" fill="#FDB813" />
|
||||
<rect x="28" y="30" width="8" height="8" fill="#00D66C" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 698 B |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "KI FWI SA YÉ ?",
|
||||
"short_name": "KI FWI",
|
||||
"description": "Reconnaître les fruits, les arbres et les plantes des Antilles. En kréyòl d'abord.",
|
||||
"lang": "gcf",
|
||||
"dir": "ltr",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"background_color": "#0D0D0D",
|
||||
"theme_color": "#0D0D0D",
|
||||
"categories": ["education", "games"],
|
||||
"icons": [
|
||||
{ "src": "/icone-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
|
||||
{ "src": "/icone-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
|
||||
{ "src": "/icone-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Enregistrement du service worker par fichier statique, avec chemin ABSOLU.
|
||||
*
|
||||
* Playbook §2.9 : l'injection du plugin n'atteint pas le HTML prérendu, et le
|
||||
* registerSW.js généré utilise un chemin relatif `./sw.js` qui casse sur les routes
|
||||
* imbriquées comme /fr/zerbaj/kowosol/.
|
||||
*/
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/sw.js', { scope: '/' }).catch(function () {
|
||||
/* Hors ligne au premier chargement, ou contexte non sécurisé : sans conséquence. */
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://ki-fwi.o-k-i.net/sitemap.xml
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Anti-FOUC. Fichier externe et non inline : la CSP du projet est `script-src 'self'`.
|
||||
* Pose les classes d'état avant le premier rendu pour éviter le flash de thème.
|
||||
*/
|
||||
(function () {
|
||||
var racine = document.documentElement;
|
||||
racine.classList.remove('no-js');
|
||||
racine.classList.add('js');
|
||||
|
||||
try {
|
||||
var choix = localStorage.getItem('oki-theme');
|
||||
if (choix === 'light') racine.classList.add('light-theme');
|
||||
else if (!choix && window.matchMedia('(prefers-color-scheme: light)').matches) {
|
||||
racine.classList.add('light-theme');
|
||||
}
|
||||
} catch (e) {
|
||||
/* localStorage indisponible (navigation privée stricte) : on garde le thème sombre. */
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user