Evan Bombart · v1.0.0
Socle visuel commun aux projets. Copier le code, adapter le contenu, ne pas redessiner.
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
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.
var(--eb-texte) · contenu principal
var(--eb-texte-doux) · secondaire
var(--eb-texte-faible) · méta, légendes
var(--eb-accent) · liens, focus
var(--eb-accent-aplat) · fonds pleins
var(--eb-succes) · confirmation
var(--eb-alerte) · erreur
var(--eb-attention) · avertissement
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.
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).
| Rôle | Police | Mobile | Tablette | Desktop |
|---|---|---|---|---|
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.
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
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.
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.
{% 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.
{{ kit.etiquette("Succès", ton="succes") }}
{{ 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.
{{ 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.
{{ 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.
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.
{% 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.