Patrick Schurig 95a9edda5a fix(rgpd): polices auto-hébergées — la doc ne transmet plus l'IP du visiteur à Google
`theme.font: {text, code}` faisait injecter par Material, dans le <head> de
chacune des 316 pages, un preconnect vers fonts.gstatic.com et une feuille
depuis fonts.googleapis.com (Sans 300/300i/400/400i/700/700i + Mono
400/400i/700/700i). Chaque visite transmettait donc l'IP du visiteur à Google
AVANT tout consentement.

Ce qui rendait ça gênant n'est pas la règle en soi, c'est l'asymétrie : le site
vitrine a fait exactement l'inverse en T10, délibérément, pour cette raison
(jurisprudence LG München 2022, doctrine CNIL) et pour ne pas contredire « vos
données ne sortent pas de chez vous ». La doc affichait donc le contraire de
l'engagement que le produit met en avant, sur le même nom de domaine.

Correctif : `theme.font: false` retire les deux balises ; les 16 woff2 viennent
du dépôt du site — mêmes fichiers, mêmes unicode-range (latin + latin-ext pour
les accents), déjà éprouvés en production depuis juin. Les 16 blocs @font-face
sont repris tels quels plutôt que réécrits, avec les chemins adaptés. Material
lit `--md-text-font` / `--md-code-font`, qu'extra.css définit.

312 Ko d'actifs, servis localement.

⚠️ LES ITALIQUES SONT DÉSORMAIS SYNTHÉTISÉES, et c'est la contrepartie à
connaître. Google servait 4 fontes italiques que nous n'avons pas (aucun fichier
italique dans le dépôt du site, aucune IBM Plex installée sur la machine de
build) : le navigateur incline donc la romaine. Visible sur les intertitres en
italique des blocs générés — `_Einstellungen:_`, `_Gemessene Größen:_`. Pour y
remédier : récupérer les woff2 italiques depuis le dépôt officiel IBM/plex (OFL)
et ajouter les @font-face correspondants. La méthode est celle déjà documentée
au T10 du site, et je ne l'ai pas prise d'office — c'est un ajout de binaires
depuis Internet.

Vérifié sur le site construit, et pas seulement sur le <head> :
  · 0 url() externe dans toute la CSS
  · 0 preconnect
  · 0 ressource (link/script/img/iframe) chargée depuis un tiers — le seul hôte
    cité est docs.etm-powersync.fr lui-même, par les <link rel="canonical">
  · les 16 woff2 présents dans le build, 16 références, 0 chemin cassé
  · build --strict sans avertissement

La seule mention restante de Google dans le dépôt est le commentaire d'extra.css
qui explique pourquoi elle n'y est plus.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-13 09:14:26 +02:00

115 lines
12 KiB
CSS

/* ══════════════════════════════════════════════════════════════════════════
POLICES AUTO-HÉBERGÉES — IBM Plex (2026-10-13)
Material injectait `theme.font: {text, code}`, ce qui ajoutait au <head> un
preconnect vers fonts.gstatic.com et une feuille depuis fonts.googleapis.com.
Chaque visite transmettait donc l'IP du visiteur à Google AVANT tout
consentement — et le site vitrine avait fait exactement l'inverse en T10,
délibérément, pour cette raison (jurisprudence LG München 2022, doctrine
CNIL) et pour ne pas contredire « vos données ne sortent pas de chez vous ».
La doc affichait donc le contraire de l'engagement du produit.
`theme.font: false` dans mkdocs.yml supprime les deux balises ; les 16 woff2
viennent du dépôt du site (mêmes fichiers, mêmes unicode-range, déjà éprouvés
en production depuis juin).
⚠️ LES ITALIQUES SONT SYNTHÉTISÉES. Google servait 4 fontes italiques
(Sans 300i/400i/700i, Mono 400i) que nous n'avons pas : le navigateur incline
donc la romaine. C'est visible sur `_Einstellungen:_` et les intertitres en
italique des blocs générés. Pour y remédier, récupérer les woff2 italiques
depuis le dépôt officiel IBM/plex (OFL) et ajouter les @font-face
correspondants — méthode déjà documentée au T10 du site.
══════════════════════════════════════════════════════════════════════════ */
:root {
--md-text-font: "IBM Plex Sans";
--md-code-font: "IBM Plex Mono";
}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:300;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-ext-300.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-ext-400.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-ext-500.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-ext-600.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:700;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-ext-700.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:300;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-300.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-400.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-500.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-600.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:700;font-display:swap;src:url('../assets/fonts/ibm-plex-sans-latin-700.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('../assets/fonts/ibm-plex-mono-latin-ext-400.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('../assets/fonts/ibm-plex-mono-latin-ext-500.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('../assets/fonts/ibm-plex-mono-latin-ext-600.woff2') format('woff2');unicode-range:U+0100-02AF,U+0304,U+0308,U+0329,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('../assets/fonts/ibm-plex-mono-latin-400.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('../assets/fonts/ibm-plex-mono-latin-500.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('../assets/fonts/ibm-plex-mono-latin-600.woff2') format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
/* ETM PowerSync — surcharge couleurs de marque sur Material (scheme slate) */
[data-md-color-scheme="slate"] {
--md-primary-fg-color: #31a3dd; /* cyan ETM */
--md-primary-fg-color--light: #5cb8e6;
--md-primary-fg-color--dark: #2589bd;
--md-accent-fg-color: #fec113; /* ambre ETM */
--md-default-bg-color: #0a1f2b;
--md-default-bg-color--light: #0d2b3b;
--md-code-bg-color: #06151d;
--md-footer-bg-color: #0d2b3b;
}
[data-md-color-scheme="slate"] .md-typeset a { color: var(--md-primary-fg-color); }
/* badges — 3 familles : canal APT · stabilité plugin · maturité fonctionnalité */
.badge{font-family:"IBM Plex Mono",monospace;font-size:.7rem;letter-spacing:.06em;
text-transform:uppercase;padding:2px 8px;border-radius:20px;font-weight:600;white-space:nowrap;
display:inline-block;vertical-align:middle;line-height:1.4}
/* canal APT */
.badge.stable {color:#3fd18a;background:rgba(63,209,138,.12);border:1px solid rgba(63,209,138,.3)}
.badge.testing {color:#fec113;background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.3)}
.badge.nightly {color:#8fa9b5;background:rgba(143,169,181,.1);border:1px solid rgba(143,169,181,.3)}
/* stabilité plugin (meta.json) */
.badge.consumer {color:#31a3dd;background:rgba(49,163,221,.1);border:1px solid rgba(49,163,221,.3)}
.badge.community {color:#a78bfa;background:rgba(167,139,250,.1);border:1px solid rgba(167,139,250,.3)}
.badge.experimental {color:#fb923c;background:rgba(251,146,60,.1);border:1px solid rgba(251,146,60,.3)}
/* origine plugin (PORTING_STATUS.repo ou champ origin:) */
.badge.origin-nymea {color:#94a3b8;background:rgba(148,163,184,.1);border:1px solid rgba(148,163,184,.3)}
.badge.origin-etm {color:#4ade80;background:rgba(74,222,128,.1);border:1px solid rgba(74,222,128,.3)}
.badge.origin-tiers {color:#fb923c;background:rgba(251,146,60,.1);border:1px solid rgba(251,146,60,.3)}
/* maturité fonctionnalités (à la main) */
.badge.ok {color:#3fd18a;background:rgba(63,209,138,.12);border:1px solid rgba(63,209,138,.3)}
.badge.part {color:#fec113;background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.3)}
.badge.road {color:#8fa9b5;background:rgba(143,169,181,.1);border:1px solid rgba(143,169,181,.3)}
/* permission scope badges (API JsonRPC) */
.badge.perm-none {color:#3fd18a;background:rgba(63,209,138,.12);border:1px solid rgba(63,209,138,.3)}
.badge.perm-control {color:#fec113;background:rgba(254,193,19,.1);border:1px solid rgba(254,193,19,.3)}
.badge.perm-configure {color:#fb923c;background:rgba(251,146,60,.1);border:1px solid rgba(251,146,60,.3)}
.badge.perm-admin {color:#ef4444;background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.3)}
.badge.perm-unknown {color:#94a3b8;background:rgba(148,163,184,.1);border:1px solid rgba(148,163,184,.3)}
/* ══════════════════════════════════════════════════════════════════════════
SÉLECTEUR DE LANGUE — drapeaux (2026-10-13)
Material rend son sélecteur en liste déroulante, chaque entrée portant un
`hreflang` : on l'habille en CSS, sans toucher au gabarit du thème.
Drapeaux en SVG inline (data-URI) et NON en emoji : 🇫🇷 ne s'affiche pas sous
Windows, où Chrome rend « FR » dans un carré faute de police de drapeaux.
Le NOM de la langue reste affiché à côté — un drapeau désigne un pays, pas
une langue, et l'anglais ici sert les deux marchés plus la communauté.
══════════════════════════════════════════════════════════════════════════ */
.md-select__link[hreflang]{display:flex;align-items:center;gap:.6rem}
.md-select__link[hreflang]::before{content:"";width:1.25em;height:.9em;flex:none;
background-size:cover;background-position:center;border-radius:1px;
box-shadow:0 0 0 1px rgba(255,255,255,.22)}
.md-select__link[hreflang="fr"]::before{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 3 2'%3E%3Crect width='3' height='2' fill='%23ED2939'/%3E%3Crect width='2' height='2' fill='%23FFFFFF'/%3E%3Crect width='1' height='2' fill='%23002395'/%3E%3C/svg%3E")}
.md-select__link[hreflang="de"]::before{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 5 3'%3E%3Crect width='5' height='3' fill='%23FFCE00'/%3E%3Crect width='5' height='2' fill='%23DD0000'/%3E%3Crect width='5' height='1' fill='%23000000'/%3E%3C/svg%3E")}
.md-select__link[hreflang="en"]::before{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 30'%3E%3Crect width='60' height='30' fill='%23012169'/%3E%3Cpath d='M0,0 60,30M60,0 0,30' stroke='%23FFFFFF' stroke-width='6'/%3E%3Cpath d='M0,0 60,30M60,0 0,30' stroke='%23C8102E' stroke-width='4'/%3E%3Cpath d='M30,0v30M0,15h60' stroke='%23FFFFFF' stroke-width='10'/%3E%3Cpath d='M30,0v30M0,15h60' stroke='%23C8102E' stroke-width='6'/%3E%3C/svg%3E")}
/* le bouton du sélecteur : l'icône « traduction » de Material ne dit pas dans
quelle langue on est. On le laisse tel quel (c'est le gabarit du thème), mais
on lui donne de l'air pour qu'il ne colle pas à la recherche. */
.md-header__option{margin-right:.2rem}