From b687720ccee922bb7f440caf38803e273719347f Mon Sep 17 00:00:00 2001 From: Marc Reverdin Date: Sun, 7 Jun 2026 10:04:06 +0200 Subject: [PATCH] =?UTF-8?q?refonte:=20UI/UX=20complet=20=E2=80=94=20landin?= =?UTF-8?q?g=20page=20=C3=A9ditoriale=20+=20design=20system=20Tailwind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BaseLayout: bandeau tricolore, nav sticky mobile (hamburger), footer 4col - index.astro: landing éditoriale (hero + stats live + méthode + thèmes + scénarios) Données Eurostat live depuis indicators.json (dette, déficit, chômage, croissance) Thèmes à venir en cards dashed — séparation claire publiés / en préparation - themes/index.astro: nettoyage inline styles → Tailwind classes - themes/[slug].astro: header hero refait, nav interne, sidebar sticky, prose améliorée - donnees/index.astro: grille indicateurs responsive, tableau comparatif - global.css: design tokens unifiés (nuit/republic/accent/rouge), badges, cards, prose Co-Authored-By: Claude Sonnet 4.6 --- src/layouts/BaseLayout.astro | 206 +++++++++++-------- src/pages/donnees/index.astro | 209 ++++++++------------ src/pages/index.astro | 363 +++++++++++++++++++++++----------- src/pages/themes/[slug].astro | 336 +++++++++++-------------------- src/pages/themes/index.astro | 105 ++++++---- src/styles/global.css | 119 +++++++---- 6 files changed, 706 insertions(+), 632 deletions(-) diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 934cc3c..8f2e9e4 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -14,104 +14,142 @@ const { } = Astro.props; const canonical = new URL(Astro.url.pathname, Astro.site); +const currentPath = Astro.url.pathname; --- - - - - {title} - - + + + + {title} + + + + + + + + + + + + + + - - - - - - + +
+
+
+
+
- - - - + +
+
- - - - + + +
IRPF
+ +
- -
- INSTITUT DE RECHERCHE POLITIQUE POUR LA FRANCE · Analyse indépendante · Données publiques + + + + +
- -
-
+ +
+ {[ + { href: '/themes/', label: 'Thèmes' }, + { href: '/donnees/', label: 'Données' }, + { href: '/scenarios/', label: 'Scénarios' }, + { href: '/rapports/', label: 'Publications' }, + { href: '/a-propos/', label: 'À propos' }, + ].map(({ href, label }) => ( + {label} + ))} +
+
- -
IRPF
- -
+ +
+ +
- + +
+
+
+
+
IRPF
+ Institut de Recherche Politique pour la France +
+

+ Analyse indépendante, non-partisane, fondée sur les données publiques de l'INSEE, Eurostat, l'OCDE et des institutions parlementaires. +

+

Un projet Reverdin

-
- - -
- -
- - - + +
+ © {new Date().getFullYear()} Institut de Recherche Politique pour la France · Données publiques · Analyse libre +
+ - + + + diff --git a/src/pages/donnees/index.astro b/src/pages/donnees/index.astro index 2440236..465ecc9 100644 --- a/src/pages/donnees/index.astro +++ b/src/pages/donnees/index.astro @@ -1,23 +1,15 @@ --- import BaseLayout from '../../layouts/BaseLayout.astro'; import KpiLive from '../../components/KpiLive.astro'; -import { INDICATEURS_BY_THEME, PAYS_LABEL } from '../../lib/indicators-config.ts'; import { existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; -// Charger le JSON des indicateurs (généré en pré-build) -// Utilise readFileSync + existsSync pour éviter l'échec de résolution statique Vite -let indicateursData: any = { indicators: [], insee: {}, generated_at: null }; +let indicateursData: any = { indicators: [], generated_at: null }; const jsonPath = join(process.cwd(), 'src/data/live/indicators.json'); if (existsSync(jsonPath)) { - try { - indicateursData = JSON.parse(readFileSync(jsonPath, 'utf-8')); - } catch { - // fichier corrompu, on utilise les défauts - } + try { indicateursData = JSON.parse(readFileSync(jsonPath, 'utf-8')); } catch {} } -// Indexer par ID pour accès rapide const byId = Object.fromEntries( (indicateursData.indicators || []).map((i: any) => [i.id, i]) ); @@ -26,54 +18,18 @@ const generatedAt = indicateursData.generated_at ? new Date(indicateursData.generated_at).toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : null; -// Groupes d'indicateurs pour le dashboard const GROUPES = [ - { - titre: 'Finances publiques', - couleur: '#1e3a6e', - bg: '#eff6ff', - ids: ['deficit_pib', 'dette_pib', 'depenses_publiques_pib', 'prelevements_obligatoires', 'croissance_pib', 'depenses_sociales_pib'], - }, - { - titre: 'Emploi & Marché du travail', - couleur: '#7c3aed', - bg: '#f5f3ff', - ids: ['taux_emploi', 'taux_chomage', 'taux_chomage_jeunes', 'taux_emploi_seniors'], - }, - { - titre: 'Santé', - couleur: '#dc2626', - bg: '#fef2f2', - ids: ['esperance_vie', 'depenses_sante_pib'], - }, - { - titre: 'Éducation', - couleur: '#0891b2', - bg: '#ecfeff', - ids: ['decrochage_scolaire', 'diplomes_superieurs', 'depenses_education_pib'], - }, - { - titre: 'Cohésion sociale', - couleur: '#d97706', - bg: '#fffbeb', - ids: ['gini', 'taux_pauvrete', 'taux_pauvrete_enfants'], - }, - { - titre: 'Logement', - couleur: '#059669', - bg: '#f0fdf4', - ids: ['indice_prix_immobilier', 'surcharge_logement', 'taux_proprietaires'], - }, - { - titre: 'Environnement & Énergie', - couleur: '#16a34a', - bg: '#f0fdf4', - ids: ['emissions_ghg_per_capita', 'part_energies_renouvelables', 'intensite_energetique'], - }, + { titre: 'Finances publiques', couleur: '#1e3a6e', ids: ['deficit_pib','dette_pib','depenses_publiques_pib','prelevements_obligatoires','croissance_pib','depenses_sociales_pib'] }, + { titre: 'Emploi', couleur: '#7c3aed', ids: ['taux_emploi','taux_chomage','taux_chomage_jeunes','taux_emploi_seniors'] }, + { titre: 'Santé', couleur: '#dc2626', ids: ['esperance_vie','depenses_sante_pib'] }, + { titre: 'Éducation', couleur: '#0891b2', ids: ['decrochage_scolaire','diplomes_superieurs','depenses_education_pib'] }, + { titre: 'Cohésion sociale', couleur: '#d97706', ids: ['gini','taux_pauvrete','taux_pauvrete_enfants'] }, + { titre: 'Logement', couleur: '#059669', ids: ['indice_prix_immobilier','surcharge_logement','taux_proprietaires'] }, + { titre: 'Environnement & Énergie', couleur: '#16a34a', ids: ['emissions_ghg_per_capita','part_energies_renouvelables','intensite_energetique'] }, ]; const PAYS_ORDRE = ['FR', 'DE', 'ES', 'IT', 'SE', 'NL', 'EU27_2020']; -const INDICATEURS_TABLE = ['deficit_pib', 'dette_pib', 'depenses_publiques_pib', 'taux_chomage', 'taux_emploi', 'esperance_vie', 'gini', 'emissions_ghg_per_capita']; +const INDICATEURS_TABLE = ['deficit_pib','dette_pib','depenses_publiques_pib','taux_chomage','taux_emploi','esperance_vie','gini','emissions_ghg_per_capita']; --- -
-
-
IRPF — DONNÉES OUVERTES
-

Tableau de bord France

-

- Indicateurs clés de la France comparés à l'Allemagne, l'Espagne, l'Italie, la Suède, les Pays-Bas et la moyenne UE27. - Sources : Eurostat API (données ouvertes) + INSEE BDM. +

+
+ +
IRPF — Données ouvertes
+

Tableau de bord France

+

+ Indicateurs clés de la France comparés à l'Allemagne, l'Espagne, l'Italie, la Suède, les Pays-Bas et la moyenne UE27. Sources : Eurostat API + INSEE BDM.

{generatedAt && ( -
- - Mis à jour le {generatedAt} — {indicateursData.indicators?.length || 0} indicateurs collectés +
+ + Mis à jour le {generatedAt} — {indicateursData.indicators?.length ?? 0} indicateurs
)}
- - -
+
{GROUPES.map(groupe => { - const inds = groupe.ids.map(id => byId[id]).filter(Boolean); - if (inds.length === 0) return null; + const inds = groupe.ids.map((id: string) => byId[id]).filter(Boolean); + if (!inds.length) return null; + const ancre = groupe.titre.toLowerCase().replace(/[^a-z0-9]+/g, '-'); return ( -
-
-
-

{groupe.titre}

-
+
+
+
+

{groupe.titre}

+
-
+
{inds.map((ind: any) => ( -
-
-

Comparatif France — Europe

-
- Source : Eurostat API +
+
+

Comparatif France — Europe

+
+ Eurostat API
-
- +
+
- - + + {PAYS_ORDRE.map(code => ( - ))} - {INDICATEURS_TABLE.map((id, idx) => { + {INDICATEURS_TABLE.map((id: string, idx: number) => { const ind = byId[id]; if (!ind) return null; return ( - - + - {PAYS_ORDRE.map(code => { - const pv = code === 'FR' ? ind.france : ind.pays?.[code]; + {PAYS_ORDRE.map((code: string) => { + const pv = code === 'FR' ? ind.france : ind.pays?.[code]; const val = pv?.value; - const formatted = val !== null && val !== undefined - ? (Math.abs(val) >= 100 ? Math.round(val).toLocaleString('fr-FR') : val.toFixed(1)) + const txt = val != null + ? (Math.abs(val) >= 100 ? Math.round(val).toLocaleString('fr-FR') : Number(val).toFixed(1)) : '—'; - const isFr = code === 'FR'; return ( - ); })} @@ -195,33 +158,27 @@ const INDICATEURS_TABLE = ['deficit_pib', 'dette_pib', 'depenses_publiques_pib',
Indicateur
Indicateur + {code === 'EU27_2020' ? 'UE27' : code}
+
{ind.label} -
{ind.unite}
+
{ind.unite}
- {formatted} + + {txt}
-

- Dernière mise à jour : {generatedAt || 'N/A'} — Eurostat API JSON-stat 2.0 +

+ Mis à jour le {generatedAt ?? 'N/A'} — Eurostat API JSON-stat 2.0

-
-

Sources et méthodologie

-
-
- Eurostat — API JSON-stat 2.0, données officielles de l'Office statistique de l'Union européenne. - Mise à jour automatique à chaque rebuild du site (max 6h de délai). - CORS ouvert, pas d'authentification requise. -
-
- Comparaison — France vs Allemagne, Espagne, Italie, Suède, Pays-Bas et moyenne UE27. - Les valeurs en rouge indiquent un retard par rapport à la moyenne UE ; - les valeurs en vert indiquent une avance. - Données non disponibles (N/D) = non publiées par Eurostat pour cette période. -
+
+

Sources et méthodologie

+
+

+ Eurostat — API JSON-stat 2.0, données officielles de l'Office statistique de l'Union européenne. Mise à jour automatique à chaque rebuild du site (max 6h de délai). CORS ouvert, pas d'authentification requise. +

+

+ Comparaison — France vs Allemagne, Espagne, Italie, Suède, Pays-Bas et moyenne UE27. Données non disponibles (N/D) = non publiées par Eurostat pour cette période. INSEE OAuth2 en cours de configuration. +

-
+
- diff --git a/src/pages/index.astro b/src/pages/index.astro index b752ff4..ea878a7 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,148 +1,275 @@ --- import BaseLayout from '../layouts/BaseLayout.astro'; import { getCollection } from 'astro:content'; +import { existsSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; const themes = (await getCollection('themes')).sort((a, b) => a.data.ordre - b.data.ordre); -const rapports = (await getCollection('rapports')).slice(0, 3); + +// Indicateurs live +let byId: Record = {}; +let generatedAt = ''; +const jsonPath = join(process.cwd(), 'src/data/live/indicators.json'); +if (existsSync(jsonPath)) { + try { + const d = JSON.parse(readFileSync(jsonPath, 'utf-8')); + byId = Object.fromEntries((d.indicators ?? []).map((i: any) => [i.id, i])); + generatedAt = d.generated_at ?? ''; + } catch {} +} + +function fmt(id: string, digits = 1): string { + const v = byId[id]?.france?.value; + if (v == null) return 'N/D'; + return Number(v).toLocaleString('fr-FR', { minimumFractionDigits: digits, maximumFractionDigits: digits }); +} + +const THEMES_A_VENIR = [ + 'Éducation', 'Emploi & Travail', 'Retraites', 'Logement', + 'Justice & Sécurité', 'Énergie & Environnement', 'Agriculture', + 'Numérique', 'Cohésion sociale', 'Démocratie', 'Europe', 'Immigration', +]; --- - -
-
-
- INSTITUT DE RECHERCHE POLITIQUE POUR LA FRANCE + +
+ +
+
+
+ +

+ La France
sous les données. +

+

+ Des diagnostics rigoureux sur les 14 grandes problématiques françaises. Des données, pas des slogans. Des propositions chiffrées, pas des promesses. +

+ +

+ Non-partisan · Données publiques · Accès libre +

-

- Comprendre la France.
- Proposer des solutions. -

-

- Analyse non-partisane des politiques publiques françaises, fondée sur les données de l'INSEE, de l'OCDE et des institutions internationales. Pour alimenter le débat démocratique en 2027 et au-delà. + + +

+
+
+ + +
+
+ {[ + { id: 'dette_pib', label: 'Dette / PIB', unit: '%' }, + { id: 'deficit_pib', label: 'Déficit / PIB', unit: '%' }, + { id: 'taux_chomage', label: 'Chômage', unit: '%' }, + { id: 'prelevements_obligatoires', label: 'Prélèvements oblig.', unit: '%' }, + ].map(({ id, label, unit }) => ( +
+
{fmt(id)}{unit}
+
{label}
+
{byId[id]?.france?.year ?? ''}
+
+ ))} +
+
+ + +
+
+ +

Le diagnostic avant le programme

+

+ Le débat politique français manque de données. L'IRPF fait le chemin inverse : partir des faits pour cartographier chaque problématique, recenser les propositions, et évaluer leur impact.

- +
+ +
+ {[ + { + num: '01', + titre: 'Données publiques', + desc: 'INSEE, OCDE, Eurostat, Banque de France, Cour des comptes, data.gouv.fr. Chaque chiffre est sourcé, daté, contextualisé.', + color: '#1e3a6e', + }, + { + num: '02', + titre: 'Diagnostic rigoureux', + desc: 'Cartographie des dynamiques longues, comparaisons européennes, identification des nœuds structurels — sans idéologie.', + color: '#2563eb', + }, + { + num: '03', + titre: 'Propositions chiffrées', + desc: 'Recensement de toutes les propositions des acteurs politiques, économiques et de la société civile. Évaluation de leur faisabilité.', + color: '#0891b2', + }, + ].map(({ num, titre, desc, color }) => ( +
+
{num}
+

{titre}

+

{desc}

+
+ ))}
- -
-
-
-
-
-5,5%
-
Déficit public / PIB 2024
-
Source : INSEE
-
-
-
113%
-
Dette publique / PIB
-
Source : Eurostat
-
-
-
7,3%
-
Taux de chômage T1 2025
-
Source : DARES
-
-
-
28e
-
Rang PISA 2022 (sciences)
-
Source : OCDE
-
-
-
-
- - -
-
NOTRE DÉMARCHE
-

- La France a besoin d'un diagnostic rigoureux avant un programme -

-
-

Le débat politique français souffre d'un paradoxe : beaucoup de propositions, peu de données ; beaucoup de slogans, peu de diagnostic. L'IRPF fait le chemin inverse.

-

Nous partons des faits — ceux de l'INSEE, de l'OCDE, de la Cour des comptes, des rapports parlementaires — pour cartographier chaque grande problématique française, recenser toutes les propositions des acteurs du débat, et proposer des analyses originales.

-

Ni de gauche ni de droite. Exigeant sur les chiffres. Libre sur les idées.

-
-
- - -
-
-
+ +
+
- -
-
-
-
SCÉNARIOS MACRO 2027–2032
-

Trois avenirs possibles pour la France

-

De la dérive budgétaire au choc républicain : nous chiffrons les conséquences de chaque trajectoire sur la dette, la croissance et les services publics.

- Lire les scénarios → + +
+
+
+
Projection macro 2027–2032
+

+ Trois trajectoires possibles pour la France +

+

+ Déficit, dette, croissance, services publics — nous chiffrons les conséquences de chaque trajectoire politique sur un horizon de 5 ans. +

+ + Lire les scénarios → +
-
-
-
Dérive
-
-5,5% PIB en 2032 · +0,8%/an
-
-
-
Réforme graduelle
-
-3% PIB en 2030 · +1,4%/an
-
-
-
Choc républicain
-
-1,5% PIB en 2032 · +2%/an
-
+
+ {[ + { label: 'Dérive', kpi: '-5,5% PIB · +0,8%/an', color: '#ef4444' }, + { label: 'Réforme graduelle',kpi: '-3% PIB · +1,4%/an', color: '#f59e0b' }, + { label: 'Choc républicain', kpi: '-1,5% PIB · +2%/an', color: '#22c55e' }, + ].map(({ label, kpi, color }) => ( +
+
+
+ {label} +
+ {kpi} +
+ ))} +
+
+
+ + +
+
+ +

Ni de gauche ni de droite. Exigeant sur les chiffres.

+
+

+ Le débat politique français souffre d'un paradoxe : beaucoup de propositions, peu de données ; beaucoup de slogans, peu de diagnostic. Chaque camp dispose de ses instituts, de ses économistes, de ses chiffres. L'IRPF fait le chemin inverse. +

+

+ Nous partons des faits — ceux de l'INSEE, de l'OCDE, de la Cour des comptes, des rapports parlementaires — pour cartographier chaque grande problématique française, recenser toutes les propositions des acteurs du débat, et proposer des analyses originales. +

+
+
- - diff --git a/src/pages/themes/[slug].astro b/src/pages/themes/[slug].astro index 3c89e5d..8272c3c 100644 --- a/src/pages/themes/[slug].astro +++ b/src/pages/themes/[slug].astro @@ -16,7 +16,6 @@ const { Content } = await render(theme); const allPropositions = await getCollection('propositions'); const propositions = allPropositions.filter(p => p.data.theme === theme.data.slug); -// Sections configurées dans le frontmatter → nav dynamique const SECTION_LABELS: Record = { diagnostic: 'Diagnostic', historique: 'Historique', @@ -29,115 +28,115 @@ const SECTION_LABELS: Record = { sources: 'Sources', }; -const navSections = theme.data.sections.map(s => ({ +const navSections = theme.data.sections.map((s: string) => ({ id: s, label: SECTION_LABELS[s] ?? s, })); -function orientationBadge(o?: string) { - const map: Record = { - 'gauche': 'background:#fee2e2;color:#991b1b', - 'centre-gauche': 'background:#fce7f3;color:#9d174d', - 'centre': 'background:#dbeafe;color:#1e40af', - 'centre-droit': 'background:#ede9fe;color:#5b21b6', - 'droite': 'background:#f3e8ff;color:#6b21a8', - 'neutre': 'background:#f3f4f6;color:#374151', - 'irpf': 'background:#0f172a;color:white', - }; - return map[o ?? 'neutre'] ?? map['neutre']; -} +const TENDANCE_COLOR: Record = { + hausse: '#16a34a', baisse: '#dc2626', stable: '#6b7280', critique: '#dc2626', alerte: '#d97706' +}; +const TENDANCE_ICON: Record = { + hausse: '↑', baisse: '↓', stable: '→', critique: '⚠', alerte: '!' +}; -function tendanceCouleur(t?: string) { - return { hausse: '#16a34a', baisse: '#dc2626', stable: '#6b7280', critique: '#dc2626', alerte: '#d97706' }[t ?? 'stable'] ?? '#6b7280'; -} - -function tendanceIcon(t?: string) { - return { hausse: '↑', baisse: '↓', stable: '→', critique: '⚠', alerte: '!' }[t ?? 'stable'] ?? '→'; -} +const ORIENTATION_CLASS: Record = { + 'gauche': 'bg-red-100 text-red-800', + 'centre-gauche': 'bg-pink-100 text-pink-800', + 'centre': 'bg-blue-100 text-blue-800', + 'centre-droit': 'bg-violet-100 text-violet-800', + 'droite': 'bg-purple-100 text-purple-800', + 'neutre': 'bg-gray-100 text-gray-700', + 'irpf': 'bg-[#0f172a] text-white', +}; --- - + -
-
- Accueil - - Thèmes - - {theme.data.title} -
+
+
- -
-
-
-
+ +
+
+
+ +
{theme.data.priorite === 'critique' && ( - PRIORITÉ CRITIQUE + PRIORITÉ CRITIQUE )} {theme.data.priorite === 'haute' && ( - PRIORITÉ HAUTE + PRIORITÉ HAUTE )} -

{theme.data.title}

-

{theme.data.description}

+

{theme.data.title}

+

{theme.data.description}

- -
+ +
{theme.data.kpi_valeur && ( -
-
+
+
{theme.data.kpi_valeur} - {tendanceIcon(theme.data.kpi_tendance)} + + {TENDANCE_ICON[theme.data.kpi_tendance ?? 'stable']} +
-
{theme.data.kpi_label}
- {theme.data.kpi_contexte &&
{theme.data.kpi_contexte}
} +
{theme.data.kpi_label}
+ {theme.data.kpi_contexte &&
{theme.data.kpi_contexte}
}
)} {theme.data.budget_etat_milliards && ( -
-
{theme.data.budget_etat_milliards} Md€
-
Budget État 2025
+
+
{theme.data.budget_etat_milliards} Md€
+
Budget État 2025
)} {theme.data.opinion_pct && ( -
-
{theme.data.opinion_pct}%
-
{theme.data.opinion_question ?? 'Satisfaction'}
- {theme.data.opinion_source &&
{theme.data.opinion_source} · {theme.data.opinion_annee}
} +
+
{theme.data.opinion_pct}%
+
+ {theme.data.opinion_question ?? 'Satisfaction'} +
+ {theme.data.opinion_source && ( +
{theme.data.opinion_source} · {theme.data.opinion_annee}
+ )}
)} -
-
{propositions.length}
-
Propositions
+
+
{propositions.length}
+
Propositions
{theme.data.score_sources && ( -
-
{theme.data.score_sources}
-
Sources vérifiées
+
+
{theme.data.score_sources}
+
Sources vérifiées
)}
-
-
Mis à jour
-
{theme.data.derniere_maj}
+
+
Mis à jour
+
{theme.data.derniere_maj}
{theme.data.rang_eu && ( -
-
Rang UE
-
{theme.data.rang_eu}/{theme.data.nb_pays_compares ?? 27}
+
+
Rang UE
+
{theme.data.rang_eu}/{theme.data.nb_pays_compares ?? 27}
)}
-
Sources data
- {theme.data.sources_data.slice(0, 6).map(s => ( -
{s}
+
Sources data
+ {theme.data.sources_data.slice(0, 5).map((s: string) => ( +
{s}
))}
@@ -145,14 +144,13 @@ function tendanceIcon(t?: string) {
- -