Résumé des résultats, concepts utilisés et limites de notre typographie réactive sans SVG ni JS.
Ce bilan clôt le cycle d'expérimentations autour de la typographie géométrique en CSS pur.
Le défit était de créer des animations typographiques sans SVG, sans JavaScript,
sans fichier de police externe en s'appuyant sur l'
expérimentation d'Nguyen Grobber et Armin Hofmann
: seulement des variables CSS, des clip-path et des maths.
Voici ce qui a fonctionné, ce qui a cassé, et où ça peut encore aller,
Au delà de mes limites de connaissances actuelles.
L'idée de base : Utiliser un attribut HTML
(data-matrice-char="X") comme seule source de vérité,
et laisser le CSS décider de la forme géométrique à afficher
via des sélecteurs conditionnels.
Résultat : Un HTML parfaitement propre et sémantique.
Le navigateur lit la donnée brute et le CSS applique le clip-path
correspondant. Cela sépare strictement le fond (l'identité de la lettre)
de la forme (les coordonnées).
L'idée : Remplacer le dessin vectoriel par une équation.
Nous avons construit une grille de carrés imbriqués générant 256 points d'ancrage
relatifs (les variables --A-x1 à --D-x4
et --R1-y1 à --R4-y4).
Résultat : Une cohérence visuelle interprétable. Chaque trait,
chaque biseau est dicté par une seule variable maîtresse : --offset.
Modifier cette variable recalcule instantanément l'intégralité de la lettre.
L'idée : Percer des trous dans des polygones
(pour le "O", le "A", le "B") sans utiliser de calques superposés
(::before/::after),
afin de conserver l'interaction avec filter: drop-shadow().
Résultat : En traçant le contour extérieur dans le sens horaire, en créant une "fente" invisible vers le centre, puis en traçant le trou dans le sens anti-horaire, le navigateur évide la zone (1 - 1 = 0). L'ombre portée reste dure et respecte la transparence intérieure de la lettre.
L'idée : Abandonner les Media Queries. La lettre doit connaître sa propre taille pour éviter que son épaisseur ne détruise sa géométrie sur les petits écrans.
Résultat : En déclarant container-type: inline-size
sur la lettre, nous avons pu utiliser l'unité cqi
(Container Query Inline) couplée à la fonction mathématique clamp().
La lettre gère sa propre graisse proportionnellement à l'espace dont elle dispose.
L'idée : Animer la graisse de la police (le fameux
--offset) de manière fluide au survol ou au clic, sans Javascript.
Résultat : Une petite réussite personnelle. En utilisant
@property --offset { syntax: '<length>'; inherits: true; initial-value: 12px; },
le navigateur a compris que cette chaîne de texte était une mesure physique.
Le CSS peut désormais interpoler nativement le passage de 12px à 20px,
recalculant toute la grille en temps réel.
Note : La valeur inherits: true est critique.
Sans elle, les pseudo-éléments ::after ne reçoivent pas la mise à jour
et la transition casse silencieusement.
La découverte : Au fil des expérimentations, deux approches architecturales ont émergé pour déclarer la forme d'un glyphe.
Approche A : la variable intermédiaire --glyph.
Le clip-path complet est stocké dans une custom property,
puis assigné à ::after. Le navigateur met en cache la valeur
de la variable. La surcharge par sélecteur plus spécifique ou par classe
devient plus évidente.
Approche B : le polygone direct sur ::after.
Les coordonnées sont écrites directement sur le pseudo-élément. Plus immédiat,
mais chaque surcharge oblige à réécrire l'intégralité du polygone.
Conséquence : Ce n'est pas qu'une préférence de style. L'approche A exploite le mécanisme de cascade et d'héritage du navigateur comme une couche de mémoire : on déclare une intention, le moteur de rendu résout la valeur finale. Ce principe est exportable à d'autres contextes bien au-delà de la typographie.
clamp(),
il existe un point de rupture mathématique. Si la lettre est contenue dans une
boîte de 40px et que l'on force un --offset (graisse) de 20px,
l'espace vide (le gap) devient négatif. Les axes X et Y se croisent de manière
chaotique. Le trou du "O" s'inverse et la forme implose.M
(déplacer le stylo sans dessiner), clip-path: polygon() trace une
ligne continue. C'est ce qui nous oblige à utiliser le hack complexe de
"La Fente". Coder la lettre "B" a nécessité 26 points de coordonnées et un
double aller-retour mental pour évider les deux trous sans casser la forme.
C'est inmaintenable à l'échelle d'une police complète sans générateur.:checked ~) pour stocker l'état
(ex: Mode Light/Dark) fonctionne parfaitement, mais reste confiné au parent
immédiat. Sans JS, on ne peut pas propager un état d'une case à cocher vers
un élément qui se trouve dans une autre branche du DOM (hors fratrie).steps() et OKLCH :
Dans l'expérimentation des ondes chromatiques (animations.css),
on anime la teinte d'une couleur oklch() en faisant varier un
angle de teinte stocké dans une custom property --dynamic-h.
L'idée : changer la couleur des lettres par saccades (steps(15))
pour un effet d'onde mécanique sur le mot COMBAT.
Le problème : animer --dynamic-h sur un élément qui combine
clip-path (sur ::after) et
filter: drop-shadow (sur le parent) provoque une corruption visuelle.
Le navigateur échoue à synchroniser le recalcul de la couleur avec la découpe
du polygone lors d'un repaint brutal imposé par steps().
Les coordonnées de la matrice 4x4 sont perdues, les tracés avec fente
(lettres O, A, B) implosent, l'ombre se détache de la forme.
Cause probable : --dynamic-h n'est pas typée via
@property. Le navigateur la traite donc comme un token de texte
opaque et invalide l'intégralité de l'arbre de styles à chaque changement,
au lieu d'isoler uniquement le recalcul de couleur. La même technique que
pour --offset (point 5) devrait s'appliquer : déclarer
@property --dynamic-h { syntax: '<number>'; inherits: true; initial-value: 330; }
pour que le navigateur sache exactement ce qui change et limite la propagation
de l'invalidation. À tester.
Issue #1Ces pistes n'ont pas encore été testées, mais le système de matrice pose les bases nécessaires pour les attaquer.
Puisque la lettre est découpée dans un polygone calculé, il est possible d'empiler
plusieurs couches (via des ombres portées multiples modifiées ou des pseudo-éléments
empilés avec un décalage calculé via calc()) pour créer de la fausse 3D
réactive qui réagit à la position du curseur. (on peut respirer... la phrase est longue :))
Plutôt que d'animer --offset proprement, on pourrait appliquer
des animations @keyframes distinctes et saccadées sur des variables
spécifiques (ex: animer uniquement --A-x2). La lettre se déformerait,
s'étirerait mécaniquement.
En utilisant les checkbox comme bits de mémoire (0 ou 1) et le
clip-path comme Hitbox physique (si je survole le noir = Game Over),
nous aurions de quoi construire de vrais puzzles logiques sans une seule ligne de JS.
La grille 4x4 donne 256 points d'ancrage. Une grille 8x8 en donnerait 1024,
une 16x16 plus de 4000. Plus la résolution augmente, plus les formes peuvent être
complexes : courbes approximées par segments courts, diagonales plus fines,
détails sub-pixel. Le coût : une explosion du nombre de variables CSS et un recalcul
plus lourd à chaque changement d'--offset. Un temps de positionnement
des points plus long.
À partir de quelle résolution le navigateur sature-t-il ? Il est probable que les
navigateurs optimisent les changements mineurs en ne recalculant que les points affectés,
mais cela reste à mesurer.
Le principe n'est pas limité aux lettres. N'importe quelle forme géométrique
paramétrique peut être construite avec cette approche : icônes réactives, interfaces
de données (sparklines CSS, jauges polygonales), masques de mise en page qui
s'adaptent au contenu. Le clip-path comme outil de layout,pas
seulement de décoration.
Coder la lettre B à la main en 26 points est inmaintenable. La prochaine étape logique est un outil qui prend une forme dessinée et génère automatiquement les coordonnées de la matrice en CSS : variables, fentes, respect de la Non-Zero Winding Rule. Le CSS comme format de sortie d'un générateur, pas comme code écrit à la main.
Le CSS n'est pas qu'un langage de mise en forme. C'est un langage déclaratif : logique, calcul, propagation d'état. Ce cycle d'expérimentations en est la preuve. La suite au prochain épisode. (je suis fatigué là :D)