Evan Bombart · v1.0.0

Kit UI.

Socle visuel commun aux projets. Copier le code, adapter le contenu, ne pas redessiner.

Démarrer

Deux feuilles à charger, dans cet ordre.

<link rel="stylesheet" href="https://kit.evan-bombart.fr/v1/tokens.css">
<link rel="stylesheet" href="https://kit.evan-bombart.fr/v1/kit-composants.css">

/v1/ suit les correctifs de la version majeure. Pour figer définitivement : /v1.0.0/, servi en immutable pendant un an.

Dans un projet Jinja2, importer les macros :

{% import "kit.html" as kit %}
{{ kit.bouton("Enregistrer", variante="primaire") }}

Le fichier gabarits/kit.html se copie dans le dossier de gabarits du projet. C'est la seule chose à recopier.

Empreintes SRI de cette version :

kit-composants.css
  sha384-3l4NiPaxLXDVIsu0ZygAzXenKLCwAzYuKjSrLRAaxwJgOiluj6H/jhZnngzugshj
polices.css
  sha384-9hK0p3jVdIYOmMupJE9Wz4WOBsKgPvvjQRug36v6vy7J1HunWjCJBK+AqUdDFbZY
tokens.css
  sha384-F9CjUslWBqVAP+uAgnb0qtOg37Lqvo/THdGD+2Kl+PctlEIv1fh41Y704Ff63vrR

Couleurs

Chaque token dit un rôle, jamais une couleur : c'est ce qui permet au thème sombre de tout basculer sans toucher au CSS. Les 24 couples texte/fond sont calculés et vérifiés par des tests dans les deux thèmes. La framboise vive ne sert jamais de texte — 4,5:1 sur papier, sous le seuil AA ; sa variante texte atteint 5,5:1.

Texte
var(--eb-texte) · contenu principal
Texte doux
var(--eb-texte-doux) · secondaire
Texte faible
var(--eb-texte-faible) · méta, légendes
Accent (texte)
var(--eb-accent) · liens, focus
Accent (aplat)
var(--eb-accent-aplat) · fonds pleins
Succès
var(--eb-succes) · confirmation
Alerte
var(--eb-alerte) · erreur
Attention
var(--eb-attention) · avertissement

Typographie

Trois voix : Space Grotesk pour les titres, Inter pour le texte, JetBrains Mono pour les détails techniques. La bascule se fait à h4 : au-delà de 26 px la Grotesk a du caractère, en dessous elle devient bavarde.

Ag

Space Grotesk

Titres, logo, accents. Géométrique, moderne, un peu de caractère.

Ag

Inter

Corps de texte, interfaces. Neutre, lisible, professionnel.

Ag

JetBrains Mono

Labels, code, méta-infos. La touche dev assumée.

Échelle responsive

Chaque rôle est un clamp() qui interpole entre 360 px et 1280 px : la taille suit la largeur sans palier, donc aucun saut au redimensionnement. Les bornes sont en rem, le zoom navigateur reste fonctionnel (WCAG 2.2 SC 1.4.4).

Taille rendue de chaque rôle typographique selon la largeur d'écran
Rôle Police MobileTabletteDesktop
display Grotesk 600 34.7px 43.8px 56.0px
h1 Grotesk 600 28.5px 35.1px 44.0px
h2 Grotesk 600 24.3px 28.4px 34.0px
h3 Grotesk 600 21.2px 23.2px 26.0px
h4 Inter 600 18.1px 18.9px 20.0px
h5 Inter 600 16.0px 16.4px 17.0px
h6 Inter 600 13.0px 13.0px 13.0px
body-xl Inter 400 18.1px 18.9px 20.0px
body-l Inter 400 17.0px 17.4px 18.0px
body Inter 400 16.0px 16.0px 16.0px
body-s Inter 400 14.0px 14.0px 14.0px
body-xs Inter 400 12.5px 12.5px 12.5px
overline Mono 500 12.0px 12.0px 12.0px
button Inter 600 14.0px 14.0px 14.0px
quote Grotesk 500 19.1px 20.3px 22.0px
code Mono 400 14.0px 14.0px 14.0px

Valeurs calculées par la même interpolation que celle qui génère le CSS (outils/fluide.py) : ce tableau ne peut pas diverger des tokens. Les lignes en gris clair sont des tailles fixes — un libellé de bouton ou une mention légale n'ont aucune raison de grandir avec l'écran.

Rendu

display Evan Bombart
h1 Titre de page
h2 Titre de section
h3 Sous-section
h4 Titre de carte
h5 Intertitre
h6 Sur-titre
body-xl Accroche de paragraphe, un peu plus grande.
body-l Introduction confortable à lire.
body Le corps par défaut — la voix de toutes mes communications.
body-s Texte secondaire, notes, légendes un peu plus denses.
body-xs Mentions, méta-infos discrètes, bas de page.
overline Étiquette · label
button Libellé de bouton
quote « Le détail fait la différence. »
code const eb = build()
<p class="eb-typo-body-l">Introduction confortable à lire.</p>
<p class="eb-typo-overline">Étiquette · label</p>

h6 et overline sont des sur-titres en majuscules, pas des titres miniatures — c'est ce qui les distingue de body-s à taille comparable.

Boutons

Une seule action primaire par vue. Deux boutons pleins côte à côte, c'est zéro action primaire.

{{ kit.bouton("Enregistrer", variante="primaire") }}
{{ kit.bouton("Annuler", variante="secondaire") }}
{{ kit.bouton("Voir plus", variante="discret") }}
{{ kit.bouton("Supprimer", variante="danger") }}
{{ kit.bouton("Indisponible", desactive=true) }}

Hauteur 44 px (WCAG 2.2 SC 2.5.8 exige 24 px, Apple HIG recommande 44 — on prend le plus exigeant). Avec href, la macro rend un <a> : un lien navigue, un bouton agit.

Cartes et étiquettes

IA / LLM Google DeepMind · 3 min

Gemini 3.7 Flash comprend les vidéos

L'analyse vidéo agentique réduit la consommation de tokens de 88 % et les coûts de 66 %, tout en améliorant l'exactitude de 7 %.
{% call kit.carte(titre="Titre de la carte", href="/detail",
                  meta=kit.etiquette("IA / LLM", ton="accent") ~ " Source · 3 min") %}
  Le corps de la carte.
{% endcall %}

Le lien porte sur le titre, pas sur la carte entière : une carte cliquable en totalité empêche de sélectionner son texte et rend le parcours au clavier illisible.

Neutre Accent Succès Alerte Attention
{{ kit.etiquette("Succès", ton="succes") }}

Formulaires

Une phrase courte, à l'infinitif.
Format d'adresse invalide.
{{ kit.champ("titre", "Titre de la tâche", requis=true,
             aide="Une phrase courte, à l'infinitif.") }}
{{ kit.champ("courriel", "Adresse e-mail", type="email",
             erreur="Format d'adresse invalide.") }}

Le <label> est obligatoire et lié par for. L'aide et l'erreur sont rattachées par aria-describedby, l'état par aria-invalid : un lecteur d'écran annonce le champ, son aide et son erreur.

États

Aucune conversation

Les modèles tournent sur le PC de jeu. Il est disponible.

Nouvelle conversation
{{ kit.vide(titre="Aucune conversation",
            texte="Les modèles tournent sur le PC de jeu.",
            action_libelle="Nouvelle conversation",
            action_href="/nouvelle") }}

L'action est dans l'état vide. C'est le correctif d'un défaut réel : une page affichait « réveille-le depuis le panneau de gauche » au centre, avec le bouton ailleurs. Le regard faisait un aller-retour pour rien.

Terminé
Digest généré à 05h03.
Le PC de jeu est occupé : une partie est en cours.
{{ kit.alerte("Le PC de jeu est occupé.", ton="attention") }}

Seul le ton erreur porte role="alert" : ce rôle interrompt le lecteur d'écran, l'employer pour une confirmation banale est du bruit.

Attente et mouvement

Nielsen : rien sous 1 s, indicateur entre 1 et 10 s, progression chiffrée au-delà. Un indicateur affiché immédiatement produit un clignotement qui donne une impression de lenteur alors que la réponse était rapide.

Génération en cours…

Chargement du contenu…
{{ kit.chargement("Génération en cours…") }}
{{ kit.squelette(lignes=3) }}

Le squelette est préférable au cercle quand la forme du contenu est connue : l'utilisateur comprend ce qui arrive. Toutes les animations sont neutralisées par prefers-reduced-motion: reduce.

Supprimer cette conversation ?

Cette action est définitive.

{% call kit.dialogue("mon-id", "Supprimer ?") %}
  

Cette action est définitive.

{% endcall %}

<dialog> natif (96,11 % de support) : piège de focus, touche Échap et inertie de l'arrière-plan gérés par le navigateur — trois choses qu'une modale maison rate presque toujours.