Variable Font brutaliste et interactive, sans SVG
Pré-requis : Cette expérience repose entièrement sur le
système de matrice 4×4.
Si tu n'as pas lu l'article fondateur, les coordonnées --A-x1 à --D-x4
et le principe du clip-path te sembleront incompréhensibles.
Cette page pousse le concept un cran plus loin : au lieu de dessiner des lettres
à épaisseur fixe, nous allons animer la matrice elle-même.
Une seule propriété --offset contrôle l'épaisseur du trait,
et le navigateur recalcule toute la géométrie des 256 points d'ancrage
(16 repères sur l'axe X × 16 repères sur l'axe Y). Et ce, à 60 images par seconde s'il te plaît.
@property --offset
Habituellement, pour animer l'épaisseur d'une typographie, deux solutions existent :
stroke-width
ou les points du path. Mais le SVG est statique : une fois chargé,
sa géométrie est figée. Sans JS, pas d'animation.Notre ambition est différente : aucun fichier externe, aucun JS, juste du CSS et des maths. La forme n'est pas dessinée, elle est calculée. Pour de l'ornemental, cela pourrait le faire non ?
@property --offsetDans la page matrice, --offset est une valeur
fixe : c'est l'espace entre le bord extérieur de la cellule et le début du carré intérieur.
Cet espace, multiplié par 2, donne l'épaisseur du trait.
En CSS classique, les variables sont des tokens de texte.
Le navigateur ne sait pas interpoler 12px vers 20px pendant une transition.
La propriété @property (
CSS Houdini
) permet d'enregistrer --offset
auprès du navigateur avec un type length :
syntax: '<length>'inherits: true::after héritent de la valeur.
Quand --offset change, les 256 points d'ancrage
de la matrice recalculent.initial-value: 12pxUne fois enregistrée, --offset devient une propriété modifiable :
Le navigateur ne fait pas un fondu. À chaque frame de la transition, il recalcule toutes les coordonnées de la matrice :
La variable --offset est le pivot central.
Elle contrôle le gap (espacement entre cellules), l'épaisseur du trait
(x2, y2, x3, y3), et indirectement la taille des cellules.
C'est du morphing géométrique natif, sans canvas, sans SVG, sans JS.
Passe ta souris sur chaque lettre. La propriété --offset passe de 12px à 20px,
et toute la matrice se recalcule. Note l'ombre filter: drop-shadow
qui suit fidèlement la forme découpée — c'est normal puisque le navigateur
recalcule toutes les coordonnées à chaque frame :
La vraie force du système 4×4 apparaît sur les lettres complexes :
Le N utilise une diagonale qui relie le coin intérieur du fût gauche
au coin intérieur du fût droit. Quand --offset augmente, la diagonale
s'épaissit proportionnellement sans jamais casser l'angle.
Chaque lettre n'utilise qu'une fraction des 256 points d'ancrage, mais elle démontre une construction géométrique unique. Voici le catalogue :
A-x1, A-x3, D-x4, R1-y1, R4-y3, R4-y4A-x2, A-x3, R1-y1, R4-y4A-x1, B-x2, B-x3, D-x4, R1-y1, R1-y2, R4-y4A-x1, A-x3, D-x4, R1-y1, R1-y2, R4-y3, R4-y4A-x1, A-x3, D-x2, D-x4, R1-y1, R1-y3, R4-y2, R4-y4D-x2 R1-y3 et A-x3 R4-y2 créent
une diagonale qui traverse 3 colonnes et 3 lignes.A-x1, A-x3, D-x2, D-x4, R1-y1, R2-y2, R3-y3, R4-y4A-x1, A-x3, B-x4, C-x1, D-x2, D-x4, R1-y1, R2-y1, R2-y2, R3-y2, R4-y1, R4-y4B-x4 R4-y1 et C-x1 R4-y1 créent
un pied central large, tandis que les pentes montent vers A-x3 R2-y1
et D-x2 R2-y1.A-x1, A-x3, D-x2, D-x4, B-x2, B-x3, C-x2, C-x3, R1-y1, R1-y3, R2-y2, R2-y3, R3-y2, R3-y3, R4-y2, R4-y4A-x1, A-x2, B-x2, C-x3, D-x3, D-x4, R1-y1, R2-y2, R2-y3, R3-y3, R4-y4B-x2 C-x3.
La fente descend verticalement depuis le sommet intérieur.La règle qui unifie tout : le nombre de points n'a aucune importance. Chaque point s'accroche sur un repère de la matrice (x1, x2, x3, x4 × y1, y2, y3, y4). La seule contrainte à respecter pour les lettres avec trou est la fente.
ATTENTION : On parle d'animation par modification de--offset. On ne parle pas de transition vers une seconde lettre.Découper un "L" avec un polygone est trivial. Mais comment percer le trou central d'un "O" ou les deux trous d'un "B" sans que le polygone ne s'effondre ?
Dans ma première expérimentation, j'utilisais la fente :
tracer le contour extérieur dans un sens, créer une entaille invisible vers
l'intérieur, tracer le trou dans le sens inverse, ressortir
par la même fente. C'est la Non-Zero Winding Rule.
La fente est invisible car elle est parcourue deux fois dans des sens
opposés : le navigateur calcule "1 − 1 = 0" et ne remplit pas cette ligne.
Pourquoi ne pas utiliser evenodd qui semble plus simple ?
Le problème : polygon() n'a pas de
commande move-to comme en SVG. Le navigateur trace une
ligne de connexion entre la fin du contour extérieur et le
début du trou. Cette ligne traverse la matière de la lettre et crée un
angle visible qui la casse.
La fente (Non-Zero Winding) reste donc la seule solution propre avec
clip-path: polygon() : l'entrée et la sortie du trou passent
par le même chemin, en sens opposé. Le navigateur les annule mathématiquement.
On a testé avec des lettres toutes moches, maintenant on va essayer avec des lettres plus élaborés dans leurs découpages, pour voir...
::before,
pas de JS, juste des maths.Le LAB a un concept sacralisé : interdit d'utiliser JavaScript.
Comment faire un bouton "Activer le mode Ultra Black" sans addEventListener ?
La réponse est le Checkbox Hack, une technique CSS classique qui exploite le comportement natif du HTML :
Quand on clique sur le <label>, le navigateur coche
automatiquement l'<input> grâce à l'attribut for.
Le CSS utilise alors le sélecteur de voisinage ~ :
Le texte du bouton change aussi sans JS : deux <span> superposés
("Activer" / "Désactiver"). On utilise la classe .sr-only plutôt que
display: none pour que les lecteurs d'écran n'annoncent qu'un seul texte à la fois.
:checked ~ s'active.
Toutes les lettres passent de --offset: 12px à --offset: 21px
en 0.4s. 21px est la limite physique de la grille avant que les trous
ne s'effondrent.La matière de la lettre (couleur, texture) est totalement indépendante de sa forme.
Le clip-path découpe, le background remplit.
On peut donc superposer n'importe quel style :
Même principe que sur la page matrice :
la forme reste intacte, seule la matière change. C'est le principe qui consiste à séparer
le masque (clip-path) du remplissage (background).
Le système respecte les préférences de l'utilisateurice :
Si l'utilisateurice demande moins de mouvement, les transitions disparaissent instantanément. Les lettres changent toujours de graisse, mais sans animation. Le site reste pleinement fonctionnel.
prefers-reduced-motionprefers-contrast: morestyle.css. Le texte s'agrandit à 120%.--offset reste en px, donc l'épaisseur
relative augmente : les lettres deviennent plus massives sur mobile.Comme [data-matrice-char] est un élément HTML natif, il est reconnu par les technologies d'assistance.
Il est donc possible de naviguer et d'interagir avec ces éléments de manière accessible.
data-matrice-char="M" class="accent texture" role="img" aria-label="Lettre M"
J'ai ajouté un role d'img et une étiquette ARIA pour que les lecteurs d'écran annoncent la lettre correctement.
Après, c'est ton choix, si tu décides de mettre des points pour faire un coeur, c'est toi qui vois.
Là il est décidé d'en faire un ornement avec un role image.
Bravo d'être arrivé jusqu'ici !<
cadeau : La lettre A