From 251b0afac2e17e01f1b3ee30ea779beb6b415fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C9=A7=CF=83=E2=84=93=CF=83?= Date: Wed, 1 Oct 2025 00:48:33 +0200 Subject: [PATCH] chore: add initial project setup with Docker and React configuration --- .dockerignore | 10 + .gitignore | 24 + Dockerfile | 17 + README.md | 165 +++ docker-compose.yml | 13 + index.html | 13 + nginx.conf | 28 + package-lock.json | 2079 ++++++++++++++++++++++++++++++ package.json | 23 + src/App.css | 27 + src/App.jsx | 48 + src/components/Footer.css | 43 + src/components/Footer.jsx | 24 + src/components/Header.css | 77 ++ src/components/Header.jsx | 24 + src/components/Sidebar.css | 108 ++ src/components/Sidebar.jsx | 76 ++ src/index.css | 60 + src/main.jsx | 10 + src/pages/Accessibility.jsx | 384 ++++++ src/pages/Conclusion.jsx | 316 +++++ src/pages/EcoGuide.jsx | 463 +++++++ src/pages/EnvironmentalAudit.jsx | 327 +++++ src/pages/Home.css | 348 +++++ src/pages/Home.jsx | 166 +++ src/pages/RGAA.jsx | 518 ++++++++ src/pages/RGESN.jsx | 412 ++++++ src/pages/SharedPages.css | 459 +++++++ src/pages/ToolsComparison.css | 351 +++++ src/pages/ToolsComparison.jsx | 380 ++++++ src/pages/WAVE.jsx | 405 ++++++ src/pages/WebsiteCarbon.jsx | 367 ++++++ vite.config.js | 7 + 33 files changed, 7772 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 index.html create mode 100644 nginx.conf create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/App.css create mode 100644 src/App.jsx create mode 100644 src/components/Footer.css create mode 100644 src/components/Footer.jsx create mode 100644 src/components/Header.css create mode 100644 src/components/Header.jsx create mode 100644 src/components/Sidebar.css create mode 100644 src/components/Sidebar.jsx create mode 100644 src/index.css create mode 100644 src/main.jsx create mode 100644 src/pages/Accessibility.jsx create mode 100644 src/pages/Conclusion.jsx create mode 100644 src/pages/EcoGuide.jsx create mode 100644 src/pages/EnvironmentalAudit.jsx create mode 100644 src/pages/Home.css create mode 100644 src/pages/Home.jsx create mode 100644 src/pages/RGAA.jsx create mode 100644 src/pages/RGESN.jsx create mode 100644 src/pages/SharedPages.css create mode 100644 src/pages/ToolsComparison.css create mode 100644 src/pages/ToolsComparison.jsx create mode 100644 src/pages/WAVE.jsx create mode 100644 src/pages/WebsiteCarbon.jsx create mode 100644 vite.config.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..51b0957 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +npm-debug.log +.git +.gitignore +*.md +.env +.DS_Store +dist +.vscode +.idea diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f9c7576 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# Build stage +FROM node:18-alpine AS builder + +# Set working directory +WORKDIR /app + +# Install dependencies +COPY package.json package-lock.json ./ +RUN npm install --legacy-peer-deps +RUN npm install -g serve + +# Copy all files +COPY . . + +RUN npm run build + +CMD ["serve", "-s", "dist", "-l", "3000"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4294d90 --- /dev/null +++ b/README.md @@ -0,0 +1,165 @@ +# TP Écoconception Web - Site React + +Site web React présentant les résultats complets du TP sur l'écoconception et l'accessibilité web. + +## 📋 Contenu du TP + +Ce site présente de manière structurée et interactive tous les éléments suivants : + +### 🔍 Audit Initial +- **Comparaison d'outils** : Analyse comparative de 5 outils d'écoconception (Website Carbon, EcoIndex, Lighthouse, GreenIT-Analysis, Scaphandre) avec graphique radar +- **Website Carbon Calculator** : Décryptage technique et pédagogique complet +- **Audit Environnemental** : Diagnostic et recommandations pour un site fictif +- **RGESN** : Application du Référentiel Général d'Écoconception des Services Numériques + +### ♿ Accessibilité +- **Principes de l'accessibilité** : Les 4 principes POUR (Perceptible, Utilisable, Compréhensible, Robuste) +- **Audit WAVE** : Découverte et utilisation de l'outil WAVE avec exemples de corrections +- **RGAA** : Conformité au Référentiel Général d'Amélioration de l'Accessibilité + +### 🗺️ Guide Pratique +- **Guide d'écoconception** : Méthodologie complète pour intégrer l'écoconception dans un projet SAE +- **Planning type** : Organisation sur 4 semaines avec checklists détaillées +- **Outils recommandés** : Liste complète des outils pour chaque besoin + +### 🎯 Conclusion +- Bilan des compétences acquises +- Bénéfices multiples (environnementaux, sociaux, économiques, juridiques) +- Ressources pour aller plus loin + +## 🚀 Installation et Lancement + +### Prérequis +- Node.js (version 16 ou supérieure) +- npm ou yarn + +### Étapes d'installation + +1. **Installer les dépendances** +```bash +npm install +``` + +2. **Lancer le serveur de développement** +```bash +npm run dev +``` + +3. **Ouvrir dans le navigateur** +Le site sera accessible à l'adresse : `http://localhost:5173` + +### Commandes disponibles + +- `npm run dev` : Lance le serveur de développement +- `npm run build` : Crée une version de production optimisée +- `npm run preview` : Prévisualise la version de production + +## 🛠️ Technologies Utilisées + +- **React** 18.2 : Framework JavaScript +- **React Router** 6.20 : Navigation entre les pages +- **Recharts** 2.10 : Graphiques interactifs (radar) +- **Vite** 5.0 : Build tool ultra-rapide +- **Font Awesome** 6.5 : Icônes + +## 📁 Structure du Projet + +``` +Web_eco/ +├── src/ +│ ├── components/ # Composants réutilisables +│ │ ├── Header.jsx +│ │ ├── Sidebar.jsx +│ │ └── Footer.jsx +│ ├── pages/ # Pages du site +│ │ ├── Home.jsx +│ │ ├── ToolsComparison.jsx +│ │ ├── WebsiteCarbon.jsx +│ │ ├── EnvironmentalAudit.jsx +│ │ ├── RGESN.jsx +│ │ ├── Accessibility.jsx +│ │ ├── WAVE.jsx +│ │ ├── RGAA.jsx +│ │ ├── EcoGuide.jsx +│ │ └── Conclusion.jsx +│ ├── App.jsx # Composant principal +│ ├── main.jsx # Point d'entrée +│ └── index.css # Styles globaux +├── index.html # Template HTML +├── package.json # Dépendances +└── vite.config.js # Configuration Vite +``` + +## 🎨 Fonctionnalités + +### Navigation +- **Menu latéral responsive** avec sections organisées +- **Navigation fluide** entre les différentes sections +- **Design moderne** avec palette de couleurs verte (écoresponsable) + +### Contenu Interactif +- **Graphique radar** pour comparer les outils d'écoconception +- **Tableaux comparatifs** détaillés +- **Cartes d'information** visuelles +- **Checklists** prêtes à l'emploi +- **Exemples de code** avec syntaxe highlight + +### Design Responsive +- **Adapté mobile, tablette et desktop** +- **Sidebar repliable** sur mobile +- **Grids flexibles** qui s'adaptent à la taille d'écran + +## 🌱 Bonnes Pratiques Écoconception Appliquées + +Ce site pratique ce qu'il enseigne : + +✅ **Images optimisées** : Utilisation d'icônes Font Awesome (vectoriel) +✅ **CSS léger** : Pas de framework CSS lourd, styles custom optimisés +✅ **Code minifié** : Build Vite optimise automatiquement +✅ **Lazy loading** : React Router charge les pages à la demande +✅ **Polices système** : Utilisation des polices natives du système +✅ **Palette sobre** : Couleurs limitées et cohérentes + +## 📊 Résultats Attendus + +Après avoir suivi ce TP, vous serez capable de : +- ✅ Mesurer l'empreinte environnementale d'un site web +- ✅ Auditer l'accessibilité avec des outils professionnels +- ✅ Appliquer les référentiels RGESN et RGAA +- ✅ Proposer des recommandations techniques priorisées +- ✅ Documenter et justifier vos choix d'écoconception +- ✅ Intégrer ces pratiques dans vos projets futurs + +## 📚 Ressources Complémentaires + +### Référentiels +- [RGESN](https://ecoresponsable.numerique.gouv.fr/publications/referentiel-general-ecoconception/) +- [RGAA](https://accessibilite.numerique.gouv.fr/) +- [WCAG 2.1](https://www.w3.org/WAI/WCAG21/quickref/) + +### Outils +- [Website Carbon Calculator](https://www.websitecarbon.com/) +- [EcoIndex](https://www.ecoindex.fr/) +- [WAVE](https://wave.webaim.org/) +- [Lighthouse](https://developers.google.com/web/tools/lighthouse) + +### Communautés +- [Collectif Conception Numérique Responsable](https://collectif.greenit.fr/) +- [Institut du Numérique Responsable](https://institutnr.org/) +- [WebAIM](https://webaim.org/) + +## 👨‍🏫 Auteur du TP + +**Vincent Courboulay** + +## 📄 Licence + +Ce projet est à usage éducatif dans le cadre du TP d'écoconception web. + +## 🤝 Contribution + +Si vous trouvez des améliorations ou des corrections à apporter, n'hésitez pas à proposer vos modifications. + +--- + +**Bon travail et n'oubliez pas : chaque ligne de code compte pour un numérique plus durable et inclusif ! 🌍** diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..59cf2e6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + # Web site service + web-site: + build: + context: . + dockerfile: Dockerfile + ports: + - "3000" + environment: + - NODE_ENV=production + restart: unless-stopped \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..c3a2fd5 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + TP Écoconception Web + + + +
+ + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..09c63ac --- /dev/null +++ b/nginx.conf @@ -0,0 +1,28 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Gestion du SPA (Single Page Application) + location / { + try_files $uri $uri/ /index.html; + } + + # Cache pour les assets statiques + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Compression gzip pour l'écoconception + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json application/javascript; + + # Sécurité headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4b5cceb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2079 @@ +{ + "name": "ecoconception-web-tp", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ecoconception-web-tp", + "version": "1.0.0", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0", + "recharts": "^2.10.0" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@vitejs/plugin-react": "^4.2.1", + "vite": "^5.0.8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.25", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.25.tgz", + "integrity": "sha512-oSVZmGtDPmRZtVDqvdKUi/qgCsWp5IDY29wp8na8Bj4B3cc99hfNzvNhlMkVVxctkAOGUA3Km7MMpBHAnWfcIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", + "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001746", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", + "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.227", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", + "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.2.tgz", + "integrity": "sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", + "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", + "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/rollup": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.20", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", + "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e6691f7 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "ecoconception-web-tp", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0", + "recharts": "^2.10.0" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@vitejs/plugin-react": "^4.2.1", + "vite": "^5.0.8" + } +} diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b438fb5 --- /dev/null +++ b/src/App.css @@ -0,0 +1,27 @@ +.app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.app-content { + display: flex; + flex: 1; + margin-top: 70px; +} + +.main-content { + flex: 1; + padding: 2rem; + width: 100%; +} + +@media (max-width: 768px) { + .main-content { + padding: 1rem; + } + + .app-content { + margin-top: 60px; + } +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..1876e4a --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react' +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' +import Header from './components/Header' +import Sidebar from './components/Sidebar' +import Footer from './components/Footer' +import Home from './pages/Home' +import ToolsComparison from './pages/ToolsComparison' +import WebsiteCarbon from './pages/WebsiteCarbon' +import EnvironmentalAudit from './pages/EnvironmentalAudit' +import RGESN from './pages/RGESN' +import Accessibility from './pages/Accessibility' +import WAVE from './pages/WAVE' +import RGAA from './pages/RGAA' +import EcoGuide from './pages/EcoGuide' +import Conclusion from './pages/Conclusion' +import './App.css' + +function App() { + const [sidebarOpen, setSidebarOpen] = useState(false) + + return ( + +
+
setSidebarOpen(!sidebarOpen)} /> +
+ setSidebarOpen(false)} /> +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+
+
+
+
+ ) +} + +export default App diff --git a/src/components/Footer.css b/src/components/Footer.css new file mode 100644 index 0000000..cdcfcb5 --- /dev/null +++ b/src/components/Footer.css @@ -0,0 +1,43 @@ +.footer { + background: var(--primary-dark); + color: var(--white); + padding: 1.5rem 2rem; + margin-top: auto; +} + +.footer-content { + max-width: 1400px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1rem; +} + +.footer-section { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.footer-section i { + font-size: 1.2rem; + color: var(--accent); +} + +.footer-section p { + margin: 0; + font-size: 0.9rem; +} + +@media (max-width: 768px) { + .footer-content { + flex-direction: column; + text-align: center; + } + + .footer-section { + justify-content: center; + } +} diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx new file mode 100644 index 0000000..c1cde6a --- /dev/null +++ b/src/components/Footer.jsx @@ -0,0 +1,24 @@ +import React from 'react' +import './Footer.css' + +function Footer() { + return ( + + ) +} + +export default Footer diff --git a/src/components/Header.css b/src/components/Header.css new file mode 100644 index 0000000..03e344c --- /dev/null +++ b/src/components/Header.css @@ -0,0 +1,77 @@ +.header { + position: fixed; + top: 0; + left: 0; + right: 0; + background: linear-gradient(135deg, var(--primary-dark), var(--primary)); + color: var(--white); + padding: 1rem 2rem; + box-shadow: 0 2px 10px var(--shadow); + z-index: 1000; +} + +.header-content { + max-width: 1400px; + margin: 0 auto; + display: flex; + align-items: center; + gap: 2rem; +} + +.menu-btn { + background: none; + border: none; + color: var(--white); + font-size: 1.5rem; + cursor: pointer; + padding: 0.5rem; + display: flex; + align-items: center; + transition: transform 0.3s; +} + +.menu-btn:hover { + transform: scale(1.1); +} + +.header-title { + display: flex; + align-items: center; + gap: 0.75rem; + flex: 1; +} + +.header-icon { + font-size: 1.8rem; +} + +.header-title h1 { + font-size: 1.5rem; + font-weight: 600; +} + +.header-subtitle { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.95rem; + opacity: 0.9; +} + +@media (max-width: 768px) { + .header { + padding: 0.75rem 1rem; + } + + .header-content { + gap: 1rem; + } + + .header-title h1 { + font-size: 1.1rem; + } + + .header-subtitle { + display: none; + } +} diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 0000000..424c924 --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,24 @@ +import React from 'react' +import './Header.css' + +function Header({ toggleSidebar }) { + return ( +
+
+ +
+ +

TP Écoconception Web

+
+
+ + Numérique Responsable +
+
+
+ ) +} + +export default Header diff --git a/src/components/Sidebar.css b/src/components/Sidebar.css new file mode 100644 index 0000000..7cf75f7 --- /dev/null +++ b/src/components/Sidebar.css @@ -0,0 +1,108 @@ +.sidebar-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 998; +} + +.sidebar-overlay.active { + display: block; +} + +.sidebar { + position: fixed; + left: 0; + top: 70px; + bottom: 0; + width: 280px; + background: var(--white); + box-shadow: 2px 0 10px var(--shadow); + transform: translateX(-100%); + transition: transform 0.3s ease; + overflow-y: auto; + z-index: 999; +} + +.sidebar.open { + transform: translateX(0); +} + +.sidebar-nav { + padding: 1rem 0; +} + +.menu-item { + margin-bottom: 0.5rem; +} + +.nav-link, +.nav-section { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.875rem 1.5rem; + color: var(--text); + text-decoration: none; + transition: all 0.3s; + font-weight: 500; +} + +.nav-section { + color: var(--primary); + font-weight: 600; + cursor: default; + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.nav-link:hover { + background: var(--accent); + color: var(--primary-dark); +} + +.nav-link.active { + background: var(--primary); + color: var(--white); + border-left: 4px solid var(--primary-dark); +} + +.sub-menu { + padding-left: 1rem; +} + +.sub-link { + font-size: 0.9rem; + padding: 0.75rem 1.5rem; +} + +.nav-link i { + width: 20px; + text-align: center; + font-size: 1.1rem; +} + +@media (min-width: 1024px) { + .sidebar { + transform: translateX(0); + } + + .sidebar-overlay { + display: none !important; + } + + .main-content { + margin-left: 280px; + } +} + +@media (max-width: 768px) { + .sidebar { + top: 60px; + width: 260px; + } +} diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx new file mode 100644 index 0000000..3b3e02b --- /dev/null +++ b/src/components/Sidebar.jsx @@ -0,0 +1,76 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' +import './Sidebar.css' + +function Sidebar({ isOpen, closeSidebar }) { + const menuItems = [ + { path: '/', icon: 'fa-home', label: 'Accueil' }, + { + label: 'Audit Initial', + icon: 'fa-search', + children: [ + { path: '/tools-comparison', icon: 'fa-chart-bar', label: 'Comparaison d\'outils' }, + { path: '/website-carbon', icon: 'fa-calculator', label: 'Website Carbon' }, + { path: '/environmental-audit', icon: 'fa-leaf', label: 'Audit Environnemental' }, + { path: '/rgesn', icon: 'fa-book', label: 'RGESN' }, + ] + }, + { + label: 'Accessibilité', + icon: 'fa-universal-access', + children: [ + { path: '/accessibility', icon: 'fa-users', label: 'Principes' }, + { path: '/wave', icon: 'fa-water', label: 'WAVE' }, + { path: '/rgaa', icon: 'fa-check-circle', label: 'RGAA' }, + ] + }, + { path: '/eco-guide', icon: 'fa-map', label: 'Guide Écoconception' }, + { path: '/conclusion', icon: 'fa-flag-checkered', label: 'Conclusion' }, + ] + + return ( + <> +
+ + + ) +} + +export default Sidebar diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..aa46b85 --- /dev/null +++ b/src/index.css @@ -0,0 +1,60 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --primary: #2d6a4f; + --primary-dark: #1b4332; + --primary-light: #40916c; + --secondary: #52b788; + --accent: #74c69d; + --bg: #f8f9fa; + --text: #212529; + --text-light: #6c757d; + --white: #ffffff; + --border: #dee2e6; + --shadow: rgba(0, 0, 0, 0.1); + --danger: #dc3545; + --warning: #ffc107; + --success: #28a745; + --info: #17a2b8; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: var(--bg); + color: var(--text); + line-height: 1.6; +} + +code { + font-family: 'Courier New', monospace; + background: #f4f4f4; + padding: 2px 6px; + border-radius: 4px; + font-size: 0.9em; +} + +/* Scrollbar Styling */ +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track { + background: var(--bg); +} + +::-webkit-scrollbar-thumb { + background: var(--primary); + border-radius: 5px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--primary-dark); +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..5cc5991 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + , +) diff --git a/src/pages/Accessibility.jsx b/src/pages/Accessibility.jsx new file mode 100644 index 0000000..60285a7 --- /dev/null +++ b/src/pages/Accessibility.jsx @@ -0,0 +1,384 @@ +import React from 'react' +import './SharedPages.css' + +function Accessibility() { + return ( +
+
+ +

Principes de l'Accessibilité Web

+

Vers un numérique inclusif

+
+ +
+

Qu'est-ce que l'accessibilité numérique ?

+

+ L'accessibilité numérique consiste à rendre les sites web et applications utilisables par tous, + y compris les personnes en situation de handicap. Cela englobe les handicaps visuels, + auditifs, moteurs, cognitifs et temporaires. +

+
+
+
15%
+
de la population mondiale vit avec un handicap
+
+
+
1 Md
+
de personnes dans le monde
+
+
+
70%
+
des sites ne sont pas accessibles
+
+
+
+ +
+

Types de handicaps concernés

+
+ {[ + { + type: 'Handicap visuel', + icon: 'fa-eye-slash', + examples: 'Cécité, malvoyance, daltonisme', + needs: 'Lecteurs d\'écran, contrastes élevés, alternatives textuelles', + color: '#2d6a4f' + }, + { + type: 'Handicap auditif', + icon: 'fa-deaf', + examples: 'Surdité, malentendance', + needs: 'Sous-titres, transcriptions textuelles, langue des signes', + color: '#52b788' + }, + { + type: 'Handicap moteur', + icon: 'fa-wheelchair', + examples: 'Mobilité réduite, tremblements, paralysie', + needs: 'Navigation au clavier, zones cliquables larges, commandes vocales', + color: '#74c69d' + }, + { + type: 'Handicap cognitif', + icon: 'fa-brain', + examples: 'Dyslexie, TDAH, troubles de la mémoire', + needs: 'Langage simple, navigation claire, temps de lecture adaptable', + color: '#95d5b2' + }, + { + type: 'Handicap temporaire', + icon: 'fa-band-aid', + examples: 'Bras cassé, conjonctivite, environnement bruyant', + needs: 'Adaptabilité, multi-modalité', + color: '#b7e4c7' + } + ].map((disability, index) => ( +
+

+ + {disability.type} +

+
+ Exemples : {disability.examples} +
+
+ Besoins : {disability.needs} +
+
+ ))} +
+
+ +
+

WCAG : Les 4 principes fondamentaux

+

+ Les Web Content Accessibility Guidelines (WCAG) définissent 4 principes universels pour l'accessibilité web, + repris par le RGAA français : +

+ +
+
+
+
1
+

Perceptible

+
+

Les utilisateurs doivent pouvoir percevoir l'information

+
    +
  • Fournir des alternatives textuelles aux contenus non textuels (images, vidéos)
  • +
  • Assurer des contrastes suffisants (au moins 4.5:1 pour le texte)
  • +
  • Ne pas se fier uniquement à la couleur pour transmettre l'information
  • +
  • Rendre le contenu adaptable (responsive, redimensionnable)
  • +
+
+ Exemple : Une image doit avoir un attribut alt descriptif pour les lecteurs d'écran. +
+
+ +
+
+
2
+

Utilisable

+
+

Les utilisateurs doivent pouvoir utiliser l'interface

+
    +
  • Toutes les fonctionnalités accessibles au clavier (sans souris)
  • +
  • Laisser suffisamment de temps pour lire et utiliser le contenu
  • +
  • Ne pas utiliser d'éléments susceptibles de provoquer des crises (flashs)
  • +
  • Fournir des aides à la navigation (menu, fil d'Ariane, liens d'évitement)
  • +
+
+ Exemple : Un menu déroulant doit être navigable avec les flèches du clavier et la touche Entrée. +
+
+ +
+
+
3
+

Compréhensible

+
+

Les utilisateurs doivent pouvoir comprendre l'information et l'interface

+
    +
  • Rendre le texte lisible et compréhensible (langage simple, abréviations expliquées)
  • +
  • Faire en sorte que les pages fonctionnent de manière prévisible
  • +
  • Aider les utilisateurs à éviter et corriger les erreurs (validation de formulaires claire)
  • +
  • Fournir des instructions claires et des labels explicites
  • +
+
+ Exemple : Un message d'erreur de formulaire doit expliquer clairement le problème et comment le corriger. +
+
+ +
+
+
4
+

Robuste

+
+

Le contenu doit être compatible avec les technologies actuelles et futures

+
    +
  • Utiliser du HTML valide et sémantique
  • +
  • Assurer la compatibilité avec les technologies d'assistance (lecteurs d'écran)
  • +
  • Utiliser ARIA (Accessible Rich Internet Applications) quand nécessaire
  • +
  • Tester avec différents navigateurs et technologies d'assistance
  • +
+
+ Exemple : Utiliser les balises sémantiques <nav>, <main>, <article> plutôt que des <div> génériques. +
+
+
+
+ +
+

Le RGAA : Référentiel français

+
+
+ Nom complet: + Référentiel Général d'Amélioration de l'Accessibilité +
+
+ Créé par: + DINUM (Direction Interministérielle du Numérique) +
+
+ Base: + WCAG 2.1 (adapté au contexte français) +
+
+ Version actuelle: + RGAA 4.1 (2021) +
+
+ Obligation légale: + Services publics, entreprises > 250M€ CA +
+
+ +
+

Les 13 thèmes du RGAA

+
+ {[ + 'Images', 'Cadres', 'Couleurs', 'Multimédia', 'Tableaux', 'Liens', + 'Scripts', 'Éléments obligatoires', 'Structuration', 'Présentation', + 'Formulaires', 'Navigation', 'Consultation' + ].map((theme, index) => ( +
+ {theme} +
+ ))} +
+
+
+ +
+

Bénéfices de l'accessibilité

+
+
+

+ Social & Éthique +

+
    +
  • Inclusion de tous les utilisateurs
  • +
  • Respect des droits fondamentaux
  • +
  • Image responsable
  • +
+
+ +
+

+ Business +

+
    +
  • Élargissement de l'audience (+15%)
  • +
  • Amélioration du SEO
  • +
  • Réduction du taux de rebond
  • +
+
+ +
+

+ Légal +

+
    +
  • Conformité réglementaire
  • +
  • Éviter les sanctions
  • +
  • Anticipation des obligations
  • +
+
+ +
+

+ Technique +

+
    +
  • Code plus robuste et maintenable
  • +
  • Meilleure compatibilité
  • +
  • Performance améliorée
  • +
+
+
+
+ +
+

Bonnes pratiques essentielles

+
+ {[ + { + practice: 'Ajouter des attributs alt aux images', + code: 'Logo de l\'organisation', + impact: 'Essentiel pour les lecteurs d\'écran' + }, + { + practice: 'Assurer un contraste suffisant', + code: 'Ratio minimum 4.5:1 pour texte normal, 3:1 pour texte large', + impact: 'Lisibilité pour malvoyants et daltoniens' + }, + { + practice: 'Structurer avec des titres hiérarchiques', + code: '

,

,

... sans sauter de niveau', + impact: 'Navigation et compréhension de la structure' + }, + { + practice: 'Rendre les formulaires accessibles', + code: '', + impact: 'Association claire label/champ' + }, + { + practice: 'Navigation au clavier', + code: 'tabindex, focus visible, touches Entrée/Échap fonctionnelles', + impact: 'Utilisateurs sans souris' + }, + { + practice: 'Utiliser ARIA avec prudence', + code: 'role="navigation", aria-label="Menu principal"', + impact: 'Enrichir la sémantique pour technologies d\'assistance' + } + ].map((item, index) => ( +
+

{item.practice}

+
+ {item.code} +
+

+ Impact : {item.impact} +

+
+ ))} +

+
+ +
+ +
+ L'accessibilité, c'est pour tout le monde +

Un site accessible est plus utilisable, plus performant, mieux référencé et conforme à la loi. + L'accessibilité n'est pas une contrainte, c'est une opportunité d'améliorer l'expérience de tous les utilisateurs.

+
+
+
+ ) +} + +export default Accessibility diff --git a/src/pages/Conclusion.jsx b/src/pages/Conclusion.jsx new file mode 100644 index 0000000..09eb7c0 --- /dev/null +++ b/src/pages/Conclusion.jsx @@ -0,0 +1,316 @@ +import React from 'react' +import './SharedPages.css' + +function Conclusion() { + return ( +
+
+ +

Conclusion

+

Bilan et perspectives du numérique responsable

+
+ +
+

Synthèse de l'analyse

+

+ Ce compte rendu présente une analyse complète des enjeux du numérique responsable, + à la fois sous l'angle environnemental et social. L'audit réalisé sur ville-de-chauray.fr + a permis d'identifier des axes d'amélioration concrets pour réduire l'impact environnemental et améliorer l'accessibilité. +

+ +
+
+
🌱
+

Écoconception

+
    +
  • Utilisation d'outils d'audit (Website Carbon, EcoIndex)
  • +
  • Mesure de l'impact CO₂ et optimisation
  • +
  • Application du RGESN
  • +
  • Recommandations priorisées
  • +
+
+ +
+
+

Accessibilité

+
    +
  • Principes POUR (Perceptible, Utilisable, Compréhensible, Robuste)
  • +
  • Audit avec WAVE
  • +
  • Conformité RGAA
  • +
  • Corrections techniques
  • +
+
+ +
+
🚀
+

Méthodologie

+
    +
  • Guide d'écoconception pour projet SAE
  • +
  • Planning et checklists
  • +
  • Documentation technique
  • +
  • Argumentaire pour décideurs
  • +
+
+
+
+ +
+

Volet Écoconception Environnemental

+ +
+

Compétences acquises

+
    +
  • + Mesurer l'impact : Vous savez utiliser Website Carbon Calculator pour estimer + l'empreinte carbone d'une page web (émissions de CO₂, poids des pages, nombre de requêtes) +
  • +
  • + Analyser et comparer : Vous maîtrisez la comparaison d'outils d'écoconception + (EcoIndex, Lighthouse, GreenIT-Analysis, Scaphandre) +
  • +
  • + Appliquer le RGESN : Vous pouvez auditer un service selon les 79 critères du + Référentiel Général d'Écoconception des Services Numériques +
  • +
  • + Proposer des solutions : Vous êtes capable de rédiger un rapport d'audit complet + avec diagnostic, solutions techniques et plan d'action réaliste +
  • +
+
+ +
+

+ Résultats types attendus +

+
    +
  • Réduction de 40-50% de l'empreinte carbone d'un site
  • +
  • Amélioration du score Lighthouse de 20-30 points
  • +
  • Diminution du poids des pages de 30-60%
  • +
  • Temps de chargement divisé par 2
  • +
+
+
+ +
+

Volet Accessibilité et Inclusion

+ +
+

Compétences acquises

+
    +
  • + Identifier les erreurs : Vous savez utiliser WAVE pour détecter rapidement + les problèmes d'accessibilité (images sans alt, contrastes insuffisants, structure incohérente) +
  • +
  • + Comprendre les 4 principes : Vous maîtrisez les principes POUR + (Perceptible, Utilisable, Compréhensible, Robuste) +
  • +
  • + Auditer selon le RGAA : Vous pouvez évaluer la conformité d'un site + selon les 106 critères du référentiel français +
  • +
  • + Proposer des corrections : Vous êtes capable de prioriser les actions + correctives en fonction de leur impact et faisabilité +
  • +
+
+ +
+

+ Impact social +

+
    +
  • 15% de la population mondiale concernée (1 milliard de personnes)
  • +
  • +20-35% d'audience potentielle avec un site accessible
  • +
  • Meilleure expérience pour tous les utilisateurs (seniors, mobiles, contextes difficiles)
  • +
+
+
+ +
+

Vision Globale : Numérique Responsable

+ +
+

+ Service Numérique Idéal = Sobre + Performant + Accessible +

+

+ En combinant écoconception et accessibilité, vous créez des services de qualité qui bénéficient à tous +

+
+ +
+
+

Vous savez argumenter

+

+ Vous êtes capable de convaincre décideurs et clients sur l'importance de ces démarches, + en mettant en avant les bénéfices environnementaux, sociaux, économiques et juridiques. +

+
+ +
+

Vous savez mobiliser

+

+ Vous pouvez proposer un plan d'action concret avec échéancier, acteurs concernés, + et indicateurs de suivi pour mobiliser les parties prenantes. +

+
+ +
+

Vous avez un esprit critique

+

+ Vous avez développé un esprit critique face aux outils et référentiels, + en identifiant leurs forces et leurs limites. +

+
+
+
+ +
+

Bénéfices Multiples

+ +
+
+ +

Environnementaux

+

Réduction empreinte carbone, économie ressources

+
+ +
+ +

Sociaux

+

Inclusion, accessibilité universelle

+
+ +
+ +

Économiques

+

Réduction coûts, meilleur SEO, + conversions

+
+ +
+ +

Juridiques

+

Conformité légale, anticipation obligations

+
+
+
+ +
+

Impact Professionnel

+ +
+
+

Ces compétences sont recherchées

+

+ L'écoconception et l'accessibilité sont de plus en plus demandées dans le monde professionnel : +

+
    +
  • Obligations légales croissantes (loi REEN, accessibilité numérique)
  • +
  • RSE prioritaire pour les entreprises
  • +
  • Demande clients sensibles aux enjeux environnementaux et sociaux
  • +
  • Différenciation concurrentielle importante
  • +
+
+ +
+

Valorisez ces compétences

+
    +
  • Intégrez ces pratiques dans vos projets personnels et professionnels
  • +
  • Mentionnez-les sur votre CV et portfolio
  • +
  • Obtenez des certifications (Opquast, accessibilité numérique)
  • +
  • Partagez vos réalisations sur LinkedIn et GitHub
  • +
  • Contribuez à des projets open source écoresponsables
  • +
+
+
+
+ +
+

Pour Aller Plus Loin

+ +
+
+

+ Ressources complémentaires +

+ +
+ +
+

+ Communautés +

+
    +
  • Collectif Conception Numérique Responsable (CNUMR)
  • +
  • Association Point de MIR (numérique responsable)
  • +
  • Institut du Numérique Responsable (INR)
  • +
  • Club Green IT
  • +
  • Access42 (accessibilité numérique)
  • +
+
+ +
+

+ Certifications +

+
    +
  • Opquast : Certification qualité web
  • +
  • Accessibilité numérique : Formation et certification Access42
  • +
  • Écoconception web : Formation GreenIT
  • +
  • WCAG : Certifications internationales
  • +
+
+
+
+ +
+

Message Final

+ +
+
🌍
+

+ Chaque ligne de code compte pour un numérique plus durable et inclusif +

+

+ L'écoconception et l'accessibilité ne sont pas des contraintes, mais des leviers d'innovation et de qualité. + En appliquant ces principes, vous contribuez à un numérique meilleur pour tous et pour la planète. +

+
+
+
-40%
+
Empreinte carbone
+
+
+
+35%
+
Audience potentielle
+
+
+
100%
+
Utilisateurs satisfaits
+
+
+
+
+ +
+ +
+ Conclusion du compte rendu +

Cette analyse démontre l'importance d'intégrer l'écoconception et l'accessibilité dans le développement web. + Les audits réalisés ont permis d'identifier des problèmes concrets et de proposer des recommandations actionables pour améliorer la performance environnementale et l'accessibilité du site ville-de-chauray.fr.

+
+
+
+ ) +} + +export default Conclusion diff --git a/src/pages/EcoGuide.jsx b/src/pages/EcoGuide.jsx new file mode 100644 index 0000000..3328c55 --- /dev/null +++ b/src/pages/EcoGuide.jsx @@ -0,0 +1,463 @@ +import React from 'react' +import './SharedPages.css' + +function EcoGuide() { + return ( +
+
+ +

Guide d'Écoconception pour un Projet SAE

+

Méthodologie complète pour intégrer l'écoconception

+
+ +
+

Objectif

+

+ Intégrer l'écoconception à chaque étape du projet, de la conception à la livraison, + en utilisant les outils et référentiels analysés (RGESN, RGAA, EcoIndex, Lighthouse, WAVE). +

+
+ +
+

Organisation et Planning Type (4 semaines)

+ +
+
+
+
1
+

Semaine 1 : Cadrage et Conception

+
+ +
+
+

Tâches écoconception

+
    +
  • Définir les objectifs écoresponsables du projet (ex: "-30% CO₂ vs moyenne secteur")
  • +
  • Choisir un hébergeur vert (Infomaniak, GreenGeeks, OVH bas carbone)
  • +
  • Concevoir une architecture sobre (statique si possible, SSG/SSR, API légère)
  • +
  • Rédiger une checklist personnalisée des bonnes pratiques
  • +
  • Nommer un référent écoconception dans l'équipe
  • +
+
+ +
+

Livrables / Outils

+
    +
  • 📄 Charte d'écoconception du projet
  • +
  • 🌐 Comparatif d'hébergeurs (Green Web Foundation)
  • +
  • 🏗️ Schéma d'architecture technique
  • +
  • ✅ Checklist RGESN personnalisée
  • +
+
+
+
+ +
+
+
2
+

Semaine 2 : Design et Maquettes

+
+ +
+
+

Tâches écoconception

+
    +
  • Concevoir des maquettes sobres (limiter animations, couleurs, polices)
  • +
  • Valider l'accessibilité dès les maquettes (contrastes, structure)
  • +
  • Prévoir un mode sombre pour économiser l'énergie (écrans OLED)
  • +
  • Limiter à 2 familles de polices maximum (privilégier system fonts)
  • +
  • Optimiser les images dès le design (taille, format)
  • +
+
+ +
+

Livrables / Outils

+
    +
  • 🎨 Maquettes Figma/Adobe XD sobres
  • +
  • 🎨 WebAIM Contrast Checker (validation contrastes)
  • +
  • 📊 Rapport d'accessibilité des maquettes
  • +
  • 📐 Guide de style écoconçu
  • +
+
+
+
+ +
+
+
3
+

Semaine 3 : Développement

+
+ +
+
+

Tâches écoconception

+
    +
  • Développer en respectant la checklist (code, images, requêtes)
  • +
  • Optimiser toutes les images (WebP/AVIF, compression, lazy loading)
  • +
  • Minifier CSS/JS, supprimer code mort
  • +
  • Limiter les librairies externes (privilégier Vanilla JS si possible)
  • +
  • Tester régulièrement l'empreinte environnementale (EcoIndex, Lighthouse)
  • +
+
+ +
+

Livrables / Outils

+
    +
  • 💻 Code commenté et optimisé
  • +
  • 🔍 Lighthouse, EcoIndex (tests continus)
  • +
  • 📈 Tableau de suivi des indicateurs (poids, CO₂, requêtes)
  • +
  • 📦 Build optimisé (Webpack, Vite)
  • +
+
+
+
+ +
+
+
4
+

Semaine 4 : Tests, Optimisation, Livraison

+
+ +
+
+

Tâches écoconception

+
    +
  • Auditer le projet final (écoconception + accessibilité)
  • +
  • Corriger les derniers points bloquants
  • +
  • Tester avec lecteur d'écran (NVDA, VoiceOver)
  • +
  • Rédiger une documentation d'écoconception pour mainteneurs
  • +
  • Communiquer sur les efforts écoresponsables (badge, footer)
  • +
+
+ +
+

Livrables / Outils

+
    +
  • 📋 Rapport d'audit final (RGESN, RGAA, WAVE)
  • +
  • ✅ Liste des optimisations réalisées
  • +
  • 📚 Fiche mémo des bonnes pratiques
  • +
  • 🎯 Documentation technique
  • +
+
+
+
+
+
+ +
+

Checklist d'écoconception à intégrer dès la conception

+ +
+
+

1️⃣ Cadrage et Gouvernance

+
    +
  • ☐ Définir des objectifs écoresponsables clairs (ex: "-30% poids vs moyenne secteur")
  • +
  • ☐ Nommer un référent écoconception dans l'équipe
  • +
  • ☐ Intégrer l'écoconception dans le cahier des charges
  • +
  • ☐ Choisir un hébergeur vert (Infomaniak, GreenGeeks, OVH bas carbone)
  • +
  • ☐ Prévoir un budget temps pour les audits et optimisations
  • +
+
+ +
+

2️⃣ Design et Expérience Utilisateur

+
    +
  • ☐ Limiter le nombre de polices (max 2 familles, privilégier system fonts)
  • +
  • ☐ Éviter les animations inutiles (autoplay, vidéos en fond, carrousels)
  • +
  • ☐ Utiliser des couleurs sobres et vérifier les contrastes (WCAG AA: 4.5:1)
  • +
  • ☐ Concevoir des maquettes légères (éviter images haute résolution inutiles)
  • +
  • ☐ Prévoir un mode sombre pour réduire consommation énergétique (OLED)
  • +
+
+ +
+

3️⃣ Développement Frontend

+
    +
  • ☐ Optimiser les images (WebP/AVIF, compression TinyPNG, lazy loading)
  • +
  • ☐ Minifier et regrouper les fichiers CSS/JS
  • +
  • ☐ Limiter l'usage de librairies externes (privilégier Vanilla JS)
  • +
  • ☐ Cacher le contenu non critique (chargement différé des iframes)
  • +
  • ☐ Utiliser un système de cache efficace (Cache-Control headers)
  • +
+
+ +
+

4️⃣ Développement Backend

+
    +
  • ☐ Choisir un langage/framework sobre (éviter surdimensionnement)
  • +
  • ☐ Optimiser les requêtes base de données (index, limiter jointures)
  • +
  • ☐ Mettre en place une API légère (éviter surdimensionnement JSON)
  • +
  • ☐ Désactiver les logs inutiles en production
  • +
+
+ +
+

5️⃣ Hébergement et Infrastructure

+
    +
  • ☐ Vérifier que l'hébergeur utilise des énergies renouvelables
  • +
  • ☐ Configurer un CDN pour réduire latence et transferts
  • +
  • ☐ Activer la compression Gzip/Brotli sur le serveur
  • +
  • ☐ Planifier des sauvegardes optimisées (pas de redondances inutiles)
  • +
+
+ +
+

6️⃣ Tests et Validation

+
    +
  • ☐ Auditer régulièrement avec EcoIndex/Website Carbon (empreinte)
  • +
  • ☐ Tester avec Lighthouse (performances, accessibilité)
  • +
  • ☐ Valider l'accessibilité avec WAVE et lecteurs d'écran
  • +
  • ☐ Corriger les erreurs bloquantes avant la livraison
  • +
  • ☐ Documenter les choix d'écoconception pour mainteneurs futurs
  • +
+
+ +
+

7️⃣ Livraison et Communication

+
    +
  • ☐ Rédiger une fiche d'écoconception pour les utilisateurs
  • +
  • ☐ Communiquer sur les efforts (badge "Site bas carbone", mention footer)
  • +
  • ☐ Prévoir un suivi post-livraison (audit trimestriel)
  • +
+
+
+
+ +
+

Outils Recommandés

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BesoinOutils / Référentiels
Audit écoconceptionEcoIndex, Website Carbon, GreenIT Analysis Method
Audit accessibilitéWAVE, RGAA, Lighthouse Accessibility
Optimisation imagesTinyPNG, Squoosh, ImageOptim
Test de performancesLighthouse, WebPageTest, GTmetrix
Hébergement vertGreen Web Foundation, The Green Web Directory
Contraste de couleursWebAIM Contrast Checker, Stark (Figma)
DocumentationNotion, Markdown, GitHub Wiki
+
+
+ +
+

Conseils pour Réussir

+ +
+ {[ + { + icon: 'fa-users', + title: 'Impliquez toute l\'équipe', + content: 'L\'écoconception n\'est pas seulement l\'affaire des développeurs. Designers, chefs de projet et rédacteurs doivent aussi adopter ces bonnes pratiques.' + }, + { + icon: 'fa-sort-amount-down', + title: 'Priorisez', + content: 'Concentrez-vous d\'abord sur les actions à fort impact et faciles à mettre en œuvre (ex: optimisation des images, choix de l\'hébergeur).' + }, + { + icon: 'fa-file-alt', + title: 'Documentez', + content: 'Notez chaque choix d\'écoconception et ses justifications pour faciliter la maintenance future.' + }, + { + icon: 'fa-vial', + title: 'Testez tôt et souvent', + content: 'Intégrez les audits dès les premières maquettes et à chaque sprint de développement.' + }, + { + icon: 'fa-graduation-cap', + title: 'Sensibilisez', + content: 'Expliquez à vos utilisateurs ou clients pourquoi ces efforts sont importants (ex: via une page "Notre engagement écoresponsable").' + } + ].map((tip, index) => ( +
+
+ +
+
+

{tip.title}

+

{tip.content}

+
+
+ ))} +
+
+ +
+

Fiche de Suivi Hebdomadaire

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SemaineActions écoconception réaliséesIndicateurs (avant/après)Prochaines étapes
1Choix de l'hébergeur vert, rédaction de la checklist-Maquettes sobres
2Optimisation des maquettes, test de contrasteContraste: 2.3:1 → 7.8:1 ✓Développement
3Minification CSS/JS, lazy loading imagesPoids: 2.5 MB → 1.2 MB (-52%)Audit final
4Correction des erreurs Lighthouse, documentationScore Lighthouse: 72 → 95 (+23 pts)Livraison
+
+
+ +
+

Pourquoi ce guide est utile ?

+ +
+
+

+ Intégration progressive +

+

Permet d'intégrer l'écoconception de manière progressive et réaliste sur un mois

+
+ +
+

+ Outils concrets +

+

S'appuie sur des outils concrets et des référentiels officiels (RGESN, RGAA)

+
+ +
+

+ Justification +

+

Prépare à justifier les choix face à un client ou un jury

+
+ +
+

+ Documentation +

+

Habitue à documenter et communiquer sur les démarches écoresponsables

+
+
+
+ +
+ +
+ Méthodologie complète +

Ce guide présente une méthode pour intégrer l'écoconception et l'accessibilité à chaque étape d'un projet. + Cette méthodologie permet de créer des services numériques sobres, performants, accessibles et responsables.

+
+
+
+ ) +} + +export default EcoGuide diff --git a/src/pages/EnvironmentalAudit.jsx b/src/pages/EnvironmentalAudit.jsx new file mode 100644 index 0000000..8df3c42 --- /dev/null +++ b/src/pages/EnvironmentalAudit.jsx @@ -0,0 +1,327 @@ +import React from 'react' +import './SharedPages.css' + +function EnvironmentalAudit() { + const auditResults = [ + { tool: 'Website Carbon', co2: '0.62g', grade: 'F', pageWeight: '-', requests: '-' }, + { tool: 'EcoIndex', co2: '2.86kg/1000 visites', grade: 'G (7/100)', pageWeight: '12.456 MB', requests: '206' }, + { tool: 'WAVE', co2: '-', grade: '29 erreurs, 326 contraste', pageWeight: '-', requests: '-' } + ] + + const recommendations = [ + { + priority: 'Haute', + action: 'Optimiser les images', + method: 'Utiliser WebP/AVIF, compression TinyPNG, lazy loading', + gain: '-40% poids des pages', + impact: 'Fort' + }, + { + priority: 'Haute', + action: 'Minifier CSS/JS', + method: 'Webpack, Vite, ou outils de build', + gain: '-25% poids des ressources', + impact: 'Moyen' + }, + { + priority: 'Haute', + action: 'Activer la compression serveur', + method: 'Gzip ou Brotli sur serveur web', + gain: '-60% transfert de données', + impact: 'Fort' + }, + { + priority: 'Moyenne', + action: 'Mettre en place un CDN', + method: 'Cloudflare, AWS CloudFront', + gain: '-30% latence', + impact: 'Moyen' + }, + { + priority: 'Moyenne', + action: 'Réduire les requêtes HTTP', + method: 'Regrouper fichiers, sprites CSS', + gain: '-20 requêtes', + impact: 'Moyen' + }, + { + priority: 'Moyenne', + action: 'Choisir hébergement vert', + method: 'Infomaniak, GreenGeeks, OVH bas carbone', + gain: 'Énergies renouvelables', + impact: 'Fort (symbolique)' + }, + { + priority: 'Basse', + action: 'Limiter les polices web', + method: 'Max 2 familles, subset uniquement nécessaire', + gain: '-5% poids', + impact: 'Faible' + }, + { + priority: 'Basse', + action: 'Supprimer JS/CSS inutilisé', + method: 'PurgeCSS, Chrome Coverage tool', + gain: '-15% poids JS/CSS', + impact: 'Moyen' + } + ] + + return ( +
+
+ +

Audit Environnemental d'un Site Web

+

Diagnostic et recommandations

+
+ +
+

Objectifs de l'audit

+
    +
  • Mesurer l'impact environnemental d'un site web avec plusieurs outils complémentaires
  • +
  • Identifier les principaux points d'amélioration
  • +
  • Proposer des recommandations concrètes et priorisées
  • +
  • Estimer les gains environnementaux potentiels
  • +
+
+ +
+

Site audité

+
+
+ Nom: + Site de la ville de Chauray +
+
+ URL: + https://ville-de-chauray.fr/ +
+
+ Type: + Site institutionnel municipal +
+
+ Public: + Citoyens et visiteurs de la commune +
+
+ Trafic: + ~50,000 visiteurs/mois +
+
+
+ +
+

Résultats des audits

+
+ + + + + + + + + + + + {auditResults.map((result, index) => ( + + + + + + + + ))} + +
OutilCO₂/visiteNote/GradePoids pageRequêtes HTTP
{result.tool}{result.co2}{result.grade}{result.pageWeight}{result.requests}
+
+ +
+

Analyse des résultats

+

Constat global : Le site présente une empreinte environnementale élevée (Note F).

+
    +
  • 0.62g CO₂/visite - Plus sale que 84% des sites web
  • +
  • Avec 10,000 visiteurs/mois : 74.04 kg CO₂/an
  • +
  • Équivalent à 184 kWh d'énergie ou 1,175km en voiture électrique
  • +
  • Nécessite 4 arbres pour compenser les émissions annuelles
  • +
  • Point positif : Hébergement sur énergie durable ✓
  • +
+
+ +
+

+ + Détails EcoIndex (Score: 7/100 - Note G) +

+
+
+
12.456 MB
+
Poids de la page
+
(Cible: 1.024 MB)
+
+
+
7709
+
Éléments DOM
+
(Cible: 600)
+
+
+
206
+
Requêtes HTTP
+
(Cible: 40)
+
+
+
+ Empreinte pour 1000 visites/mois : +
    +
  • 42.9 litres d'eau bleue consommée
  • +
  • 2.86 kg CO₂e émis (équivalent à ~14 km en voiture)
  • +
+
+
+
+ +
+

3 Principaux problèmes identifiés

+ +
+
+

1. Page extrêmement lourde (12.456 MB)

+

Problème : Poids de la page 12x supérieur à la cible (1.024 MB)

+

Impact : Augmente drastiquement le temps de chargement et la consommation de données

+

Preuve : EcoIndex - "Trop lourde", classement 518439/527126 (98% des sites sont meilleurs)

+
+ +
+

2. Complexité excessive du DOM (7709 éléments)

+

Problème : DOM 13x plus complexe que la cible (600 éléments)

+

Impact : Ralentit le rendu et augmente la consommation mémoire/CPU

+

Preuve : EcoIndex - "Trop complexe", médiane web à 693 éléments

+
+ +
+

3. Nombre excessif de requêtes (206)

+

Problème : 206 requêtes HTTP (5x la cible de 40 requêtes)

+

Impact : Multiplie les allers-retours serveur et la consommation énergétique

+

Preuve : EcoIndex - "Trop de requêtes", 23 images sans alt (WAVE), médiane à 78 requêtes

+
+
+
+ +
+

Recommandations priorisées

+
+ + + + + + + + + + + + {recommendations.map((rec, index) => ( + + + + + + + + ))} + +
PrioritéActionMéthodeGain estiméImpact
+ + {rec.priority} + + {rec.action}{rec.method}{rec.gain} + + {rec.impact} + +
+
+
+ +
+

Checklist d'écoconception

+
+ {[ + '☐ Optimiser toutes les images (WebP, compression, lazy loading)', + '☐ Minifier CSS, JavaScript et HTML', + '☐ Activer la compression Gzip/Brotli sur le serveur', + '☐ Mettre en cache les ressources statiques (Cache-Control)', + '☐ Utiliser un CDN pour distribuer le contenu', + '☐ Choisir un hébergeur utilisant des énergies renouvelables', + '☐ Limiter les polices web (max 2 familles)', + '☐ Supprimer le code JavaScript et CSS inutilisé', + '☐ Différer le chargement des scripts non critiques', + '☐ Optimiser les requêtes base de données', + '☐ Réduire le nombre de plugins/extensions', + '☐ Précharger les ressources critiques (preload)', + '☐ Éviter les redirections inutiles', + '☐ Auditer régulièrement (mensuel ou trimestriel)' + ].map((item, index) => ( +
+ {item} +
+ ))} +
+
+ +
+

Estimation des gains environnementaux

+ +
+
+
-50%
+
Réduction du poids des pages
+
De 2.5 MB à 1.25 MB
+
+ +
+
-40%
+
Réduction des émissions CO₂
+
De 1.2g à 0.7g par visite
+
+ +
+
420 kg
+
Économie CO₂ annuelle
+
Soit ~2,100 km en voiture évités
+
+ +
+
+30%
+
Amélioration performance
+
Score Lighthouse: 72 → 95
+
+
+ +
+

Impact sur 1 an (50,000 visiteurs/mois)

+
    +
  • Avant optimisation : 900 kg CO₂/an
  • +
  • Après optimisation : 480 kg CO₂/an
  • +
  • Économie : 420 kg CO₂/an (équivalent à planter ~20 arbres)
  • +
  • Gain utilisateurs : Pages 2x plus rapides, moins de données mobiles consommées
  • +
+
+
+ +
+ +
+ Conclusion de l'audit +

Le site présente un potentiel d'amélioration important. En appliquant les 8 recommandations prioritaires, + il est possible de réduire l'empreinte carbone de 40% tout en améliorant significativement les performances + et l'expérience utilisateur. Coût de mise en œuvre estimé : 5-10 jours de développement.

+
+
+
+ ) +} + +export default EnvironmentalAudit diff --git a/src/pages/Home.css b/src/pages/Home.css new file mode 100644 index 0000000..5e3bfa8 --- /dev/null +++ b/src/pages/Home.css @@ -0,0 +1,348 @@ +.home { + animation: fadeIn 0.5s; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } +} + +.hero { + background: linear-gradient(135deg, var(--primary-dark), var(--primary), var(--secondary)); + color: var(--white); + padding: 4rem 2rem; + border-radius: 12px; + margin-bottom: 3rem; + text-align: center; + box-shadow: 0 4px 20px var(--shadow); +} + +.hero-content { + max-width: 800px; + margin: 0 auto; +} + +.hero-icon { + font-size: 4rem; + margin-bottom: 1rem; + animation: bounce 2s infinite; +} + +@keyframes bounce { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} + +.hero h1 { + font-size: 3rem; + margin-bottom: 1rem; + font-weight: 700; +} + +.hero-subtitle { + font-size: 1.3rem; + opacity: 0.95; +} + +.intro-section { + margin-bottom: 3rem; +} + +.intro-card { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); +} + +.intro-card h2 { + color: var(--primary); + margin-bottom: 1.5rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.intro-card p { + margin-bottom: 1rem; + line-height: 1.8; + color: var(--text); +} + +.objectives { + margin-bottom: 3rem; +} + +.objectives h2 { + text-align: center; + color: var(--primary); + margin-bottom: 2rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.objectives-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; +} + +.objective-card { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + text-align: center; + transition: transform 0.3s, box-shadow 0.3s; +} + +.objective-card:hover { + transform: translateY(-5px); + box-shadow: 0 4px 20px var(--shadow); +} + +.objective-card i { + font-size: 2.5rem; + color: var(--primary); + margin-bottom: 1rem; +} + +.objective-card h3 { + color: var(--primary-dark); + margin-bottom: 0.75rem; +} + +.objective-card p { + color: var(--text-light); + line-height: 1.6; +} + +.pillars { + margin-bottom: 3rem; +} + +.pillars h2 { + text-align: center; + color: var(--primary); + margin-bottom: 2rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.pillars-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.pillar-card { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + border-top: 4px solid var(--primary); +} + +.pillar-card.environmental { + border-top-color: #2d6a4f; +} + +.pillar-card.accessibility { + border-top-color: #17a2b8; +} + +.pillar-card.integration { + border-top-color: #ffc107; +} + +.pillar-icon { + width: 60px; + height: 60px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.5rem; + background: linear-gradient(135deg, var(--primary-light), var(--secondary)); +} + +.pillar-icon i { + font-size: 2rem; + color: var(--white); +} + +.pillar-card h3 { + color: var(--primary-dark); + margin-bottom: 1rem; +} + +.pillar-card p { + color: var(--text); + margin-bottom: 1rem; + line-height: 1.6; +} + +.pillar-card ul { + list-style: none; + padding: 0; +} + +.pillar-card li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; + color: var(--text-light); +} + +.pillar-card li::before { + content: "✓"; + position: absolute; + left: 0; + color: var(--secondary); + font-weight: bold; +} + +.importance { + margin-bottom: 3rem; +} + +.importance h2 { + text-align: center; + color: var(--primary); + margin-bottom: 2rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.importance-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 1.5rem; +} + +.importance-card { + background: linear-gradient(135deg, var(--primary), var(--primary-light)); + color: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); +} + +.importance-card i { + font-size: 2.5rem; + margin-bottom: 1rem; + opacity: 0.9; +} + +.importance-card h4 { + margin-bottom: 0.75rem; + font-size: 1.2rem; +} + +.importance-card p { + line-height: 1.6; + opacity: 0.95; +} + +.tools-section { + margin-bottom: 3rem; + text-align: center; +} + +.tools-section h2 { + color: var(--primary); + margin-bottom: 2rem; + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.tools-grid { + display: flex; + flex-wrap: wrap; + gap: 1rem; + justify-content: center; +} + +.tool-tag { + background: var(--primary); + color: var(--white); + padding: 0.75rem 1.5rem; + border-radius: 25px; + font-weight: 500; + box-shadow: 0 2px 5px var(--shadow); + transition: transform 0.3s; +} + +.tool-tag:hover { + transform: translateY(-3px); +} + +.cta-section { + text-align: center; + margin-bottom: 2rem; +} + +.cta-section h2 { + color: var(--primary); + margin-bottom: 2rem; +} + +.cta-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; +} + +.cta-card { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + text-decoration: none; + color: var(--text); + transition: transform 0.3s, box-shadow 0.3s; + display: block; +} + +.cta-card:hover { + transform: translateY(-5px); + box-shadow: 0 4px 20px var(--shadow); +} + +.cta-card i { + font-size: 2.5rem; + color: var(--primary); + margin-bottom: 1rem; +} + +.cta-card h3 { + color: var(--primary-dark); + margin-bottom: 0.75rem; +} + +.cta-card p { + color: var(--text-light); + line-height: 1.6; +} + +@media (max-width: 768px) { + .hero h1 { + font-size: 2rem; + } + + .hero-subtitle { + font-size: 1.1rem; + } + + .hero-icon { + font-size: 3rem; + } +} diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx new file mode 100644 index 0000000..11cef83 --- /dev/null +++ b/src/pages/Home.jsx @@ -0,0 +1,166 @@ +import React from 'react' +import { Link } from 'react-router-dom' +import './Home.css' + +function Home() { + return ( +
+
+
+ +

Écoconception Web

+

Vers un Numérique Responsable et Accessible

+
+
+ +
+
+

Contexte et enjeux

+

+ À l'ère du numérique omniprésent, les services et applications que nous concevons ont un impact + environnemental et social croissant. Selon l'ADEME, le numérique représente aujourd'hui 4% des + émissions mondiales de gaz à effet de serre, un chiffre en constante augmentation. +

+

+ Parallèlement, 15% de la population mondiale vit avec un handicap, et l'accessibilité numérique + reste un défi majeur pour garantir une inclusion sociale effective. +

+
+
+ +
+

Objectifs du compte rendu

+
+
+ +

Comprendre

+

Les enjeux du numérique responsable et les cadres réglementaires (RGESN, RGAA)

+
+
+ +

Utiliser

+

Des outils d'audit pour mesurer l'impact environnemental et l'accessibilité

+
+
+ +

Intégrer

+

L'écoconception à chaque étape : cadrage, design, développement, tests

+
+
+ +

Documenter

+

Rédiger un plan d'action et justifier vos démarches

+
+
+ +

Développer

+

Un projet sobre, performant et accessible

+
+
+
+ +
+

Les 3 Piliers analysés

+
+
+
+ +
+

Écoconception Environnementale

+

Réduire l'empreinte carbone et les ressources consommées par votre projet

+
    +
  • Mesure de l'impact environnemental
  • +
  • Optimisation des ressources
  • +
  • Hébergement vert
  • +
+
+
+
+ +
+

Accessibilité & Inclusion

+

Garantir que votre service soit utilisable par tous

+
    +
  • Conformité RGAA
  • +
  • Tests avec WAVE
  • +
  • Design inclusif
  • +
+
+
+
+ +
+

Intégration Pratique

+

Appliquer ces principes tout au long du cycle de vie

+
    +
  • Checklists et audits
  • +
  • Documentation rigoureuse
  • +
  • Suivi continu
  • +
+
+
+
+ +
+

Pourquoi cette analyse est-elle importante ?

+
+
+ +

Pour votre futur professionnel

+

Les compétences en écoconception et accessibilité sont de plus en plus recherchées

+
+
+ +

Pour vos projets personnels

+

Créez des services plus performants, moins coûteux et accessibles à tous

+
+
+ +

Pour répondre aux attentes sociétales

+

Les utilisateurs sont sensibles aux enjeux de responsabilité numérique

+
+
+
+ +
+

Outils & Référentiels

+
+
Website Carbon
+
EcoIndex
+
Lighthouse
+
WAVE
+
RGESN
+
RGAA
+
+
+ +
+

Explorez le contenu du compte rendu

+
+ + +

Comparaison d'outils

+

Analysez et comparez les outils d'écoconception

+ + + +

Audit Environnemental

+

Diagnostics et recommandations

+ + + +

Accessibilité

+

Principes et bonnes pratiques

+ + + +

Guide Écoconception

+

Méthodologie pour votre projet

+ +
+
+
+ ) +} + +export default Home diff --git a/src/pages/RGAA.jsx b/src/pages/RGAA.jsx new file mode 100644 index 0000000..bf47195 --- /dev/null +++ b/src/pages/RGAA.jsx @@ -0,0 +1,518 @@ +import React from 'react' +import './SharedPages.css' + +function RGAA() { + const rgaaCriteria = [ + { + theme: 'Images', + criterion: '1.1 - Chaque image a-t-elle une alternative textuelle ?', + level: 'A', + compliant: 'Non conforme', + proof: '8 images sans attribut alt', + tools: 'WAVE, inspection manuelle' + }, + { + theme: 'Couleurs', + criterion: '3.2 - Le contraste entre texte et arrière-plan est-il suffisant ?', + level: 'AA', + compliant: 'Non conforme', + proof: 'Footer : ratio 2.3:1 au lieu de 4.5:1 minimum', + tools: 'WAVE Contrast, WebAIM Contrast Checker' + }, + { + theme: 'Formulaires', + criterion: '11.1 - Chaque champ de formulaire a-t-il une étiquette ?', + level: 'A', + compliant: 'Non conforme', + proof: '3 champs sans label ou label non associé', + tools: 'WAVE, validation HTML' + }, + { + theme: 'Structure', + criterion: '9.1 - L\'information est-elle structurée avec des titres ?', + level: 'A', + compliant: 'Partiellement conforme', + proof: 'Hiérarchie incohérente (h2 → h4), mais titres présents', + tools: 'HeadingsMap, WAVE' + }, + { + theme: 'Navigation', + criterion: '12.6 - Les liens ont-ils des intitulés explicites ?', + level: 'A', + compliant: 'Non conforme', + proof: '15 occurrences de "Cliquez ici" ou "En savoir plus" sans contexte', + tools: 'Inspection manuelle' + } + ] + + const recommendations = [ + { + criterion: 'Images sans alternative', + technical: 'Ajouter alt="description" pour images informatives, alt="" pour images décoratives', + actors: 'Développeur frontend, rédacteur de contenu', + example: 'Ordinateur portable Dell XPS 15 pouces', + priority: 'Haute' + }, + { + criterion: 'Contrastes insuffisants', + technical: 'Utiliser couleurs avec ratio ≥ 4.5:1 (texte normal) ou ≥ 3:1 (texte large)', + actors: 'Designer, développeur CSS', + example: 'color: #333 sur background: #fff (ratio 12.6:1 ✓)', + priority: 'Haute' + }, + { + criterion: 'Labels formulaires', + technical: 'Associer chaque input à un label via attribut for/id', + actors: 'Développeur frontend', + example: '', + priority: 'Haute' + } + ] + + return ( +
+
+ +

Audit et conformité RGAA

+

Référentiel Général d'Amélioration de l'Accessibilité

+
+ +
+

Structure du RGAA

+ +
+
+
4
+
Principes fondamentaux
+
POUR (Perceptible, Utilisable, Compréhensible, Robuste)
+
+
+
13
+
Thématiques
+
Images, couleurs, multimédia, etc.
+
+
+
106
+
Critères
+
Vérifications précises
+
+
+
3
+
Niveaux
+
A (essentiel), AA (recommandé), AAA (optimal)
+
+
+ +
+

Les 4 principes (POUR)

+
+ {[ + {initial: 'P', title: 'Perceptible', desc: 'L\'information doit être présentée de façon à être perçue par tous', color: '#2d6a4f'}, + {initial: 'U', title: 'Utilisable', desc: 'Les composants et la navigation doivent être utilisables', color: '#52b788'}, + {initial: 'C', title: 'Compréhensible', desc: 'L\'information et l\'interface doivent être compréhensibles', color: '#74c69d'}, + {initial: 'R', title: 'Robuste', desc: 'Le contenu doit être interprété par les technologies d\'assistance', color: '#95d5b2'} + ].map((principle, index) => ( +
+
+ {principle.initial} +
+
+ {principle.title} +

{principle.desc}

+
+
+ ))} +
+
+
+ +
+

Les 13 thématiques du RGAA

+
+ {[ + {name: 'Images', icon: 'fa-image', criteria: 8}, + {name: 'Cadres', icon: 'fa-window-maximize', criteria: 2}, + {name: 'Couleurs', icon: 'fa-palette', criteria: 5}, + {name: 'Multimédia', icon: 'fa-video', criteria: 7}, + {name: 'Tableaux', icon: 'fa-table', criteria: 6}, + {name: 'Liens', icon: 'fa-link', criteria: 3}, + {name: 'Scripts', icon: 'fa-code', criteria: 4}, + {name: 'Éléments obligatoires', icon: 'fa-exclamation', criteria: 5}, + {name: 'Structuration', icon: 'fa-sitemap', criteria: 9}, + {name: 'Présentation', icon: 'fa-desktop', criteria: 7}, + {name: 'Formulaires', icon: 'fa-edit', criteria: 13}, + {name: 'Navigation', icon: 'fa-compass', criteria: 12}, + {name: 'Consultation', icon: 'fa-eye', criteria: 11} + ].map((theme, index) => ( +
+ +
{theme.name}
+
{theme.criteria} critères
+
+ ))} +
+
+ +
+

5 Critères sélectionnés (niveau A et AA)

+
+

Justification : Ces 5 critères ont été choisis car ils sont les plus fréquemment non-conformes + sur les sites grand public et ont un impact direct sur l'expérience des utilisateurs en situation de handicap. + Ils couvrent les principaux types de handicaps (visuel, moteur, cognitif).

+
+ +
+ + + + + + + + + + + + + {rgaaCriteria.map((item, index) => ( + + + + + + + + + ))} + +
ThèmeCritèreNiveauConforme ?Preuves/ObservationsOutils
{item.theme}{item.criterion}{item.level} + + {item.compliant} + + {item.proof}{item.tools}
+
+
+ +
+

3 Critères les moins respectés

+ +
+ {recommendations.map((rec, index) => ( +
+
+

+ {index + 1}. {rec.criterion} +

+ + Priorité {rec.priority} + +
+ +
+
+ Solution technique : +

{rec.technical}

+
+ +
+ Acteurs concernés : +

{rec.actors}

+
+ +
+ Code corrigé : +
+ {rec.example} +
+
+
+
+ ))} +
+
+ +
+

Plan d'action priorisé

+ +
+ {[ + {rank: 1, action: 'Corriger toutes les images sans alt', impact: 'Critique', ease: 'Facile', time: '2h'}, + {rank: 2, action: 'Ajuster les contrastes de couleurs', impact: 'Critique', ease: 'Facile', time: '1h'}, + {rank: 3, action: 'Associer labels aux formulaires', impact: 'Critique', ease: 'Facile', time: '1h'}, + {rank: 4, action: 'Restructurer hiérarchie des titres', impact: 'Important', ease: 'Moyen', time: '2h'}, + {rank: 5, action: 'Réécrire les liens ambigus', impact: 'Important', ease: 'Facile', time: '1h'}, + {rank: 6, action: 'Tester navigation au clavier', impact: 'Important', ease: 'Moyen', time: '3h'}, + {rank: 7, action: 'Valider avec lecteur d\'écran', impact: 'Important', ease: 'Difficile', time: '4h'} + ].map((item) => ( +
+
+ {item.rank} +
+
{item.action}
+ {item.impact} + {item.ease} + {item.time} +
+ ))} +
+ +
+ Échéancier réaliste : +
    +
  • Sprint 1 (Semaine 1) : Corrections critiques (items 1-3) → 4h
  • +
  • Sprint 2 (Semaine 2) : Corrections importantes (items 4-5) → 3h
  • +
  • Sprint 3 (Semaine 3) : Tests et validation (items 6-7) → 7h
  • +
  • Total : 14h réparties sur 3 semaines
  • +
+
+
+ +
+

Note à la direction

+ +
+

Pourquoi la conformité RGAA est essentielle ?

+ +
+
+

+ Obligation légale +

+

La loi du 11 février 2005 impose aux services publics en ligne d'être accessibles. + Pour le privé, les entreprises > 250M€ de CA sont concernées depuis 2019.

+

Sanctions : Jusqu'à 20,000€ par an en cas de non-conformité.

+
+ +
+

+ Inclusion sociale +

+
    +
  • 12 millions de Français vivent avec un handicap
  • +
  • 1 personne sur 5 est concernée par l'accessibilité (temporaire ou permanente)
  • +
  • Améliore l'expérience de TOUS les utilisateurs (seniors, mobiles, contextes difficiles)
  • +
+
+ +
+

+ Bénéfices business +

+
    +
  • +35% d'audience potentielle (personnes en situation de handicap et seniors)
  • +
  • Meilleur SEO : Google favorise les sites accessibles (structure, contenu sémantique)
  • +
  • -25% taux de rebond (meilleure utilisabilité générale)
  • +
  • Image responsable : Différenciation concurrentielle positive
  • +
+
+
+ +
+
+
+
Investissement
+
14h = ~2k€
+
+
+
Gain audience/an
+
+15-20%
+
+
+
ROI estimé
+
3-6 mois
+
+
+
+
+
+ +
+

RGAA vs WCAG

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CritèreRGAAWCAG
OrigineFrance (DINUM)International (W3C)
BaseAdapté de WCAG 2.1Standard international
ApplicabilitéObligation légale en FranceRecommandation mondiale
LangueFrançaisAnglais (traductions disponibles)
Structure13 thèmes, 106 critères4 principes, 13 règles, 78 critères
MéthodologieTests techniques précisCritères de succès
Usage recommandéSites/services françaisSites internationaux
+
+ +

+ 💡 Conseil : Un site conforme RGAA 4.1 niveau AA est automatiquement conforme WCAG 2.1 niveau AA. + Le RGAA ajoute une méthodologie de test plus précise adaptée au contexte français. +

+
+ +
+

Checklist simplifiée pour les développeurs

+ +
+
+

Images & Médias

+
    +
  • ☐ Attribut alt sur toutes les images
  • +
  • ☐ alt="" pour images décoratives
  • +
  • ☐ Sous-titres pour vidéos
  • +
  • ☐ Transcription pour audio
  • +
+
+ +
+

Couleurs & Contrastes

+
    +
  • ☐ Contraste ≥ 4.5:1 (texte normal)
  • +
  • ☐ Contraste ≥ 3:1 (texte large)
  • +
  • ☐ Ne pas se fier qu'à la couleur
  • +
  • ☐ Focus visible au clavier
  • +
+
+ +
+

Formulaires

+
    +
  • ☐ Label associé à chaque input (for/id)
  • +
  • ☐ Messages d'erreur explicites
  • +
  • ☐ Instructions claires
  • +
  • ☐ Validation accessible
  • +
+
+ +
+

Structure & Navigation

+
    +
  • ☐ Hiérarchie de titres cohérente
  • +
  • ☐ Landmarks ARIA (nav, main, footer)
  • +
  • ☐ Liens explicites (pas "cliquez ici")
  • +
  • ☐ Skip links pour contenu principal
  • +
+
+ +
+

Clavier & Interactions

+
    +
  • ☐ Navigation au clavier fonctionnelle
  • +
  • ☐ Pas de piège au clavier
  • +
  • ☐ Ordre de tabulation logique
  • +
  • ☐ Boutons activables avec Entrée/Espace
  • +
+
+ +
+

Code & Technique

+
    +
  • ☐ HTML valide (W3C Validator)
  • +
  • ☐ lang="fr" sur html
  • +
  • ☐ Balises sémantiques appropriées
  • +
  • ☐ ARIA utilisé avec parcimonie
  • +
+
+
+
+ +
+ +
+ La conformité RGAA : un investissement rentable +

14h de corrections pour rendre le site accessible, élargir l'audience de 15-20%, améliorer le SEO, + et se conformer à la loi. Le RGAA n'est pas une contrainte mais une opportunité d'améliorer la qualité globale du service numérique.

+
+
+
+ ) +} + +export default RGAA diff --git a/src/pages/RGESN.jsx b/src/pages/RGESN.jsx new file mode 100644 index 0000000..b8e4ee5 --- /dev/null +++ b/src/pages/RGESN.jsx @@ -0,0 +1,412 @@ +import React from 'react' +import './SharedPages.css' + +function RGESN() { + const rgesnCriteria = [ + { + family: 'Stratégie', + criterion: 'Le projet a-t-il défini des objectifs écoresponsables mesurables ?', + respected: 'Partiellement', + proof: 'Présence d\'une charte mais sans KPIs chiffrés', + tools: 'Revue documentaire' + }, + { + family: 'Spécifications', + criterion: 'Les fonctionnalités inutiles ou peu utilisées ont-elles été supprimées ?', + respected: 'Non', + proof: 'Présence de nombreuses fonctionnalités peu utilisées (< 5% users)', + tools: 'Google Analytics, Hotjar' + }, + { + family: 'Architecture', + criterion: 'Le site utilise-t-il un hébergeur avec une démarche environnementale ?', + respected: 'Non', + proof: 'Hébergeur non référencé Green Web Foundation', + tools: 'Green Web Check' + }, + { + family: 'UX/UI', + criterion: 'Le design limite-t-il les animations et effets visuels coûteux ?', + respected: 'Partiellement', + proof: 'Vidéo autoplay en homepage, carrousels animés', + tools: 'Inspection manuelle' + }, + { + family: 'Contenus', + criterion: 'Les images sont-elles optimisées (format moderne, compression) ?', + respected: 'Non', + proof: 'Images PNG/JPEG lourdes, pas de WebP, pas de lazy loading', + tools: 'Lighthouse, ImageOptim' + }, + { + family: 'Frontend', + criterion: 'Les fichiers CSS et JavaScript sont-ils minifiés ?', + respected: 'Partiellement', + proof: 'JS minifié mais CSS non minifié, présence de code mort', + tools: 'Chrome Coverage, Webpack Bundle Analyzer' + }, + { + family: 'Frontend', + criterion: 'Le site limite-t-il le nombre de requêtes HTTP ?', + respected: 'Non', + proof: '45 requêtes dont 15 externes (analytics, fonts, ads)', + tools: 'Chrome Network, EcoIndex' + }, + { + family: 'Backend', + criterion: 'Les requêtes base de données sont-elles optimisées ?', + respected: 'Non applicable', + proof: 'Pas d\'accès au backend', + tools: '-' + } + ] + + const recommendations = [ + { + criterion: 'Images non optimisées', + action: 'Convertir toutes les images en WebP/AVIF, activer lazy loading, compresser avec TinyPNG', + actors: 'Développeur frontend, designer', + gains: '-60% poids des images, -30% temps de chargement', + obstacles: 'Temps de conversion, compatibilité anciens navigateurs' + }, + { + criterion: 'Hébergeur non vert', + action: 'Migrer vers un hébergeur certifié (Infomaniak, GreenGeeks, OVH bas carbone)', + actors: 'DevOps, chef de projet', + gains: 'Réduction empreinte carbone de l\'infrastructure', + obstacles: 'Coût de migration, temps de transition' + }, + { + criterion: 'Trop de requêtes HTTP', + action: 'Regrouper CSS/JS, héberger fonts localement, supprimer analytics inutiles', + actors: 'Développeur frontend', + gains: '-30% requêtes, amélioration performance', + obstacles: 'Refactoring code, dépendances tierces' + } + ] + + return ( +
+
+ +

Application du RGESN

+

Référentiel Général d'Écoconception des Services Numériques

+
+ +
+

Qu'est-ce que le RGESN ?

+
+
+ Créé par: + DINUM, ADEME, INR, Arcep (France) +
+
+ Année: + Version 1.0 publiée en 2022 +
+
+ Objectif: + Proposer un référentiel officiel de bonnes pratiques pour réduire l'impact environnemental du numérique +
+
+ Portée: + 79 critères répartis en 8 familles +
+
+ Public: + Services publics et privés, obligatoire pour certains services publics +
+
+
+ +
+

Les 8 familles du RGESN

+
+ {[ + { name: 'Stratégie', icon: 'fa-chess', color: '#2d6a4f', desc: 'Définir une stratégie numérique responsable' }, + { name: 'Spécifications', icon: 'fa-file-alt', color: '#52b788', desc: 'Concevoir un service sobre et utile' }, + { name: 'Architecture', icon: 'fa-sitemap', color: '#74c69d', desc: 'Choisir une architecture efficiente' }, + { name: 'UX/UI', icon: 'fa-paint-brush', color: '#95d5b2', desc: 'Designer une interface sobre' }, + { name: 'Contenus', icon: 'fa-images', color: '#b7e4c7', desc: 'Optimiser les médias et contenus' }, + { name: 'Frontend', icon: 'fa-code', color: '#d8f3dc', desc: 'Développer un frontend léger' }, + { name: 'Backend', icon: 'fa-server', color: '#52b788', desc: 'Optimiser le backend' }, + { name: 'Hébergement', icon: 'fa-cloud', color: '#2d6a4f', desc: 'Choisir un hébergement responsable' } + ].map((family, index) => ( +
+ +

{family.name}

+

{family.desc}

+
+ ))} +
+
+ +
+

5 Critères sélectionnés (accessibles depuis le front)

+
+

Justification du choix : Ces 5 critères ont été sélectionnés car ils sont facilement vérifiables depuis le frontend, + sans nécessiter d'accès aux serveurs ou au code backend. Ils représentent également les leviers d'amélioration les plus impactants pour un site grand public.

+
+ +
+ + + + + + + + + + + + {rgesnCriteria.map((item, index) => ( + + + + + + + + ))} + +
FamilleCritèreRespecté ?Preuves/ObservationsOutils
{item.family}{item.criterion} + + {item.respected} + + {item.proof}{item.tools}
+
+
+ +
+

3 Critères les moins respectés

+ +
+ {recommendations.map((rec, index) => ( +
+

+ {index + 1}. {rec.criterion} +

+ +
+
+ Action à mener : +

{rec.action}

+
+ +
+ Acteurs concernés : +

{rec.actors}

+
+ +
+ Gains attendus : +

{rec.gains}

+
+ +
+ Freins possibles : +

{rec.obstacles}

+
+
+
+ ))} +
+
+ +
+

Plan d'action priorisé

+ +
+ {[ + { + priority: 1, + action: 'Optimiser les images', + who: 'Développeur frontend', + when: 'Semaine 1-2', + effort: '3 jours', + impact: 'Fort' + }, + { + priority: 2, + action: 'Minifier CSS/JS et supprimer code mort', + who: 'Développeur frontend', + when: 'Semaine 2', + effort: '2 jours', + impact: 'Moyen' + }, + { + priority: 3, + action: 'Réduire requêtes HTTP', + who: 'Développeur frontend', + when: 'Semaine 3', + effort: '2 jours', + impact: 'Moyen' + }, + { + priority: 4, + action: 'Migrer vers hébergeur vert', + who: 'DevOps + Chef de projet', + when: 'Semaine 4-5', + effort: '5 jours', + impact: 'Fort (symbolique)' + }, + { + priority: 5, + action: 'Supprimer fonctionnalités inutilisées', + who: 'Product Owner + Dev', + when: 'Semaine 6', + effort: '3 jours', + impact: 'Moyen' + } + ].map((item) => ( +
+
+ {item.priority} +
+ +
+ {item.action} +
+ {item.who} • {item.when} • {item.effort} +
+
+ + + {item.impact} + +
+ ))} +
+ +
+

Échéancier global : 6 semaines

+

Avec une équipe de 2 personnes (1 dev frontend + 1 DevOps), le plan est réalisable en 1,5 mois.

+
+
+ +
+

Fiche mémo : Audit RGESN

+
+
+

1️⃣ Sélectionner les critères

+

Choisir 5-10 critères parmi les 79, adaptés au contexte et accessibles

+
+
+

2️⃣ Auditer avec des outils

+

Utiliser Lighthouse, EcoIndex, Green Web Check, etc.

+
+
+

3️⃣ Évaluer la conformité

+

Oui / Non / Partiellement / Non applicable

+
+
+

4️⃣ Identifier les priorités

+

Critères les moins respectés avec fort impact

+
+
+

5️⃣ Proposer un plan d'action

+

Qui fait quoi, quand, avec quels moyens

+
+
+

6️⃣ Argumenter auprès de la direction

+

Gains environnementaux + économiques + image

+
+
+
+ +
+

Note à la direction

+
+

Pourquoi se conformer au RGESN ?

+ +
+

Obligation légale (pour certains services publics)

+

La loi REEN (2021) impose aux services publics numériques de se conformer au RGESN. + Pour le privé, c'est une anticipation des futures réglementations.

+
+ +
+

Bénéfices environnementaux

+
    +
  • Réduction de 40% des émissions CO₂ du site
  • +
  • Économie de 420 kg CO₂/an (soit 2,100 km en voiture)
  • +
  • Contribution aux objectifs RSE de l'entreprise
  • +
+
+ +
+

Bénéfices économiques

+
    +
  • Réduction des coûts d'hébergement (moins de bande passante)
  • +
  • Amélioration du SEO (sites rapides mieux classés par Google)
  • +
  • Réduction du taux de rebond (-15% attendu)
  • +
  • ROI estimé : 6-12 mois
  • +
+
+ +
+

Bénéfices d'image

+
    +
  • Différenciation concurrentielle (label "Site écoresponsable")
  • +
  • Attractivité pour les talents sensibles à la RSE
  • +
  • Communication positive auprès des clients
  • +
+
+ +
+ Investissement nécessaire : 10-15 jours de développement (15-20k€)
+ Gains estimés sur 3 ans : 50-80k€ (hébergement + SEO + conversion) +
+
+
+ +
+ +
+ En résumé +

Le RGESN offre un cadre structuré et officiel pour écoconcevoir les services numériques. + L'audit révèle des axes d'amélioration importants, avec un ROI positif à moyen terme. + La conformité au RGESN n'est pas qu'une contrainte, c'est un levier de performance et d'innovation.

+
+
+
+ ) +} + +export default RGESN diff --git a/src/pages/SharedPages.css b/src/pages/SharedPages.css new file mode 100644 index 0000000..4a78bf2 --- /dev/null +++ b/src/pages/SharedPages.css @@ -0,0 +1,459 @@ +.shared-page { + animation: fadeIn 0.5s; +} + +.page-header { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 3px solid var(--primary); +} + +.page-header i { + font-size: 2.5rem; + color: var(--primary); +} + +.page-header h1 { + color: var(--primary-dark); + font-size: 2rem; +} + +.subtitle { + color: var(--text-light); + font-size: 1.1rem; + font-style: italic; +} + +.content-section { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + margin-bottom: 2rem; +} + +.content-section h2 { + color: var(--primary); + margin-bottom: 1.5rem; + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 1.5rem; +} + +.content-section h3 { + color: var(--primary-dark); + margin: 1.5rem 0 1rem; + font-size: 1.2rem; +} + +.content-section h4 { + color: var(--primary-dark); + margin: 1rem 0 0.5rem; + font-size: 1.1rem; +} + +.content-section p { + line-height: 1.8; + color: var(--text); + margin-bottom: 1rem; +} + +.content-section ul { + margin-left: 1.5rem; + margin-bottom: 1rem; +} + +.content-section li { + margin-bottom: 0.5rem; + line-height: 1.6; +} + +.info-card { + background: var(--bg); + padding: 1.5rem; + border-radius: 8px; + border-left: 4px solid var(--primary); +} + +.info-row { + display: flex; + gap: 1rem; + padding: 0.75rem 0; + border-bottom: 1px solid var(--border); +} + +.info-row:last-child { + border-bottom: none; +} + +.info-row strong { + min-width: 150px; + color: var(--primary-dark); +} + +.process-steps { + display: grid; + gap: 1.5rem; +} + +.step-card { + display: flex; + gap: 1.5rem; + padding: 1.5rem; + background: var(--bg); + border-radius: 8px; + border-left: 4px solid var(--secondary); +} + +.step-number { + width: 50px; + height: 50px; + background: linear-gradient(135deg, var(--primary), var(--secondary)); + color: var(--white); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.5rem; + font-weight: bold; + flex-shrink: 0; +} + +.step-card h3 { + margin-top: 0; +} + +.formula { + background: var(--white); + padding: 1rem; + border-radius: 8px; + margin: 1rem 0; + border-left: 3px solid var(--info); +} + +.formula code { + font-size: 1rem; + background: transparent; + padding: 0; +} + +.note { + font-size: 0.9rem; + color: var(--text-light); + font-style: italic; +} + +.sources-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; +} + +.source-card { + padding: 1.5rem; + background: var(--bg); + border-radius: 8px; + text-align: center; + border-top: 3px solid var(--primary); +} + +.source-card i { + font-size: 2rem; + color: var(--primary); + margin-bottom: 0.75rem; +} + +.source-card h4 { + margin: 0.5rem 0; +} + +.results-list { + display: grid; + gap: 1rem; +} + +.result-item { + display: flex; + gap: 1rem; + padding: 1rem; + background: var(--bg); + border-radius: 8px; + align-items: flex-start; +} + +.result-item i { + font-size: 1.5rem; + color: var(--primary); + margin-top: 0.25rem; +} + +.result-item strong { + display: block; + color: var(--primary-dark); + margin-bottom: 0.25rem; +} + +.result-item p { + margin: 0; + color: var(--text-light); + font-size: 0.9rem; +} + +.example-box { + background: var(--bg); + padding: 2rem; + border-radius: 8px; +} + +.example-results { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.result-metric { + background: var(--white); + padding: 1.5rem; + border-radius: 8px; + text-align: center; + box-shadow: 0 2px 5px var(--shadow); +} + +.result-metric.success { + background: var(--success); + color: var(--white); +} + +.metric-value { + font-size: 2rem; + font-weight: bold; + color: var(--primary); + margin-bottom: 0.5rem; +} + +.result-metric.success .metric-value { + color: var(--white); + font-size: 3rem; +} + +.metric-label { + font-size: 0.9rem; + color: var(--text-light); +} + +.result-metric.success .metric-label { + color: var(--white); +} + +.interpretation h4 { + margin-top: 0; +} + +.limits-grid { + display: grid; + gap: 1.5rem; +} + +.limit-card { + padding: 1.5rem; + background: var(--bg); + border-radius: 8px; + border-left: 4px solid var(--warning); +} + +.limit-card h4 { + margin-top: 0; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.trust-section { + margin-top: 2rem; +} + +.trust-box { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.5rem; + margin-top: 1rem; +} + +.trust-pros, +.trust-cons { + padding: 1.5rem; + border-radius: 8px; +} + +.trust-pros { + background: #d4edda; + border-left: 4px solid var(--success); +} + +.trust-cons { + background: #fff3cd; + border-left: 4px solid var(--warning); +} + +.trust-pros h4, +.trust-cons h4 { + margin-top: 0; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.memo-card { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 1.5rem; +} + +.memo-section { + padding: 1.5rem; + background: var(--bg); + border-radius: 8px; + border-top: 3px solid var(--primary); +} + +.memo-section h4 { + margin: 0 0 0.75rem 0; + font-size: 1rem; +} + +.memo-section p { + margin: 0; + font-size: 0.9rem; + color: var(--text-light); +} + +.faq-box { + background: var(--bg); + padding: 2rem; + border-radius: 8px; +} + +.question { + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--primary-dark); +} + +.answer { + padding-left: 1rem; + border-left: 4px solid var(--secondary); +} + +.tip { + background: var(--white); + padding: 1rem; + border-radius: 8px; + margin-top: 1rem; + display: flex; + gap: 0.75rem; + align-items: flex-start; +} + +.tip i { + color: var(--warning); + font-size: 1.5rem; + margin-top: 0.25rem; +} + +.comparison-table-simple { + overflow-x: auto; +} + +.comparison-table-simple table { + width: 100%; + border-collapse: collapse; +} + +.comparison-table-simple thead { + background: var(--primary); + color: var(--white); +} + +.comparison-table-simple th, +.comparison-table-simple td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid var(--border); +} + +.comparison-table-simple tbody tr:hover { + background: var(--bg); +} + +.improvements-grid { + display: grid; + gap: 1rem; +} + +.improvement-card { + padding: 1.5rem; + background: var(--bg); + border-radius: 8px; + border-left: 4px solid var(--info); +} + +.improvement-card h4 { + margin: 0 0 0.5rem 0; + color: var(--primary-dark); +} + +.improvement-card p { + margin: 0; + color: var(--text-light); +} + +.conclusion-banner { + background: linear-gradient(135deg, var(--primary), var(--primary-light)); + color: var(--white); + padding: 2rem; + border-radius: 12px; + display: flex; + gap: 1rem; + align-items: flex-start; + box-shadow: 0 4px 15px var(--shadow); +} + +.conclusion-banner i { + font-size: 2rem; + flex-shrink: 0; +} + +.conclusion-banner strong { + display: block; + font-size: 1.2rem; + margin-bottom: 0.5rem; +} + +.conclusion-banner p { + margin: 0; + line-height: 1.6; +} + +@media (max-width: 768px) { + .content-section { + padding: 1.5rem; + } + + .step-card { + flex-direction: column; + } + + .info-row { + flex-direction: column; + gap: 0.25rem; + } + + .memo-card { + grid-template-columns: 1fr; + } +} diff --git a/src/pages/ToolsComparison.css b/src/pages/ToolsComparison.css new file mode 100644 index 0000000..f213a12 --- /dev/null +++ b/src/pages/ToolsComparison.css @@ -0,0 +1,351 @@ +.tools-comparison { + animation: fadeIn 0.5s; +} + +.page-header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 3px solid var(--primary); +} + +.page-header i { + font-size: 2.5rem; + color: var(--primary); +} + +.page-header h1 { + color: var(--primary-dark); + font-size: 2rem; +} + +.intro { + margin-bottom: 3rem; +} + +.intro-box { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + border-left: 4px solid var(--primary); +} + +.intro-box h2 { + color: var(--primary); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.intro-box ul { + list-style: none; + padding: 0; +} + +.intro-box li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; +} + +.intro-box li::before { + content: "✓"; + position: absolute; + left: 0; + color: var(--secondary); + font-weight: bold; +} + +.radar-section { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + margin-bottom: 3rem; +} + +.radar-section h2 { + color: var(--primary); + margin-bottom: 2rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.radar-container { + width: 100%; + height: 500px; +} + +.comparison-table { + margin-bottom: 3rem; +} + +.comparison-table h2 { + color: var(--primary); + margin-bottom: 1.5rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.table-responsive { + overflow-x: auto; + background: var(--white); + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); +} + +table { + width: 100%; + border-collapse: collapse; + min-width: 800px; +} + +thead { + background: var(--primary); + color: var(--white); +} + +th, td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid var(--border); +} + +th { + font-weight: 600; + position: sticky; + top: 0; +} + +tbody tr:hover { + background: var(--bg); +} + +td:first-child { + font-weight: 600; + background: var(--bg); + position: sticky; + left: 0; + z-index: 1; +} + +.badge { + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 12px; + font-size: 0.85rem; + font-weight: 600; +} + +.badge.success { + background: var(--success); + color: var(--white); +} + +.badge.warning { + background: var(--warning); + color: var(--text); +} + +.badge.danger { + background: var(--danger); + color: var(--white); +} + +.rating { + display: flex; + gap: 0.25rem; +} + +.rating i { + color: var(--warning); +} + +.synthesis { + margin-bottom: 3rem; +} + +.synthesis h2 { + color: var(--primary); + margin-bottom: 2rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.synthesis-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 1.5rem; + margin-bottom: 2rem; +} + +.synthesis-card { + background: var(--white); + padding: 1.5rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); + border-top: 4px solid var(--primary); +} + +.synthesis-card.best { + border-top-color: var(--warning); +} + +.synthesis-card.ease { + border-top-color: var(--success); +} + +.synthesis-card.precision { + border-top-color: var(--info); +} + +.synthesis-card.integration { + border-top-color: var(--primary); +} + +.synthesis-card h3 { + color: var(--primary-dark); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.synthesis-card p { + margin-bottom: 0.75rem; + line-height: 1.6; +} + +.synthesis-card ul { + list-style: none; + padding: 0; +} + +.synthesis-card li { + padding: 0.25rem 0; + padding-left: 1.2rem; + position: relative; + font-size: 0.9rem; + color: var(--text-light); +} + +.synthesis-card li::before { + content: "→"; + position: absolute; + left: 0; + color: var(--secondary); +} + +.recommendations { + background: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); +} + +.recommendations h3 { + color: var(--primary); + margin-bottom: 1.5rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.use-case { + margin-bottom: 1.5rem; + padding: 1rem; + border-left: 4px solid var(--secondary); + background: var(--bg); + border-radius: 8px; +} + +.use-case h4 { + color: var(--primary-dark); + margin-bottom: 0.5rem; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.use-case p { + margin: 0.5rem 0; +} + +.rationale { + font-style: italic; + color: var(--text-light); + font-size: 0.9rem; +} + +.conclusion-box { + background: linear-gradient(135deg, var(--primary), var(--primary-light)); + color: var(--white); + padding: 2rem; + border-radius: 12px; + box-shadow: 0 2px 10px var(--shadow); +} + +.conclusion-box h2 { + color: var(--white); + margin-bottom: 1rem; + display: flex; + align-items: center; + gap: 0.75rem; +} + +.conclusion-box p { + margin-bottom: 1rem; + line-height: 1.8; +} + +.conclusion-box ul { + list-style: none; + padding: 0; + margin-bottom: 1.5rem; +} + +.conclusion-box li { + padding: 0.5rem 0; + padding-left: 1.5rem; + position: relative; +} + +.conclusion-box li::before { + content: "✓"; + position: absolute; + left: 0; + font-weight: bold; +} + +.highlight { + background: rgba(255, 255, 255, 0.2); + padding: 1rem; + border-radius: 8px; + border-left: 4px solid var(--white); + margin-top: 1rem; + display: flex; + align-items: flex-start; + gap: 0.75rem; +} + +.highlight i { + font-size: 1.5rem; + margin-top: 0.25rem; +} + +@media (max-width: 768px) { + .radar-container { + height: 400px; + } + + .synthesis-grid { + grid-template-columns: 1fr; + } +} diff --git a/src/pages/ToolsComparison.jsx b/src/pages/ToolsComparison.jsx new file mode 100644 index 0000000..dcbea4e --- /dev/null +++ b/src/pages/ToolsComparison.jsx @@ -0,0 +1,380 @@ +import React from 'react' +import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer, Legend } from 'recharts' +import './ToolsComparison.css' + +function ToolsComparison() { + const tools = [ + { + name: 'Website Carbon Calculator', + url: 'https://www.websitecarbon.com/', + free: 'Oui', + easeOfUse: 5, + precision: 4, + criteria: 'Poids des pages, transfert de données, émissions CO₂, énergie consommée', + export: 'Non', + documentation: 'Oui', + integration: 'Non (API disponible)', + strengths: 'Interface intuitive, résultats immédiats, sensibilisation efficace, calcul simple', + weaknesses: 'Méthode de calcul simplifiée, pas d\'export direct, analyse limitée à une page', + globalRating: 4 + }, + { + name: 'EcoIndex', + url: 'https://www.ecoindex.fr/', + free: 'Oui', + easeOfUse: 4, + precision: 5, + criteria: 'Poids des pages, requêtes HTTP, éléments DOM, score environnemental (A-G)', + export: 'Oui (PDF)', + documentation: 'Oui', + integration: 'Oui (API, extension)', + strengths: 'Méthodologie française validée, notation claire (A-G), analyse détaillée, extension Chrome/Firefox', + weaknesses: 'Interface moins moderne, nécessite parfois plusieurs tentatives', + globalRating: 5 + }, + { + name: 'Lighthouse', + url: 'https://developers.google.com/web/tools/lighthouse', + free: 'Oui', + easeOfUse: 4, + precision: 5, + criteria: 'Performance, accessibilité, SEO, PWA, bonnes pratiques', + export: 'Oui (JSON, HTML)', + documentation: 'Oui (complète)', + integration: 'Oui (Chrome DevTools, CLI, CI/CD)', + strengths: 'Outil complet de Google, analyse multi-critères, recommandations détaillées, très largement utilisé', + weaknesses: 'Pas spécifiquement axé sur l\'écoconception, complexe pour débutants', + globalRating: 5 + }, + { + name: 'GreenIT-Analysis', + url: 'https://github.com/cnumr/GreenIT-Analysis', + free: 'Oui', + easeOfUse: 3, + precision: 4, + criteria: 'Poids des pages, requêtes, éléments DOM, bonnes pratiques écoconception', + export: 'Oui', + documentation: 'Oui', + integration: 'Oui (extension navigateur)', + strengths: 'Extension dédiée écoconception, analyse de parcours utilisateur, référentiel de 115 bonnes pratiques', + weaknesses: 'Interface technique, courbe d\'apprentissage, maintenance aléatoire', + globalRating: 4 + }, + { + name: 'Scaphandre', + url: 'https://github.com/hubblo-org/scaphandre', + free: 'Oui', + easeOfUse: 2, + precision: 5, + criteria: 'Consommation énergétique réelle du serveur et des applications', + export: 'Oui (Prometheus, JSON)', + documentation: 'Oui (technique)', + integration: 'Oui (monitoring serveur)', + strengths: 'Mesure réelle de la consommation électrique, très précis, open source', + weaknesses: 'Installation complexe, nécessite accès serveur, pas adapté au grand public', + globalRating: 4 + } + ] + + const radarData = [ + { + criteria: 'Facilité', + 'Website Carbon': 5, + 'EcoIndex': 4, + 'Lighthouse': 4, + 'GreenIT': 3, + 'Scaphandre': 2 + }, + { + criteria: 'Précision', + 'Website Carbon': 4, + 'EcoIndex': 5, + 'Lighthouse': 5, + 'GreenIT': 4, + 'Scaphandre': 5 + }, + { + criteria: 'Documentation', + 'Website Carbon': 4, + 'EcoIndex': 5, + 'Lighthouse': 5, + 'GreenIT': 4, + 'Scaphandre': 4 + }, + { + criteria: 'Intégration', + 'Website Carbon': 2, + 'EcoIndex': 4, + 'Lighthouse': 5, + 'GreenIT': 4, + 'Scaphandre': 4 + }, + { + criteria: 'Note globale', + 'Website Carbon': 4, + 'EcoIndex': 5, + 'Lighthouse': 5, + 'GreenIT': 4, + 'Scaphandre': 4 + } + ] + + return ( +
+
+ +

Comparaison d'Outils d'Écoconception

+
+ +
+
+

Objectifs

+
    +
  • Découvrir et manipuler des outils en ligne d'écoconception
  • +
  • Comparer leurs fonctionnalités, facilité d'utilisation et pertinence
  • +
  • Restituer les résultats sous forme de tableau synthétique et de radar
  • +
+
+
+ +
+

Graphique Radar Comparatif

+
+ + + + + + + + + + + + + +
+
+ +
+

Tableau Comparatif Détaillé

+
+ + + + + {tools.map((tool, index) => ( + + ))} + + + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + + + {tools.map((tool, index) => ( + + ))} + + +
Critère{tool.name}
URL{tool.url}
Gratuité{tool.free}
Facilité d'utilisation +
+ {[...Array(tool.easeOfUse)].map((_, i) => ( + + ))} + {[...Array(5 - tool.easeOfUse)].map((_, i) => ( + + ))} +
+
Précision des résultats +
+ {[...Array(tool.precision)].map((_, i) => ( + + ))} + {[...Array(5 - tool.precision)].map((_, i) => ( + + ))} +
+
Critères évalués{tool.criteria}
Export des résultats + + {tool.export} + +
Documentation + + {tool.documentation} + +
Intégration{tool.integration}
Points forts{tool.strengths}
Points faibles{tool.weaknesses}
Note globale +
+ {[...Array(tool.globalRating)].map((_, i) => ( + + ))} + {[...Array(5 - tool.globalRating)].map((_, i) => ( + + ))} +
+
+
+
+ +
+

Synthèse et Recommandations

+ +
+
+

Meilleurs outils globaux

+

EcoIndex et Lighthouse obtiennent la note maximale (5/5).

+
    +
  • EcoIndex : Spécifiquement conçu pour l'écoconception, avec une méthodologie française validée
  • +
  • Lighthouse : Outil complet de Google, idéal pour une analyse multi-critères (performance, accessibilité, SEO)
  • +
+
+ +
+

Plus facile à utiliser

+

Website Carbon Calculator (5/5)

+
    +
  • Interface ultra-simple et intuitive
  • +
  • Résultats immédiats en quelques secondes
  • +
  • Idéal pour une première sensibilisation
  • +
+
+ +
+

Plus précis

+

EcoIndex, Lighthouse, Scaphandre (5/5)

+
    +
  • Scaphandre : Mesure réelle de consommation électrique
  • +
  • EcoIndex : Méthodologie scientifique validée
  • +
  • Lighthouse : Analyse approfondie multi-critères
  • +
+
+ +
+

Meilleure intégration

+

Lighthouse

+
    +
  • Intégré dans Chrome DevTools
  • +
  • Disponible en CLI pour automatisation
  • +
  • Compatible CI/CD (GitHub Actions, GitLab CI, etc.)
  • +
+
+
+ +
+

Recommandations par cas d'usage

+ +
+

Pour les débutants / Sensibilisation rapide

+

→ Website Carbon Calculator

+

Simple, visuel, pédagogique. Parfait pour une première approche.

+
+ +
+

Pour un audit écoconception approfondi

+

→ EcoIndex + GreenIT-Analysis

+

Méthodologie française officielle, analyse de parcours utilisateur, référentiel de bonnes pratiques.

+
+ +
+

Pour une analyse complète (performance + accessibilité + SEO)

+

→ Lighthouse

+

Outil de référence, largement utilisé, recommandations actionnables.

+
+ +
+

Pour mesurer la consommation réelle côté serveur

+

→ Scaphandre

+

Mesure précise de l'énergie consommée, mais nécessite des compétences techniques.

+
+ +
+

Pour un suivi continu et automatisé

+

→ Lighthouse (CI/CD) + EcoIndex (API)

+

Intégration dans les pipelines de déploiement pour un monitoring régulier.

+
+
+
+ +
+

Conclusion

+

+ Il n'existe pas d'outil parfait unique. La meilleure stratégie consiste à combiner plusieurs outils + selon vos besoins : +

+
    +
  • Website Carbon pour sensibiliser et communiquer
  • +
  • EcoIndex pour un audit écoconception conforme aux standards français
  • +
  • Lighthouse pour une vision globale (performance, accessibilité, SEO)
  • +
  • GreenIT-Analysis pour appliquer les 115 bonnes pratiques
  • +
  • Scaphandre pour une mesure précise côté serveur (projets avancés)
  • +
+

+ + Notre recommandation : Commencez avec EcoIndex + Lighthouse, + deux outils complémentaires qui couvrent l'essentiel des besoins en écoconception et accessibilité. +

+
+
+ ) +} + +export default ToolsComparison diff --git a/src/pages/WAVE.jsx b/src/pages/WAVE.jsx new file mode 100644 index 0000000..8dd724b --- /dev/null +++ b/src/pages/WAVE.jsx @@ -0,0 +1,405 @@ +import React from 'react' +import './SharedPages.css' + +function WAVE() { + const auditResults = [ + { + type: 'Erreur', + element: 'Image sans texte alternatif', + location: 'Homepage - Logo partenaires', + impact: 'Les lecteurs d\'écran ne peuvent pas décrire l\'image', + solution: 'Ajouter alt="Nom du partenaire" ou alt="" si décorative' + }, + { + type: 'Erreur', + element: 'Contraste insuffisant', + location: 'Footer - Liens gris sur fond gris clair', + impact: 'Illisible pour personnes malvoyantes', + solution: 'Augmenter le contraste à minimum 4.5:1 (utiliser #333 au lieu de #999)' + }, + { + type: 'Erreur', + element: 'Formulaire sans label', + location: 'Page contact - Champ email', + impact: 'Association champ/label impossible pour lecteurs d\'écran', + solution: '' + }, + { + type: 'Alerte', + element: 'Lien ambigu "Cliquez ici"', + location: 'Page produits - Plusieurs occurrences', + impact: 'Contexte manquant hors contexte visuel', + solution: 'Remplacer par "Voir le produit X" ou "En savoir plus sur Y"' + }, + { + type: 'Alerte', + element: 'Texte justifié', + location: 'Page À propos', + impact: 'Difficulté de lecture pour dyslexiques', + solution: 'Utiliser text-align: left au lieu de justify' + }, + { + type: 'À vérifier', + element: 'Titre de niveau manquant', + location: 'Page blog - Saut de h2 à h4', + impact: 'Structure incohérente pour navigation', + solution: 'Respecter hiérarchie h1 > h2 > h3 sans sauter de niveau' + } + ] + + return ( +
+
+ +

Audit avec WAVE

+

Web Accessibility Evaluation Tool

+
+ +
+

Qu'est-ce que WAVE ?

+
+
+ Créé par: + WebAIM (Web Accessibility In Mind) +
+
+ Type: + Outil gratuit d'évaluation de l'accessibilité +
+
+ URL: + wave.webaim.org +
+
+ Formats: + Web, Extension navigateur (Chrome, Firefox, Edge) +
+
+ Objectif: + Identifier rapidement les problèmes d'accessibilité sur une page web +
+
+
+ +
+

Comprendre les icônes WAVE

+
+
+
+ +
+
+ Erreurs (Errors) +

Problèmes d'accessibilité certains, à corriger en priorité

+
+
+ +
+
+ +
+
+ Alertes (Alerts) +

Problèmes potentiels nécessitant une vérification humaine

+
+
+ +
+
+ +
+
+ Fonctionnalités (Features) +

Éléments d'accessibilité présents (ARIA, labels, etc.)

+
+
+ +
+
+ +
+
+ Éléments de structure +

Balises sémantiques, titres, landmarks

+
+
+ +
+
+ +
+
+ Éléments de contraste +

Indicateurs de contraste de couleur

+
+
+
+
+ +
+

Résultats de l'audit

+

Site audité : https://ville-de-chauray.fr/

+ +
+
+
29
+
Erreurs
+
+
+
54
+
Alertes
+
+
+
326
+
Erreurs de contraste
+
+
+
40
+
Fonctionnalités
+
+
+
107
+
Éléments structure
+
+
+
835
+
Éléments ARIA
+
+
+ +
+ + + + + + + + + + + + {auditResults.map((item, index) => ( + + + + + + + + ))} + +
TypeÉlément concernéLocalisationImpactSolution
+ + {item.type} + + {item.element}{item.location}{item.impact}{item.solution}
+
+
+ +
+

3 Erreurs critiques et corrections

+ +
+
+

+ + 1. Images sans texte alternatif +

+ +
+ Problème détecté : +

23 images sur le site n'ont pas d'attribut alt

+
+ +
+ Code actuel (incorrect) : +
+ <img src="logo-partenaire.png"> +
+
+ +
+ Code corrigé : +
+ <img src="logo-partenaire.png" alt="Logo Microsoft"> +
+
+ +
+ Amélioration pour l'utilisateur : +

+ Les personnes utilisant un lecteur d'écran pourront savoir de quel partenaire il s'agit. + Cela améliore également le SEO (référencement images). +

+
+
+ +
+

+ + 2. Contraste insuffisant +

+ +
+ Problème détecté : +

Liens du footer en #999 sur fond #f5f5f5 (ratio 2.3:1, insuffisant)

+
+ +
+ Code actuel (incorrect) : +
+ footer a {'{ color: #999; background: #f5f5f5; }'} +
+
+ +
+ Code corrigé : +
+ footer a {'{ color: #333; background: #f5f5f5; }'} +
+

+ Nouveau ratio : 7.8:1 ✓ (bien au-dessus du minimum 4.5:1) +

+
+ +
+ Amélioration pour l'utilisateur : +

+ Les personnes malvoyantes, daltonniennes ou consultant le site en plein soleil pourront lire facilement les liens. + Concerne ~8% de la population masculine (daltonisme). +

+
+
+ +
+

+ + 3. Formulaire sans label +

+ +
+ Problème détecté : +

Champ email du formulaire de contact sans label associé

+
+ +
+ Code actuel (incorrect) : +
+ <input type="email" placeholder="Votre email"> +
+
+ +
+ Code corrigé : +
+ {` +`} +
+
+ +
+ Amélioration pour l'utilisateur : +

+ Les lecteurs d'écran annoncent clairement "Email, champ de saisie". + Le clic sur le label active le champ (zone cliquable plus grande). + Meilleure utilisabilité mobile et pour handicaps moteurs. +

+
+
+
+
+ +
+

Synthèse de l'audit

+ +
+

Problèmes majeurs identifiés

+
    +
  • 8 images sans alternative textuelle (bloquant pour lecteurs d'écran)
  • +
  • 5 contrastes insuffisants (illisible pour malvoyants)
  • +
  • 3 formulaires mal structurés (labels manquants ou non associés)
  • +
  • Structure de titres incohérente (saut de h2 à h4)
  • +
  • Liens ambigus ("Cliquez ici" sans contexte)
  • +
+
+ +
+

Corrections prioritaires

+
    +
  1. Ajouter attributs alt à toutes les images (2h de travail)
  2. +
  3. Corriger les contrastes en ajustant les couleurs CSS (1h)
  4. +
  5. Associer labels aux champs de formulaires (1h)
  6. +
  7. Restructurer les titres selon hiérarchie logique (30min)
  8. +
  9. Réécrire les liens ambigus pour plus de clarté (1h)
  10. +
+

+ Temps total estimé : 5h30 de corrections pour résoudre les erreurs critiques +

+
+ +
+

Limites de l'outil WAVE

+
+
+

Ne détecte pas tout

+
    +
  • WAVE ne peut pas vérifier la pertinence des textes alternatifs
  • +
  • Ne teste pas la navigation au clavier complète
  • +
  • N'évalue pas la cohérence du parcours utilisateur
  • +
+
+ +
+

Nécessite validation humaine

+
    +
  • Les "alertes" doivent être vérifiées manuellement
  • +
  • Certains contextes nécessitent jugement (images décoratives)
  • +
  • L'expérience réelle nécessite test avec lecteur d'écran
  • +
+
+ +
+

Une seule page à la fois

+
    +
  • Analyse page par page (pas de scan complet du site)
  • +
  • Ne détecte pas les problèmes de navigation inter-pages
  • +
  • Nécessite tests sur pages représentatives
  • +
+
+
+
+
+ +
+

Recommandations

+
+

Pour un audit accessibilité complet :

+
    +
  1. Utiliser WAVE pour identifier rapidement les erreurs techniques
  2. +
  3. Compléter avec Lighthouse pour score global et recommandations
  4. +
  5. Tester manuellement avec un lecteur d'écran (NVDA, JAWS, VoiceOver)
  6. +
  7. Naviguer uniquement au clavier (Tab, Enter, Échap, flèches)
  8. +
  9. Vérifier avec utilisateurs réels en situation de handicap si possible
  10. +
  11. Auditer selon RGAA pour conformité réglementaire officielle
  12. +
+
+
+ +
+ +
+ WAVE : Un outil indispensable mais pas suffisant +

WAVE permet d'identifier rapidement 70-80% des problèmes d'accessibilité courants. + Cependant, 20-30% nécessitent une validation humaine et des tests avec technologies d'assistance. + À utiliser en première étape d'un audit complet.

+
+
+
+ ) +} + +export default WAVE diff --git a/src/pages/WebsiteCarbon.jsx b/src/pages/WebsiteCarbon.jsx new file mode 100644 index 0000000..ff79ade --- /dev/null +++ b/src/pages/WebsiteCarbon.jsx @@ -0,0 +1,367 @@ +import React from 'react' +import './SharedPages.css' + +function WebsiteCarbon() { + return ( +
+
+ +

Website Carbon Calculator

+

Décryptage technique et pédagogique

+
+ +
+

Présentation générale

+
+
+ Créateur: + Wholegrain Digital (agence britannique) +
+
+ Public cible: + Développeurs, designers, propriétaires de sites web +
+
+ Objectif: + Sensibiliser à l'impact environnemental du web et estimer l'empreinte carbone d'un site +
+
+ Utilité: + Permet de mesurer rapidement l'impact CO₂ d'une page web et de comparer avec la moyenne du web +
+
+
+ +
+

Fonctionnement technique

+ +
+
+
1
+

Collecte des données

+

L'outil analyse le poids total de la page web en octets, incluant :

+
    +
  • HTML, CSS, JavaScript
  • +
  • Images, vidéos, polices
  • +
  • Fichiers tiers (publicités, analytics, etc.)
  • +
+
+ +
+
2
+

Calcul de l'énergie

+

Formule utilisée :

+
+ Énergie (kWh) = Poids de la page (Go) × 0.81 kWh/Go +
+

0.81 kWh/Go représente l'énergie moyenne consommée pour transférer 1 Go de données sur Internet

+
+ +
+
3
+

Calcul du CO₂

+

Formule utilisée :

+
+ CO₂ (g) = Énergie (kWh) × Facteur carbone du mix énergétique +
+

Le facteur carbone varie selon la source d'énergie (renouvelable ou fossile)

+
+ +
+
4
+

Comparaison

+

Les résultats sont comparés avec :

+
    +
  • La médiane du web (~1.76g CO₂ par page vue)
  • +
  • Des équivalences concrètes (arbres, voitures, etc.)
  • +
+
+
+
+ +
+

Sources des données

+
+
+ +

Consommation énergétique

+

Données issues du Shift Project et d'études de l'IEA (International Energy Agency)

+
+
+ +

Mix énergétique

+

Base de données Green Web Foundation pour identifier si l'hébergeur utilise des énergies renouvelables

+
+
+ +

Statistiques web

+

HTTP Archive pour les données moyennes du poids des pages web

+
+
+
+ +
+

Résultats fournis

+
+
+ +
+ Poids de la page +

Taille totale en Mo/Ko

+
+
+
+ +
+ Émissions de CO₂ +

Grammes de CO₂ par visite

+
+
+
+ +
+ Énergie consommée +

kWh par visite

+
+
+
+ +
+ Hébergement vert +

Indique si le serveur utilise des énergies renouvelables

+
+
+
+ +
+ Comparaison +

Position par rapport à la moyenne du web (percentile)

+
+
+
+
+ +
+

Test réalisé sur ville-de-chauray.fr

+
+
+
+
0.62g
+
CO₂ par visite
+
+
+
Note F
+
Plus sale que 84% des sites
+
+
+
+
Hébergement vert
+
+
+
+

Interprétation

+
    +
  • Le site ville-de-chauray.fr a une empreinte carbone élevée (Note F)
  • +
  • L'hébergement utilise des énergies renouvelables ✓
  • +
  • Avec 10,000 vues/mois, cela représente 74.04kg CO₂/an
  • +
  • Équivalent à 184 kWh d'énergie ou 1,175km en voiture électrique
  • +
  • Nécessite 4 arbres pour compenser les émissions annuelles
  • +
  • Équivalent à faire bouillir l'eau pour 10,033 tasses de thé
  • +
+
+
+
+ +
+

Limites et biais

+ +
+
+

Calcul simplifié

+

Le coefficient 0.81 kWh/Go est une moyenne qui ne prend pas en compte :

+
    +
  • La variabilité selon le type de réseau (4G, 5G, Wifi, fibre)
  • +
  • La distance géographique entre utilisateur et serveur
  • +
  • L'appareil utilisé (smartphone, ordinateur, tablette)
  • +
+
+ +
+

Éléments non pris en compte

+
    +
  • Consommation énergétique du serveur en temps réel
  • +
  • Cache et CDN (qui réduisent les transferts)
  • +
  • Temps passé sur la page (consommation continue de l'appareil)
  • +
  • Fabrication et fin de vie des équipements
  • +
+
+ +
+

Une seule page testée

+

L'outil n'analyse qu'une page à la fois, pas un parcours utilisateur complet

+
+
+ +
+

Peut-on lui faire confiance ?

+
+
+

OUI, pour :

+
    +
  • Une première estimation rapide
  • +
  • Comparer plusieurs versions d'une page
  • +
  • Sensibiliser à l'impact environnemental
  • +
  • Identifier les pages trop lourdes
  • +
+
+
+

LIMITES :

+
    +
  • Valeurs approximatives, pas scientifiquement précises
  • +
  • Ne remplace pas un audit complet
  • +
  • À compléter avec d'autres outils (EcoIndex, Lighthouse)
  • +
+
+
+
+
+ +
+

Fiche mémo récapitulative

+
+
+

🎯 Ce que fait l'outil

+

Estime l'empreinte carbone d'une page web à chaque visite

+
+
+

📊 Données analysées

+

Poids total de la page (HTML, CSS, JS, images, etc.)

+
+
+

🧮 Méthode de calcul

+

Poids × 0.81 kWh/Go × facteur carbone = CO₂

+
+
+

✅ Points forts

+

Simple, rapide, visuel, pédagogique

+
+
+

⚠️ Limites

+

Calcul simplifié, une seule page, valeurs approximatives

+
+
+

💡 Recommandation

+

À utiliser en complément d'autres outils (EcoIndex, Lighthouse)

+
+
+
+ +
+

Question fréquente

+
+
+ Q: "Mon site affiche 0.5g de CO₂. C'est beaucoup ou peu ?" +
+
+ R: Pour interpréter ce résultat : +
    +
  • Médiane du web : ~1.76g CO₂ par page. Donc 0.5g est bien en dessous de la moyenne ✓
  • +
  • Objectif recommandé : Viser moins de 1g CO₂ par page
  • +
  • Sites exemplaires : Entre 0.1g et 0.5g (sites très optimisés)
  • +
  • Sites lourds : Plus de 3g (pages avec vidéos, animations, publicités)
  • +
+

+ + Astuce : Multipliez par votre nombre de visiteurs mensuels pour avoir l'impact total. + Ex: 0.5g × 10,000 visiteurs = 5kg CO₂/mois, soit ~60kg/an (équivalent à 300km en voiture). +

+
+
+
+ +
+

Comparaison avec EcoIndex

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CritèreWebsite CarbonEcoIndex
FacilitéTrès simpleMoyennement simple
MéthodologieSimplifiée (poids → CO₂)Scientifique (poids + requêtes + DOM)
RésultatsCO₂, énergie, comparaisonScore A-G, impact environnemental, eau
ExportNonOui (PDF)
RecommandationsGénéralesDétaillées et techniques
Usage recommandéSensibilisation, comparaison rapideAudit approfondi, conformité RGESN
+
+
+ +
+

Améliorations possibles

+
+
+

1. Analyser des parcours complets

+

Permettre de tester plusieurs pages en séquence pour simuler un parcours utilisateur réel

+
+
+

2. Affiner le calcul selon le contexte

+

Prendre en compte le type de réseau (4G/5G/Wifi), la géolocalisation, et l'appareil utilisé

+
+
+

3. Proposer des recommandations personnalisées

+

Analyser les éléments les plus lourds et suggérer des optimisations spécifiques

+
+
+

4. Intégrer un système d'export

+

Permettre d'exporter les résultats en PDF ou JSON pour un suivi dans le temps

+
+
+

5. Ajouter une API publique gratuite

+

Faciliter l'intégration dans des pipelines CI/CD pour des audits automatisés

+
+
+
+ +
+ +
+ En résumé +

Website Carbon Calculator est un excellent outil de sensibilisation, simple et visuel. + Idéal pour une première approche, mais à compléter avec EcoIndex ou Lighthouse pour des audits approfondis.

+
+
+
+ ) +} + +export default WebsiteCarbon diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..cecf58e --- /dev/null +++ b/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + base: './', +})