ThemeForge : un système de thèmes CSS prêt à l’emploi qu’un agent IA installe en une seule étape
- Catégorie
- IA et LLM locaux
- Publié
- 5 octobre 2026
- Mis à jour
- 6 octobre 2026
- Par
- Jacob Lloyd — rédigé avec l'aide de l'IA, une fois le projet terminé
- Temps de lecture
- 37 min de lecture
En clair : ThemeForge est un dossier gratuit contenant du CSS ainsi qu’un petit script qui ajoute à toute application web un sélecteur de thèmes, dix thèmes colorés et un ensemble complet de boutons, champs, cartes et boîtes de dialogue prêts à l’emploi. Il a été conçu de telle manière qu’un agent de codage IA puisse l’intégrer dans une application en une seule ligne : l’agent suit simplement un fichier présent dans le dépôt, et l’application obtient ainsi un sélecteur fonctionnel, un thème par défaut conforme au système, ainsi que des tokens de conception à utiliser pour le design.
La plupart des applications web finissent par disposer de trois thèmes, définis en trois endroits différents : un bloc :root dans le CSS principal, un second bloc sous @media (prefers-color-scheme: dark), et un troisième inséré manuellement par un développeur pour faire fonctionner le sélecteur de thèmes. Le résultat est toujours le même : un contrôle qui affiche un aspect différent sur la page d’accueil, un autre dans la page des paramètres ; sans oublier les demandes sur les trackers de bugs expliquant pourquoi le thème sombre colore le pied de page en bleu. J’ai créé ThemeForge afin d’éviter ce genre de problèmes. Il s’agit d’un seul dossier, ui-theme/, qui contient le runtime, une couche de base, les classes de composants, les tokens de tous les thèmes, un sélecteur, des adaptateurs optionnels pour Tailwind et Quasar, ainsi qu’un court fichier AGENTS.md destiné à une IA de programmation. Il suffit d’ajouter ce dossier, d’insérer quatre lignes dans l’en-tête de la page, et l’application dispose alors d’un sélecteur fonctionnel ainsi que d’un thème par défaut adapté au système. L’agent chargé de l’installation effectue tout cela via une seule commande ; le reste de cet article s’adresse à ceux qui souhaitent en comprendre le fonctionnement.
Cet article fait office de guide : il décrit le contenu du dossier, les dix thèmes et leurs finalités, la gestion des contrastes, ainsi que les tokens de design et les classes de composants que vous pourrez utiliser comme référence. La seconde partie constitue volontairement un guide consultable : chaque token, chaque composant, chaque méthode publique y sont listés afin qu’une IA de programmation (ou vous-même plus tard) puisse les reprendre tels quels.
Résumé
- Obtention : github.com/LaserLloyd/ThemeForge — licence MIT, gratuit ; un fichier zip source est également disponible dans la section Téléchargements ci-dessous.
- Contenu : un seul dossier,
ui-theme/, à intégrer dans n’importe quelle application web ; il apporte dix thèmes colorés, un sélecteur, environ 250 tokens de design, une couche élémentaire de base, des standards typographiques et l’ensemble des classes de composants (boutons, champs, interrupteurs, cartes, boîtes de dialogue, notifications, tableaux, shell d’application). Seul du CSS pur et un petit script sont nécessaires. Pas d’étape de compilation, pas de Node, aucune dépendance. - Installation en une seule commande (extrait littéralement du README) :
python3 -c "import urllib.request as u,sys;sys.argv=['update.py','--dest','static/ui-theme'];exec(u.urlopen('https://raw.githubusercontent.com/LaserLloyd/ThemeForge/main/ui-theme/update.py').read())" - En-tête HTML (l’ordre est important) : d’abord
ui-theme.js(classique, bloquant, jamaisdeferoumodule) ; ensuiteui-theme-base.cssetui-components.css; puis le CSS propre à l’application ; enfinui-theme.cssafin que les tokens thématiques priment. - Sélecteur : un seul élément :
<select class="ui-select" data-ui-theme-picker aria-label="Theme"></select>. Il se remplit automatiquement et reste synchronisé entre les différents onglets. - Les dix thèmes : six principaux (Purple, Midnight Gold, Glacier, Forest, Paper, Daylight) plus quatre optionnels (Electric Yellow, LaserLloyd, LaserLloyd Light, Night Red). Chaque thème réussit l’audit de contraste sans aucune défaillance.
- Contraste : texte principal 7:1, texte secondaire 6:1 ; chaque niveau de texte sur toutes les surfaces atteint 4,5:1 ; les contours des contrôles et leurs états activés/désactivés requièrent un ratio de 3:1. L’état sélectionné ou actif se reconnaît par la forme et le remplissage, pas uniquement par la couleur.
- Conçu pour les IA : AGENTS.md décrit le script d’installation sous forme de texte — il suffit d’indiquer cette page à une IA pour qu’elle configure le sélecteur en un seul geste. Le CI du dépôt (GitHub Actions, trois tâches) s’exécute correctement sur un clone : audit de contraste,
node --checksur chaque fichier JS, et exécution des tests via Chrome sans interface graphique.
Où le trouver
Un unique dépôt sous licence MIT : github.com/LaserLloyd/ThemeForge, contenant AGENTS.md comme script d’installation et le rapport de contraste comme preuve d’audit. Préférez un zip ? La section Téléchargements en bas de page propose le code source complet ; la page Téléchargements du site le liste également. Si votre machine ne dispose pas de Python, le README indique aussi git clone –depth 1, npx degit, ainsi qu’une URL jsDelivr <script> fixée à @v1.0.0 pour les prototypes.
Ce que vous obtenez finalement
En quelques instants, l’application adopte l’apparence souhaitée par l’utilisateur : un en-tête doté d’un sélecteur connaissant tous les thèmes, un mode sombre et un mode clair suivant les préférences du système, et une simple ligne CSS dans le code de l’application qui remplace #1d1d39 par background: var(–surface-2). Choisissez un thème, rechargez la page : aucun effet clignotant n’apparaît. Le script runtime s’exécute de façon synchrone dans <head>, lit ses propres paramètres data-* depuis la balise, puis applique le thème avant même le premier rendu. Ouvrez le sélecteur sur deux onglets, modifiez le thème dans l’un d’eux : l’autre se met à jour automatiquement grâce à un écouteur storage. Lors d’une impression en mode sombre ou OLED, le runtime bascule temporairement vers un thème clair (ou masque la navigation selon la valeur de data-print-theme) afin que le document reste lisible sur papier ; pour un thème clair, l’impression se fait tel quel.
ui-app, ui-card, ui-btn, ui-switch et des tokens --surface-* / --text-* / --accent. Le sélecteur est un simple <select data-ui-theme-picker> — le runtime le remplit et le synchronise.L’illustration ci-dessus est une démo intégrée à la page, non pas une capture d’écran externe. L’exemple complet — tous les composants dans chaque thème — se trouve dans le dépôt à l’adresse specimen/index.html (à ouvrir via python3 -m http.server puis en se rendant sur /specimen/).
Comment tout s’articule
Le runtime est un petit script, ui-theme.js (environ 23 Ko, 575 lignes) qui effectue trois tâches séquentielles. Il lit d’abord ses attributs data-* depuis la balise <script> où il est inséré (permettant ainsi aux paramètres de voyager avec le fichier), examine ensuite localStorage et les préférences système clair/sombre, puis écrit le thème choisi dans <html data-palette> ainsi que le mode actif dans <html data-theme> (valeurs possibles : amoled, dark, light) avant le premier rendu. Il parcourt ensuite le document à la recherche des éléments <select data-ui-theme-picker> pour les remplir de la liste des thèmes activés (et écoute ceux qui pourraient être ajoutés ultérieurement par un framework). Enfin, il expose window.UITheme et, lorsque ui-components.js est chargé, window.UIComponents, tout en installant un écouteur storage pour maintenir la synchronisation entre plusieurs onglets. Le CSS est tout aussi simple : ui-theme-base.css définit le texte de la page, les liens, l’anneau de focus, les barres de défilement et les réglages de réduction du mouvement ; ui-components.css dessine les boutons, champs, interrupteurs, cartes, boîtes de dialogue, notifications et tableaux ; ui-theme.css est le seul fichier variant selon les thèmes — il définit chaque token (couleur, typographie, espacement, rayon, ombre, animation) pour les dix thèmes via un sélecteur [data-palette=”…”] (le thème Purple correspond à :root, c’est-à-dire l’absence de data-palette). Le runtime sélectionne le thème ; le CSS s’en charge du rendu.
Le suivi du thème du système d’exploitation est ce qui surprend le plus les utilisateurs : data-default=”auto” lit prefers-color-scheme à chaque chargement de page, et le moteur combine cette valeur avec le choix enregistré par l’utilisateur (ou avec data-default-dark et data-default-light) pour déterminer le thème. Changez le thème du système et actualisez la page — elle suit automatiquement. Si l’utilisateur choisit explicitement un thème via le sélecteur, ce choix prévaut jusqu’à ce qu’il soit effacé (ou que UITheme.reset() soit appelé). Le moteur gère également l’événement storage afin que deux onglets ouverts de la même application restent synchronisés sans aucune communication avec le serveur.
Installation
Le fichier AGENTS.md du dépôt contient le guide complet ; voici la version abrégée ainsi que les quatre balises à insérer dans l’en-tête. Depuis la racine de l’application :
python3 -c "import urllib.request as u,sys;sys.argv=['update.py','--dest','static/ui-theme'];exec(u.urlopen('https://raw.githubusercontent.com/LaserLloyd/ThemeForge/main/ui-theme/update.py').read())"
Ce script télécharge l’installateur, qui récupère la dernière version, compare chaque fichier à sa liste SHA-256 dans files.json et écrit le dossier sur le disque. Sur Windows, utilisez python ou py au lieu de python3 ; la commande fonctionne pareillement dans PowerShell, cmd et bash. Les quatre lignes à placer dans l’élément <head> de chaque page :
<script src="/static/ui-theme/ui-theme.js"
data-themes="purple,midnight-gold,glacier,forest,paper,daylight"
data-default="auto" data-default-dark="midnight-gold" data-default-light="daylight"
data-storage-key="myapp.theme"></script>
<link rel="stylesheet" href="/static/ui-theme/ui-theme-base.css">
<link rel="stylesheet" href="/static/ui-theme/ui-components.css">
<!-- vos feuilles de style -->
<link rel="stylesheet" href="/static/ui-theme/ui-theme.css">
Trois règles, chacune pour une raison précise. Le script ui-theme.js est classique et bloquant — jamais type=”module”, defer ou async — car il lit ses paramètres depuis sa propre balise (document.currentScript, qui vaut null pour les scripts module) et définit le thème avant le premier rendu. Les couches de base et de composants se chargent avant la CSS de l’application afin que les règles propres à l’app aient la même spécificité. ui-theme.css vient en dernier, empêchant ainsi toute variable ancienne de l’application de masquer un token de thème. Le sélecteur ne nécessite qu’une seule ligne : <select class=”ui-select” data-ui-theme-picker aria-label=”Theme”></select>. En script, on dispose de UITheme.set(‘glacier’), UITheme.current() et UITheme.onChange(fn). L’installation complète prend environ cinq minutes, comme indiqué dans l’introduction d’AGENTS.md.
L’agent qui a effectué cette installation en une seule étape sur une vraie application ressemblait à ceci : un humain a demandé à un agent de codage IA de mettre en place un mode sombre et un sélecteur de thèmes. L’agent a lu AGENTS.md, exécuté la commande unique dans static/ui-theme/, inséré les quatre balises d’en-tête dans le modèle de base, remplacé deux valeurs de couleur par var(–surface-2) et var(–text-secondary), puis ajouté le sélecteur en haut de page. Six lignes seulement modifiées dans l’application, aucune nouvelle dépendance. L’audit de contraste couvre déjà toutes les paires possibles générées par les composants ; le moteur suit automatiquement le thème du système d’exploitation, et un futur commit qui codifierait une couleur serait immédiatement détecté par l’étape de lint.
Les thèmes
Les six premiers constituent la gamme de base proposée par défaut ; les quatre autres ne s’affichent que si l’application les répertorie explicitement. Chaque thème passe avec succès l’audit de contraste (docs/contrast-report.md) : le texte principal a un ratio de 7:1, le texte secondaire 6:1 sur toute surface, tous les autres niveaux de texte 4,5:1 ; les bords des contrôles et leurs états « on » ou « sélectionné » respectent le ratio de 3:1, et la forme suffit à identifier ces états en plus de la couleur dans chaque thème. Voici la liste complète :
| Thème | Catégorie | Fond | Description |
|---|---|---|---|
| Purple | de base | sombre | Accent violet et texte lavande doux sur un indigo profond. C’est le thème de base : ses valeurs correspondent aux paramètres par défaut de :root. |
| Midnight Gold | de base | OLED | Or chaud sur du noir pur, idéal pour les écrans OLED. |
| Glacier | de base | OLED | Texte et signature bleu glace sur noir pur, avec un accent turquoise profond. |
| Forest | de base | OLED | Vert sauge pâle et lichen, avec des bruns d’écorce sur noir pur. |
| Paper | de base | clair | Parchemin chaleureux, texte brun encre, police serif pour les textes longs et un accent rouge. |
| Daylight | de base | clair | Blanc épuré avec un accent bleu. |
| Electric Yellow | optionnel | sombre | Jaune acide sur gris graphite, avec des rayons arrondis et des traits nets. |
| LaserLloyd | optionnel | sombre | Bleu laser sur un gris graphite presque noir, inspiré du site de l’auteur. Il s’associe à LaserLloyd Light. |
| LaserLloyd Light | optionnel | clair | Version claire du thème LaserLloyd. |
| Night Red | optionnel | OLED | Thème nocturne à faible émission de lumière bleue : texte couleur braise sur noir pur, structure rouge, aucune composante bleue dans aucune couleur. |
Trois des quatre thèmes optionnels sont volontairement très contrastés. Electric Yellow est celui que j’utilise lorsque je révise du CSS dans le noir et que je veux que la page ressorte davantage que mon éditeur ; LaserLloyd reprend l’aspect bleu-graphite de ce site afin d’assurer la cohérence entre le site et l’application que je développe ; Night Red constitue un « profil nocturne » à part, soumis par l’outil de contraste à une vérification étendue sur 171 lignes (147 standards plus des lignes supplémentaires pour les conditions de faible luminosité) où chaque token du thème comporte zéro valeur bleue (docs/contrast-report.md section « Audit sans bleu » ; README.md §Night Red ; docs/THEMES.md §Night Red).
Pièges à éviter
- Une couleur codée en dur dans votre CSS est le bug, pas le thème. L’audit ne vérifie que les tokens ; un
color: #fffdans le code de l’application reste blanc quel que soit le thème.tools/lint_colors.py(dans le dépôt) ainsi que la commande grep dans AGENTS.md §8 détectent ce problème ; le lint en CI ne lance pas cette étape, donc exécutez-la localement avant de fusionner. ui-theme.jsdoit être placé en premier dans<head>, en mode classique, et non avecdeferoumodule. Un script en mode module ne dispose pas dedocument.currentScript, et le runtime lit ses propres attributsdata-*depuis la balise. Le symptôme est un affichage temporaire du mauvais thème, puis du bon thème.ui-theme.cssdoit se charger après le CSS de l’application, et non avant. L’ordre est : base, composants, application, tokens. Une règle de l’application ayant la même spécificité mais chargée après les tokens est écrasée.data-themesdétermine ce que le sélecteur de thèmes affiche. Un thème enregistré dont le slug ne figure pas dansdata-themesest ignoré lors du prochain chargement (le runtime revient alors au thème par défaut). Lorsque vous supprimez un thème, ajoutez une foisdata-legacy-keyetdata-legacy-mapafin de migrer la valeur enregistrée.- Les classes
rounded-mdetfont-sansde Tailwind changent après l’installation. C’est normal : le thème définit les tokens–radius-et–font-, dont les valeurs priment. Ne tentez pas de les modifier. - Avertissement d’hydratation de Next.js. Ajoutez
suppressHydrationWarningà<html>et désactivez la règle de lintno-sync-scriptspour le bloc : le runtime définit des attributs sur<html>avant que React ne procède à l’hydratation. - Une autre bibliothèque lit
data-themesur<html>(daisyUI, Pico). Le runtime écrit alorsdata-theme=”amoled|dark|light”dans cette balise ; vérifiez que la bibliothèque ne réagit pas à cela. Si c’est le cas, utilisezdata-mirror-attrpour lui attribuer un autre attribut. - Ne modifiez rien à l’intérieur de
ui-theme/. Ce dossier est remplacé lors des mises à jour ; le lint détecte toute modification manuelle et l’installation refusera de surcharger un fichier modifié. Transférez plutôt vos modifications dans le CSS propre à l’application. - Les polices mentionnées dans les stacks ne sont pas intégrées par défaut. Chaque stack fait appel aux polices système en cas de besoin ; si vous souhaitez utiliser Inter, JetBrains Mono, Space Grotesk ou Archivo, chargez-les vous-même (le document d’intégration propose un extrait
<link>à copier-coller). prefers-contrast: moreaugmente l’épaisseur des traits et rend plus lisibles les textes les plus clairs. Aucun paramètre de thème n’est nécessaire ; le runtime détecte cette requête et le CSS s’adapte en conséquence. Le groupe de tokenshigh-contrastconstitue l’option héritée pour obtenir le même effet.
Où cela me laisse-t-il ?
Ce que je voulais, c’était un moyen d’offrir à chaque application que je développe un vrai système de thèmes, sans avoir à en écrire un depuis zéro à chaque fois ; ThemeForge est donc la version que je souhaite conserver. Les dossiers que j’intègre dans les applications sont désormais ui-theme/ et un autre dossier correspondant aux fonctionnalités propres à l’application ; les quatre balises head-block ainsi que le sélecteur de thèmes restent identiques quel que soit le framework utilisé. L’agent chargé de l’installation lit le fichier AGENTS.md, et non un message Slack. Les audits de contraste, les tests d’exécution via Chrome headless, ainsi que node –check sur chaque fichier JS distribué me donnent la confiance nécessaire pour continuer à avancer. La version est fixée à 1.0.0 car les tokens constituent l’API publique ; renommer un token équivaut donc à une mise à jour majeure. Chaque version correspond à une balise Git, le dossier VERSION est synchronisé en conséquence, et les fichiers JSON sont signés par la même liste de sommes de contrôle que celle vérifiée par update.py. La CI du dépôt lui-même fonctionne correctement sous Linux, Windows et Python 3.9 ; les mêmes vérifications s’effectuent sans problème sur une nouvelle copie du code sur cette machine.
Référence (pour les agents d’IA)
Le reste de cet article constitue une fiche de référence qu’un agent de codage (ou mon futur moi) peut copier mot pour mot. La première moitié ci-dessus est du texte descriptif ; la seconde moitié est structurée, facile à parcourir et conçue pour être lue par un outil. Chaque token, chaque classe de composant, chaque méthode publique sur window.UITheme et window.UIComponents, chaque thème, chaque paramètre configurable via les balises script, chaque adaptateur, le chemin d’installation complet, les règles de contraste ainsi que les informations de version se trouvent ci-dessous.
Manifeste de fichiers
Le dépôt contient deux arborescences importantes : le bundle que l’application copie, et les outils ainsi que la conception permettant de le créer. Cet article porte sur le bundle.
Paquet d’intégration — ce que l’application copie (ui-theme/):
| Fichier | Taille | Lignes | Description |
|---|---|---|---|
ui-theme.js |
23 515 o | 575 | En temps d’exécution : applique le thème sauvegardé avant le premier affichage, suit les paramètres clair/sombre du système via data-default="auto", remplit les sélecteurs de thèmes, synchronise les onglets, expose window.UITheme |
ui-theme-base.css |
13 255 o | — | Valeurs par défaut des éléments : texte du corps, liens, titres, code, anneau de focus, barres de défilement, gestion du mouvement réduit, niveaux de texte, .ui-markdown, couleurs de highlight.js |
ui-components.css |
51 344 o | — | Composants : boutons, champs, cases à cocher, boutons radio, commutateurs, cartes, badges, boîtes d’information, onglets, contrôles segmentés, tableaux, boîtes de dialogue, menus, infobulles, notifications, barre de progression, blocs de code, ainsi qu’un shell d’application |
ui-theme.css |
86 389 o (21 197 o compressé en gzip) | — | Jetons de tous les dix thèmes (environ 20 Ko compressés en gzip) |
ui-components.js |
5 441 o | 135 | Fonctions optionnelles confirm(), alert(), prompt() et toast() adaptées au thème |
ui-theme.d.ts |
2 841 o | — | Déclarations TypeScript pour window.UITheme et window.UIComponents |
update.py |
16 081 o | — | Installe et met à jour le dossier ; vérifie chaque fichier via son hash SHA-256 |
themes.json |
4 089 o | — | Liste des thèmes (nom, famille, couleur de fond, jeu de couleurs, échantillons, polices) |
files.json |
1 401 o | — | Liste des sommes de contrôle que update.py vérifie |
VERSION |
30 o | — | Version du dossier (ThemeForge 1.0.0 3db64d9f574f) |
README.md |
6 342 o | — | Notes d’utilisation propres au paquet |
adapters/quasar.css |
24 997 o | — | Mappage NiceGUI/Quasar |
adapters/quasar.js |
1 468 o | 40 | Pont JavaScript NiceGUI/Quasar |
adapters/tailwind.css |
4 399 o | — | Mappage @theme inline pour Tailwind CSS v4 |
adapters/tailwind-v3.preset.js |
2 915 o | 54 | Préréglage pour Tailwind CSS v3 |
Le design et les outils utilisés pour créer le bundle (non copiés dans l’application) :
| Fichier | Taille | Description |
|---|---|---|
README.md |
6,592 B | README du dépôt |
AGENTS.md |
19,063 B | Le script d’installation, présenté sous forme de texte, destiné aux agents IA |
CHANGELOG.md |
4,425 B | Historique des versions (1.0.0 est la première version publiée) |
CLAUDE.md |
1,498 B | Conventions du dépôt pour Claude |
CONTRIBUTING.md |
4,476 B | Comment contribuer au développement des thèmes |
LICENSE |
1,067 B | Licence MIT ; copyright © 2026. Le fichier original cite l’auteur par son nom personnel ; le site utilise une forme de marque ; la chaîne de texte sur disque correspond à celle du fichier LICENSE d’origine. Non inclus dans le fichier zip de téléchargement : la ligne de copyright originale mentionne l’auteur par son nom personnel, et le système de protection du site considère cela comme une divulgation d’identité non vérifiable. |
docs/TOKENS.md |
15,391 B | Liste de tous les tokens, classés par groupe |
docs/COMPONENTS.md |
13,889 B | Description de toutes les classes de composants, avec exemples de code |
docs/THEMES.md |
8,081 B | Présentation des thèmes, notes de conception et procédure pour en ajouter un nouveau |
docs/INTEGRATIONS.md |
10,604 B | Étapes d’installation selon chaque framework |
docs/TEXT.md |
5,465 B | Niveaux de texte et style Markdown utilisé |
docs/UPDATING.md |
4,134 B | Procédure de mise à jour, verrouillage des versions, mise en cache, explication sur la validité des sommes de contrôle |
docs/contrast-report.md |
124,372 B | Rapport d’audit de contraste (10 thèmes × 147 à 171 lignes) |
docs/theme-template.css |
9,854 B | Modèle de thème vide destiné à tools/sync_theme.py |
tokens/<theme>.json |
~57 Ko chacun | Tokens résolus pour chaque thème, au format JSON conforme aux spécifications W3C DTCG |
examples/static-html/ |
3,731 B (fichier index) | Exemple simple en HTML/CSS/JS |
examples/fastapi-jinja/ |
2,172 B (fichier app.py) | Exemple d’utilisation avec FastAPI et Jinja |
specimen/index.html |
17,721 B | Présentation locale de tous les composants dans tous les thèmes |
tools/check_contrast.py |
43,171 B | Script d’audit de contraste (exécuté en CI) |
tools/lint_colors.py |
22,711 B | Vérification des valeurs de couleur dans le CSS de l’application |
tools/sync_theme.py |
41,671 B | Génère à nouveau ui-theme.css, tokens/*.json et docs/contrast-report.md à partir de src/css/*.css |
tools/update.py |
16,081 B | Script d’installation/vérification (équivalent à ui-theme/update.py) |
tools/screenshots.py |
3,273 B | Génération de captures d’écran des thèmes pour docs/images/ |
tests/test_tools.py |
17,606 B | Tests unitaires en Python (32 tests dans cette copie ; 1 test est ignoré) |
tests/run_browser_tests.py |
3,570 B | Tests exécutés via Chrome headless sur l’environnement de runtime |
tests/runtime.html |
24,930 B | Script de test pour le navigateur |
.github/workflows/ci.yml |
— | Configuration CI : vérification du contraste, node --check, tests unitaires, tests navigateur, vérification des caractères CRLF ; exécuté sur Linux, Windows et Python 3.9 |
Le CI propre au dépôt exécute trois tâches : checks sur Ubuntu avec Python 3.12, tools-on-windows sur Windows avec Python 3.11, et updater-on-oldest-python sur Ubuntu avec Python 3.9. Le clone local passe le test node --check pour chaque fichier JS distribué ; l’outil de contraste reproduit les dix lignes de l’audit, et tous les tests unitaires réussissent (32 tests réussis, 1 ignoré en 1,212 s sous Linux avec Python 3.14, le 05/10/2026).
Jeton de conception (groupes, rôle, valeur d’exemple)
Chaque thème résout tous les jetons. Les valeurs ci-dessous correspondent à celles de Purple (la base :root) ; les autres thèmes disposent de leurs propres valeurs résolues dans tokens/<theme>.json (format JSON DTCG). Utilisez un jeton via var(--nom). Ne redéclarez jamais un jeton dans le CSS de l’application : attribuez aux variables d’application un préfixe propre à l’application (--myapp-sidebar-w) ou créez un alias pour le jeton (--myapp-brand: var(--accent)). Source : docs/TOKENS.md.
| Groupe | Token | Rôle | Exemple (Violet) |
|---|---|---|---|
| Surfaces | --surface-void |
plus foncé que la page (pleine largeur) | #08080f |
| Surfaces | --surface-0 |
la page | #0e0e1b |
| Surfaces | --surface-1 |
barre de navigation | #14142a |
| Surfaces | --surface-2 |
cartes | #1d1d39 |
| Surfaces | --surface-3 |
état survol | #232342 |
| Surfaces | --surface-4 |
état actif | #262648 |
| Surfaces | --surface-sunken |
zones en retrait | #0a0a14 |
| Surfaces | --surface-overlay |
menus, popovers | #1d1d39 |
| Surfaces | --glass-1/2/3 |
panneaux translucides | rgba(29,29,57,.72/.84/.92) |
| Surfaces | --glass-highlight |
effet de brillance sur le verre | rgba(255,255,255,.055) |
| Surfaces | --code-bg |
arrière-plan du code | #0b0b16 |
| Texte | --text-primary |
texte principal | #e8e8f0 |
| Texte | --text-secondary |
étiquettes, descriptions | #a0a0bd |
| Texte | --text-tertiary |
indices, horodatages | #8d8db0 |
| Texte | --text-disabled |
contrôles désactivés | #717195 |
| Texte | --text-inverse |
texte sur un fond clair | #0e0e1b |
| Texte | --text-link / --text-link-hover |
liens et texte d’accentuation | #a78bfa / #c4b5fd |
| Texte | --on-accent / --on-danger / --on-success / --on-warning / --on-media / --on-media-muted |
texte sur un fond d’accentuation | #ffffff / #2a0808 / … |
| Accentuation | --accent / --accent-hover / --accent-pressed |
accent de marque, actions principales | #7c3aed / #8250f0 / #6d28d9 |
| Accentuation | --accent-subtle / --accent-muted / --accent-glow |
arrière-plan teinté, remplissage doux, effet de lueur | rgba(124,58,237,.16/.26/.35) |
| Accentuation | --accent-rgb |
permet à le JS de composer rgba(var(--accent-rgb), .2) |
124,58,237 |
| Accentuation | --accent-2 / --accent-2-subtle |
accentuation secondaire | #5eead4 |
| Appels à l’action | --cta / --cta-hover / --cta-pressed / --on-cta |
zone d’action principale | #7c3aed / #8250f0 / #6d28d9 / #ffffff |
| Appels à l’action | --cta-shadow / --cta-shadow-hover |
ombres portées pour la zone d’action | 0 6px 20px rgba(124,58,237,.28) / .38 |
| Appels à l’action | --highlight / --highlight-subtle / --on-highlight |
marqueurs, couleur des infobulles | #fcd34d / rgba(252,211,77,.12) / #251a00 |
| Lignes | --border-subtle / --border / --border-strong |
les bords des contrôles de formulaire sont --border-strong avec un ratio de 3:1 |
#21213b / #292945 / #6f6f9c |
| Lignes | --divider |
lignes fines | #21213b |
| Lignes | --focus-ring / --focus-ring-width / --focus-ring-offset |
cercle de focus | #a78bfa / 2px / 2px |
| Lignes | --glass-stroke / --glass-stroke-strong |
bordures en verre | rgba(255,255,255,.08/.16) |
| États des contrôles | --selected / --on-selected |
activé / coché / enfoncé / actuel | selon le thème |
| États des contrôles | --unselected-border / --unselected-fg |
contour désactivé, ratio de 3:1 sur toutes les surfaces | selon le thème |
| Statut | --success / --warning / --danger / --info |
points, barres, remplissages | selon le thème |
| Statut | --success-text / --warning-text / --danger-text / --info-text |
couleur du texte pour chaque statut | selon le thème |
| Statut | --success-subtle / --warning-subtle / --danger-subtle / --info-subtle |
arrière-plan teinté pour chaque statut | selon le thème |
| Statut | --success-border / --warning-border / --danger-border / --info-border |
bordures de statut | selon le thème |
| Statut | --idle / --idle-text / --idle-subtle / --idle-border / --idle-hover |
état neutre | selon le thème |
| Statut | --live / --live-text / --live-subtle / --live-border / --live-hover |
indicateur « actif » / « en cours d’exécution » | selon le thème |
| Code / syntaxe | --syn-bg / --syn-fg / --syn-comment / --syn-keyword / --syn-string / --syn-number / --syn-function / --syn-attr / --syn-tag / --syn-builtin / --syn-type / --syn-variable / --syn-literal / --syn-operator / --syn-title / --syn-addition |
palette de mise en évidence syntaxique | selon le thème |
| Code / syntaxe | --code-header-bg / --code-stroke |
bordures des blocs de code (foncées dans tous les thèmes) | selon le thème |
| Graphiques | --cat-1 … --cat-12 |
couleurs des séries, ratio de 3:1 sur --surface-0 |
selon le thème |
| Graphiques | --seq-1 … --seq-5 |
palette du plus clair au plus foncé | selon le thème |
| Graphiques | --div-1 … --div-5 |
mauvais, neutre, bon | selon le thème |
| Graphiques | --chart-grid / --chart-axis |
éléments graphiques des graphiques | selon le thème |
| Bulles de dialogue | --bubble-user-text / --bubble-assistant-text / --bubble-user-bg / --bubble-assistant-bg |
bulles de chat | selon le thème |
| Bulles de dialogue | --rp-speech / --rp-thought / --rp-shout / --rp-whisper / --rp-ooc / --rp-action / --rp-critical |
couleurs des bulles de rôle-play | selon le thème |
| Différences | --diff-add-bg / --diff-add-text / --diff-remove-bg / --diff-remove-text |
vue des différences | selon le thème |
| Typographie | --fs-xs / --fs-sm / --fs-md / --fs-lg / --fs-xl / --fs-2xl |
tailles de police | selon le thème |
| Typographie | --fw-medium / --fw-semibold / --fw-bold |
épaisseurs de police | selon le thème |
| Typographie | --font-sans / --font-mono / --font-display |
polices utilisées | selon le thème |
| Espacement | --space-1 … --space-8 |
4 à 64 px | selon le thème |
| Rayons | --radius-sm / --radius-md / --radius-lg / --radius-pill |
formes des contrôles | selon le thème |
| Ombres | --shadow-1 / --shadow-2 / --shadow-3 / --shadow-4 |
niveaux d’ombres portées | selon le thème |
| Couches Z | --z-modal / --z-toast / --z-popover / --z-nav |
ordre de superposition | selon le thème |
| Animation | --motion-fast / --motion-med / --motion-slow / --easing-standard |
durées d’animation (le mode « réduction du mouvement » arrête les boucles) | selon le thème |
| Markdown | --md-bold / --md-italic / --md-bolditalic / --md-bolditalic-glow |
mise en forme Markdown dans l’application | selon le thème |
| Divers | --mark-bg / --selection-text / --selection-bg / --media-filter / --quote-bar / --heartbeat-text / --heartbeat-hover / --offline-text / --on-offline / --canvas-handle |
petites fonctionnalités | selon le thème |
Source : docs/TOKENS.md (22 groupes, environ 270 tokens — grep -oE '\| \(–[a-zA-Z][a-zA-Z0-9-]*)` |’ docs/TOKENS.md | sort -u | wc -lrenvoie 272 noms de tokens uniques ; 22 groupes## `).
Classes de composants (chaque modificateur, sur une seule ligne)
L’état provient des attributs natifs (disabled, checked, aria-pressed, aria-selected, aria-current, aria-invalid, aria-busy, [open]) — jamais d’une classe supplémentaire. La plupart des sélecteurs ne comportent qu’une seule classe afin qu’une règle ultérieure de même spécificité puisse l’écraser. Les propriétés logiques sont utilisées partout (fonctionnent en écriture de droite à gauche). « On », « coché », « enfoncé » et « actuel » utilisent --selected ; « off » correspond à un contour ; « désactivé » se caractérise par un trait en pointillés et aucun remplissage. Source : docs/COMPONENTS.md.
| Classe | Modificateurs / parties | Description |
|---|---|---|
ui-stack |
— | Colonne, espacement --space-3 |
ui-row |
— | Ligne à enroulement, centrée, espacement --space-2 |
ui-grid |
--ui-grid-min |
Colonnes auto-générées, min défini sur l’élément |
ui-container |
— | Centré, largeur maximale --content-max |
ui-spacer |
— | Pousse le reste de la ligne vers la fin |
ui-app |
__header __nav __main |
Structure d’application (en-tête, navigation latérale, contenu principal) ; la navigation se masque en dessous de 760 px |
ui-brand |
— | Titre de l’application dans l’en-tête |
ui-nav |
__label __item |
Navigation latérale |
ui-title / ui-heading / ui-subheading / ui-lead / ui-kicker / ui-small / ui-mono / ui-num / ui-kbd / ui-code / ui-divider |
— | Hiérarchie typographique (<hr> correspond à .ui-divider) |
ui-text-primary / -secondary / -tertiary / -disabled / -link |
— | Classes de niveaux de texte (utilisez les tokens pour du nouveau code) |
ui-markdown |
— | Conteneur pour le Markdown rendu ; voir docs/TEXT.md |
ui-btn |
--primary --ghost --outline --danger --cta --sm --lg --icon --block |
Boutons ; fonctionne aussi avec <a> ; s’associe à aria-pressed / aria-busy / disabled |
ui-btn-group |
— | Boutons adjacents partagent un bord arrondi |
ui-field |
— | Conteneur de champ de formulaire (étiquette + contrôle + aide) |
ui-label |
— | Étiquette du champ |
ui-input |
--sm |
Champ de saisie texte |
ui-select |
— | <select> stylisé ; data-ui-theme-picker active le sélecteur de thème |
ui-textarea |
— | <textarea> stylisé |
ui-help |
— | Texte d’aide du champ (liens vers aria-describedby) |
ui-error |
— | Texte d’erreur du champ (liens vers aria-describedby) ; s’affiche lorsque aria-invalid="true" |
ui-input-group |
— | Paire champ + bouton (ex. : recherche) |
ui-check |
— | Ligne de cases à cocher / boutons radio / interrupteurs |
ui-checkbox |
— | <input type="checkbox"> natif stylisé |
ui-radio |
— | <input type="radio"> natif stylisé |
ui-switch |
— | Case à cocher role="switch" stylisée comme un interrupteur |
ui-switch-state |
data-on data-off |
Étiquettes « On » / « Off » (masquées aux lecteurs d’écran ; l’interrupteur annonce lui-même son état) |
ui-range |
— | Champ de sélection de plage natif coloré avec --slider-color |
ui-segmented |
— | Groupe de boutons agissant comme des boutons radio |
ui-tabs |
ui-tab |
Liste d’onglets (les onglets liens utilisent <a aria-current="page">) ; la gestion au clavier relève de l’application |
ui-card |
__header __title __footer --raised --interactive |
Carte ; aria-selected / aria-current mettent en évidence une carte sélectionnée |
ui-well |
— | Zone en retrait (journaux, prévisualisations, contenu secondaire) |
ui-stat |
__label __value |
Paire étiquette / valeur |
ui-badge |
--success --warning --danger --info --live --accent --neutral |
Badge en forme de pilule |
ui-dot |
--success --warning --danger --info --live --neutral |
Point d’état |
ui-count |
--accent --danger |
Compteur |
ui-callout |
--info --success --warning --danger __title |
Zone colorée (utilisez role="alert" pour les erreurs) |
ui-table-wrap |
— | Conteneur pour <table class="ui-table"> afin d’avoir des en-têtes collants / défilement |
ui-table |
--hover --compact td.num |
Styles de tableau |
ui-dialog |
— | <dialog> stylisé |
ui-drawer |
— | Panneau latéral sur <dialog> |
ui-menu |
__item |
Liste de menus |
[data-ui-tooltip] |
data-ui-tooltip-side="bottom" |
Infobulle sur n’importe quel élément |
ui-progress |
— | Barre de progression linéaire |
ui-spinner |
— | Indicateur de chargement |
ui-skeleton |
— | Skeleton de chargement |
ui-breadcrumbs |
— | Liste de navigation |
ui-pagination |
— | Pagination |
ui-avatar |
— | Avatar circulaire |
ui-chip |
__remove |
Chip supprimable / sélectionnable |
ui-details |
— | Accordéon basé sur <details> |
ui-fieldset |
— | Ensemble de champs + légende |
ui-codeblock |
__bar |
<figure> contenant <figcaption class="ui-codeblock__bar"> + <pre> |
ui-empty |
— | État vide |
ui-bubble |
--user --assistant |
Bulle de chat |
Méthodes publiques sur window.UITheme et window.UIComponents
Source : ui-theme/ui-theme.d.ts (déclarations TypeScript pour les deux objets globaux).
window.UITheme (UIThemeApi) :
| Membre | Type | Rôle |
|---|---|---|
version |
string (en lecture seule) |
La version du dossier, ex. "1.0.0" |
config |
objet (en lecture seule) | { themes, default, auto, storageKey, families, printTheme } |
current() |
() => string |
Le slug du thème actif |
theme([slug]) |
(slug?) => UIThemeInfo \| null |
Informations sur un thème (ou le thème actif) |
list() |
() => UIThemeInfo[] |
Tous les thèmes activés |
set(slug) |
(slug) => string |
Basculer vers un thème ; renvoie le slug |
reset() |
() => string |
Revenir au thème par défaut ; renvoie le slug |
partner([slug]) |
(slug?) => string \| null |
Le partenaire clair/sombre d’une famille (ex. laserlloyd ↔ laserlloyd-light) |
toggleFamily() |
() => string \| null |
Basculer vers le thème partenaire ; renvoie le nouveau slug |
onChange(listener) |
(fn) => () => void |
S’abonner ; renvoie une fonction pour se désabonner ; detail.print vaut true lors d’un changement lié à l’impression |
token(name, [el]) |
(name, element?) => string |
Valeur brute du token (ex. "#7c3aed") |
tokens(names, [el]) |
(names, element?) => Record<name, string> |
Plusieurs tokens à la fois |
color(name) |
(name) => string |
Couleur au format rgb() pour le canevas |
colors(names) |
(names) => Record<name, string> |
Plusieurs couleurs au format rgb() pour le canevas |
mountPicker(target, [opts]) |
(elOrSel, options?) => HTMLSelectElement \| null |
Remplir un sélecteur en tant que sélecteur de thèmes ; les options sont { label, coreLabel, optInLabel, systemLabel } |
L’événement ui-theme-change est déclenché sur document avec { slug, theme, previous, print }.
window.UIComponents (UIComponentsApi) : (nécessite ui-components.js)
| Méthode | Signature | Rôle |
|---|---|---|
confirm |
(opts: UIDialogOptions \| string) => Promise<boolean> |
confirm() thématisé ; les options sont { title, message, label, confirmLabel, cancelLabel, danger } |
alert |
(opts: UIDialogOptions \| string) => Promise<void> |
alert() thématisé |
prompt |
(opts: UIPromptOptions \| string) => Promise<string \| null> |
prompt() thématisé ; renvoie null si l’utilisateur annule ; options supplémentaires : { value, placeholder, type } |
toast |
(message, { kind?, timeout? }) => HTMLElement |
Toast de type info / success / warning / danger ; renvoie l’élément créé |
window.UI_THEME_MANIFEST (optionnel, prioritaire sur les valeurs par défaut définies dans la balise <script>) : { themes, default, defaultDark, defaultLight, storageKey, families, legacy, mirrorAttr, fontsHref, printTheme }. themes accepte un tableau ou une chaîne de caractères séparée par des virgules.
Paramètres via la balise <script> (data-*)
| Attribut | Valeur par défaut | Rôle |
|---|---|---|
data-themes |
les six thèmes principaux (purple,midnight-gold,glacier,forest,paper,daylight) |
Liste de thèmes activés, séparés par des virgules ; détermine le contenu du sélecteur et les valeurs stockées. Sans data-themes, ui-theme.js active les six thèmes avec set==="core" (ui-theme.js:147-149). |
data-default |
le premier thème activé (premier slug dans data-themes, ou purple si aucun n’est activé et que auto n’est pas défini) |
Le slug du thème par défaut ; la valeur littérale auto active le suivi automatique des paramètres système via prefers-color-scheme (ui-theme.js:164) ; un attribut manquant ou vide renvoie au premier thème activé (ui-theme.js:177-179). |
data-default-dark |
le premier thème non clair activé (premier slug dans data-themes où ground !== "light", sinon le premier thème activé, ou purple si rien n’est activé) |
Correspond à la version sombre du mode auto. ui-theme.js:168-175 l’utilise lorsque le système préfère le mode sombre et qu’aucune valeur explicite n’est définie. |
data-default-light |
le premier thème clair activé (premier slug dans data-themes où ground === "light", sinon le premier thème activé, ou purple si rien n’est activé) |
Correspond à la version claire du mode auto. Même logique que ci-dessus (ui-theme.js:168-175). |
data-storage-key |
ui-theme |
Clé utilisée dans localStorage pour stocker le choix de l’utilisateur (ui-theme.js:185). AGENTS.md §3 recommande de la modifier en <nom_app>.theme afin d’éviter les conflits entre plusieurs applications ThemeForge sur la même origine. |
data-families |
false |
Regrouper les thèmes par famille dans le sélecteur |
data-print-theme |
auto |
Thème utilisé lors de l’impression : si le thème actif est sombre ou OLED, auto bascule vers le partenaire clair de la famille (ou le premier thème clair activé, ou daylight) ; un slug spécifique force l’utilisation de ce thème ; none empêche tout changement ; si le thème actif est clair, rien ne change (ui-theme.js:474-490). |
data-legacy-key |
— | Ancienne clé de stockage à migrer une seule fois (avec data-legacy-map) |
data-legacy-map |
— | Correspondance d’ancien vers nouveau pour les valeurs héritées (objet JSON) |
data-mirror-attr |
— | Copier le thème actif dans un autre attribut (utile pour les bibliothèques qui s’y réfèrent) |
data-fonts-href |
— | URL d’une feuille de style à injecter pour la typographie du thème |
Sources : ui-theme.js:147-149, 164, 168-174, 185, 474-490 (valeurs par défaut) et AGENTS.md §3, §12 (recommandations de personnalisation — ex. définir data-themes avec les six thèmes principaux, data-default="auto", data-storage-key="<nom_app>.theme").
Adaptateurs
| Adaptateur | Fichiers | Rôle |
|---|---|---|
| NiceGUI / Quasar | adapters/quasar.js + adapters/quasar.css |
Charger adapters/quasar.js juste après ui-theme.js et adapters/quasar.css après ui-theme.css (via ui.add_head_html). Les boutons, cases à cocher et radios de Quasar adoptent le design des contrôles thématisés ; les contrôles désactivés sont affichés en trait discontinu avec une opacité maximale. Supprimer tout appel à ui.dark_mode() — l’adaptateur s’en charge. |
| Tailwind CSS v4 | adapters/tailwind.css |
@import "./<chemin>/ui-theme/adapters/tailwind.css"; après @import "tailwindcss";. Définit bg-surface-2, text-fg, text-fg-muted, bg-accent, text-on-accent, border-line, etc. Les textes utilisent les classes fg-* car Tailwind gère les tailles via text-*. |
| Tailwind CSS v3 | adapters/tailwind-v3.preset.js |
presets: [require('./<chemin>/ui-theme/adapters/tailwind-v3.preset.js')]. Mêmes tokens que l’adaptateur v4, mais exprimés sous forme de preset v3. |
L’adaptateur Tailwind mappe les tokens du thème ; celui de Quasar mappe les classes des composants. Aucun n’est obligatoire pour le bon fonctionnement du reste du bundle.
Procédure d’installation complète (copier tel quel, extrait d’AGENTS.md)
AGENTS.md est un script d’installation rédigé sous forme de texte, destiné aux agents de programmation IA. Les deux premières sections sont reproduites ci-dessous intégralement ; c’est le prompt auquel l’agent doit être confronté. Les caractères sont échappés conformément à la règle §8 du skill article-forge (un <blockquote> brut interprète le markdown littéralement, donc <, >, & doivent être remplacés par <, >, &).
## 1. Décider une fois | Question | Réponse par défaut | |---|---| | Où placer le dossier ? | Dans le dossier statique de l’application, tel quel : `static/ui-theme/` (Flask, FastAPI, Django), `public/ui-theme/` (Vite, Next.js, Create React App), `app/static/ui-theme/` ou à côté de `index.html` pour un site simple. | | Quels thèmes activer ? | Les six thèmes principaux : `purple,midnight-gold,glacier,forest,paper,daylight`. Ajouter les thèmes optionnels (`night-red`, `electric-yellow`, `laserlloyd`, `laserlloyd-light`) uniquement si l’utilisateur le demande. | | Thème par défaut ? | `auto` : le thème suit automatiquement les paramètres clair/sombre du système jusqu’à ce que l’utilisateur fasse un choix (`data-default-dark="midnight-gold"`, `data-default-light="daylight"`). | | Clé de stockage ? | `<nom_app>.theme`, ex. `notes.theme`. | ## 2. Installer Exécuter depuis le dossier racine de l’application (Python 3.9 ou supérieur ; remplacer `static/ui-theme` par le chemin choisi) :Sur Windows, utiliser `python` (ou `py`) au lieu de `python3` ; la ligne fonctionne de la même manière dans PowerShell, cmd et bash. Elle télécharge l’outil d’installation qui récupère la dernière version sur GitHub, vérifie chaque fichier via ses sommes de contrôle et crée le dossier. Pour les mises à jour ultérieures : `python3 static/ui-theme/update.py`. En mode hors ligne : télécharger un archive `.tar.gz` ailleurs, puis exécuter `python3 update.py --source <archive> --dest static/ui-theme` en utilisant le fichier `update.py` présent dans le dossier `ui-theme/`. Autres méthodes, si Python n’est pas disponible : - **git :** `git clone --depth 1 https://github.com/LaserLloyd/ThemeForge.git ut-tmp`, copier `ut-tmp/ui-theme` dans l’application puis supprimer `ut-tmp`. - **Node :** `npx degit LaserLloyd/ThemeForge/ui-theme#v1.0.0 static/ui-theme`. - **Pas d’installation (prototypes, pages HTML isolées) :** charger les fichiers depuis jsDelivr en pointant sur une version fixe : `https://cdn.jsdelivr.net/gh/LaserLloyd/ThemeForge@v1.0.0/ui-theme/ui-theme.js` (et le même chemin pour chaque fichier CSS). Ne jamais utiliser `@main` car le CDN met en cache les versions jusqu’à 12 heures. Pour mettre à jour une page via le CDN, il suffit de changer la version indiquée ; les étapes 8.5, 9 et 10 ci-dessous ne s’appliquent pas dans ce cas.python3 -c "import urllib.request as u,sys;sys.argv=['update.py','--dest','static/ui-theme'];exec(u.urlopen('https://raw.githubusercontent.com/LaserLloyd/ThemeForge/main/ui-theme/update.py').read())"
Les autres sections d’AGENTS.md (3 à 12) décrivent le bloc <head> à insérer, le sélecteur de thèmes, la génération de tokens, les règles, les graphiques, la vérification, la ligne à ajouter dans le AGENTS.md propre à l’application afin que les agents suivants respectent la convention, la commande de mise à jour, le tableau de dépannage et la liste de référence mentionnée plus haut.
Audit de contraste (résumé, tous les thèmes)
tools/check_contrast.py s’exécute en CI à chaque envoi. Le rapport complet pèse 124 Ko (une section par thème plus un résumé) et se trouve à docs/contrast-report.md ; voici le résumé :
| Thème | Catégorie | Profil | Vérifications | Échecs |
|---|---|---|---|---|
| purple | principal | standard | 147 | 0 |
| midnight-gold | principal | standard | 147 | 0 |
| glacier | principal | standard | 147 | 0 |
| forest | principal | standard | 147 | 0 |
| paper | principal | standard | 147 | 0 |
| daylight | principal | standard | 147 | 0 |
| electric-yellow | optionnel | standard | 147 | 0 |
| laserlloyd | optionnel | standard | 147 | 0 |
| laserlloyd-light | optionnel | standard | 147 | 0 |
| night-red | optionnel | nuit | 171 | 0 |
Contrat exigé par chaque thème : texte principal 7:1, texte secondaire 6:1 (le rapport de contraste mentionne explicitement « la promesse de 6:1 du contrat » — docs/contrast-report.md indique ainsi chaque ligne text-secondary on surface-2) ; tout niveau de texte sur toute surface doit offrir un ratio de 4,5:1, les éléments désactivés 3:1, le texte sur fonds accentués ou d’état 4,5:1, le texte sur fond teinté d’état 4,5:1, le texte sur sélection 4,5:1, les bordures de sélection/non-sélection et les textes correspondants 3:1, les bordures renforcées, pistes de contrôle et anneaux de focus 3:1, les couleurs syntaxiques sur --syn-bg 4,5:1 (les commentaires 3,5:1), et les séries graphiques (--cat-1 à --cat-12) 3:1 sur --surface-0. Activation/désactivation par forme : un contrôle « activé » est rempli avec --selected ; un contrôle « désactivé » n’est qu’un contour en --unselected-border ; pour les commutateurs, l’état « on » ajoute une barre au bouton, l’état « off » un anneau. États désactivés : les éléments désactivés sont affichés en trait discontinu avec la teinte propre à l’état désactivé, même lorsqu’ils sont activés.
Frameworks
Conformément au schéma de docs/INTEGRATIONS.md : HTML brut · FastAPI, Flask, Starlette (modèles Jinja) · Django · Vite, Vue, Svelte, SvelteKit, Astro · Next.js (routeur d’applications) · NiceGUI (Quasar) · Tailwind CSS · Electron, Tauri, pywebview · Polices · Graphiques · Politique de sécurité du contenu. Pour chaque framework : placer ui-theme/ là où les fichiers statiques sont servis, insérer le bloc <head> dans l’ordre indiqué à AGENTS.md §3, et servir le dossier sans mise en cache (ou versionner les URLs) afin que toute mise à jour soit immédiatement visible. Deux exemples concrets figurent dans le dépôt : examples/static-html/ (une seule page) et examples/fastapi-jinja/ (FastAPI + Jinja, avec un app.py de 2 172 octets).
Impression, contraste et animations
Trois éléments que le runtime surveille et auxquels la feuille de style réagit, sans nécessiter de paramètre propre à chaque thème :
-
Impression —
data-print-themedétermine ce qui sera imprimé (ui-theme.js:474-490) : lorsque le thème actif est sombre ou OLED,autobascule vers le thème clair correspondant à la famille de thèmes (ou le premier thème clair activé, oudaylight) ; un slug permet d’imprimer ce thème ;nonene provoque aucun changement. Lorsque le thème actif est clair, la page s’imprime telle quelle. Le mode impression masque la barre latérale de navigation, les notifications, les menus, les panneaux et les infobulles. -
prefers-contrast: more— dans chaque thème, l’épaisseur des contours passe au niveau maximal, et le texte tertiaire est rehaussé au niveau du texte secondaire. -
prefers-reduced-motion— les boucles d’animation s’arrêtent et les animations se raccourcissent ; les variables--motion-*prennent alors la valeur 0.
Versions
À partir du dépôt tel qu’il est au moment de la rédaction de cet article (cloné le 05/10/2026) :
$ git log -1 --format='%h %ci'
56fb665 2026-10-05 21:46:52 +0900 # ThemeForge 1.0.0
$ cat ui-theme/VERSION
ThemeForge 1.0.0 3db64d9f574f
$ wc -c ui-theme/*.js ui-theme/*.css ui-theme/*.ts ui-theme/*.py 2>/dev/null
23515 ui-theme.js
5441 ui-components.js
2841 ui-theme.d.ts
16081 update.py
13255 ui-theme-base.css
51344 ui-components.css
86389 ui-theme.css
$ node --check ui-theme.js ui-components.js
(node v24.18.0) — Tout est conforme, y compris dans adapters/quasar.js et adapters/tailwind-v3.preset.js
$ python -m unittest discover -s tests
32 tests exécutés en 1,212 s
OK (skipped=1)
$ python tools/check_contrast.py --fail-on any
…
## Résumé
| Thème | Ensemble | Profil | Vérifications | Échecs |
| purple | core | standard | 147 | 0 |
| midnight-gold | core | standard | 147 | 0 |
| glacier | core | standard | 147 | 0 |
| forest | core | standard | 147 | 0 |
| paper | core | standard | 147 | 0 |
| daylight | core | standard | 147 | 0 |
| electric-yellow | opt-in | standard | 147 | 0 |
| laserlloyd | opt-in | standard | 147 | 0 |
| laserlloyd-light | opt-in | standard | 147 | 0 |
| night-red | opt-in | night | 171 | 0 |
Cibles : les versions actuelles de Chrome, Edge, Firefox et Safari (2024 ou ultérieures). Cette couche utilise :has(), color-mix() ainsi que l’API popover ; les navigateurs plus anciens conservent les couleurs, mais certains états de composants et menus se dégradent.
Voir aussi : ChatForge : un assistant IA local utilisant le NPU pour la touche Copilot · StudioForge : un serveur LLM fonctionnant uniquement sur GPU · DisPatch : chat IA auto-hébergé · Mon propre stack d’agents IA locaux · Comment faire en sorte qu’un modèle IA adapte n’importe quel projet à votre système
Téléchargements
Gratuit pour un usage personnel. Si ça vous fait gagner un après-midi, le bouton café n'est pas loin.