frontend / css
CSS Houdini : Paint API et Properties API
Explication
Ce que vous allez apprendre
- Comprendre le principe de Houdini : ouvrir le moteur de rendu CSS à du code JavaScript
- Générer un motif de fond procédural avec le Paint API (
registerPaint) - Typer une custom property en JavaScript avec
CSS.registerProperty - Prévoir un repli visuel simple pour les navigateurs sans support de Houdini
Dans quel contexte ?
Un site vitrine haut de gamme veut un fond décoratif fait de points générés dynamiquement, dont la taille et la couleur réagissent à des variables CSS modifiables par l'utilisateur (un sélecteur de thème). Utiliser une image statique obligerait à générer une image différente par combinaison de réglages ; le Paint API de Houdini permet de dessiner ce motif directement au moment du rendu, à partir de code JavaScript.
Étape 1 : des effets historiquement impossibles
Certains effets visuels complexes, comme des motifs de fond générés algorithmiquement, étaient traditionnellement impossibles en CSS pur, forçant à recourir à des images statiques.
Étape 2 : l'approche radicale de Houdini
Houdini prend une approche différente : au lieu de contourner les limites du moteur de rendu CSS, il ouvre ce moteur lui-même à du code JavaScript personnalisé, exécuté pendant le rendu.
Étape 3 : le Paint API, dessiner par du code
Voyons d'abord le Paint API. Son idée centrale est de remplacer une image de fond fixe par une fonction qui la GÉNÈRE à chaque fois, avec un contexte de dessin proche de celui du Canvas.
Étape 4 : pourquoi ce motif reste réactif
L'avantage majeur : ce motif généré réagit automatiquement aux variables CSS déclarées dans inputProperties — changer une couleur redéclenche le dessin, sans JavaScript supplémentaire.
Étape 5 : un lien avec une notion déjà vue
Voici un pont utile : la Properties and Values API (CSS.registerProperty) est littéralement l'équivalent JavaScript de la règle @property du CSS moderne. Les deux typent une variable pour la rendre animable.
| API Houdini | Rôle |
|---|---|
Paint API (registerPaint) | générer une image de fond/bordure procédurale |
| Properties and Values API | typer une custom property (interpolable, validée) |
| Typed OM | manipuler les valeurs CSS en JS comme des objets typés, pas des chaînes |
Étape 6 : rester prudent en pratique
Il reste un point important : le support navigateur de Houdini reste très inégal, la Paint API fonctionnant surtout sur les navigateurs basés sur Chromium.
Prérequis
Toujours tester if ("paintWorklet" in CSS) avant d'enregistrer un worklet, et prévoir un background-image de repli classique dans le CSS pour les navigateurs qui ne supportent pas Houdini. Sans ce filet de sécurité, l'élément se retrouve simplement sans fond du tout.
Et ensuite ?
Toujours prévoir un repli visuel simple pour les navigateurs qui ne la supportent pas. Une fois Houdini vu, la prochaine étape aborde un sujet essentiel pour l'international : les propriétés logiques.
Commandes & code
CSS Houdini : Paint API et Properties API
Étendre le moteur de rendu CSS lui-même, en JavaScript, plutôt que le contourner avec des hacks.
// Paint Worklet : dessine un fond personnalisé, généré par du code, pas une image statique
// Fichier séparé : points-worklet.js
class PointsWorklet {
static get inputProperties() {
return ["--taille-point", "--couleur-point"];
// propriétés CSS lues à chaque repaint -- réactives comme n'importe quelle variable
}
paint(ctx, taille, proprietes) {
const rayon = parseFloat(proprietes.get("--taille-point")) || 4;
const couleur = proprietes.get("--couleur-point").toString().trim() || "black";
ctx.fillStyle = couleur;
for (let x = 0; x < taille.width; x += rayon * 4) {
for (let y = 0; y < taille.height; y += rayon * 4) {
ctx.beginPath();
ctx.arc(x, y, rayon, 0, Math.PI * 2);
ctx.fill();
}
}
}
}
registerPaint("points", PointsWorklet);// Enregistrement du worklet depuis le script principal de la page
if ("paintWorklet" in CSS) {
CSS.paintWorklet.addModule("/js/points-worklet.js");
}/* Utilisation : comme n'importe quelle image de fond */
.fond-motif {
--taille-point: 3px;
--couleur-point: #2563eb;
background-image: paint(points);
/* recalculé automatiquement si --taille-point ou --couleur-point changent */
}// Properties and Values API : typer une custom property (base de @property en CSS)
// Équivalent JS de la règle @property vue dans la leçon "CSS moderne expert"
if ("registerProperty" in CSS) {
CSS.registerProperty({
name: "--angle-degrade",
syntax: "<angle>",
inherits: false,
initialValue: "0deg",
});
}/* Une fois typée, la propriété devient interpolable -> animable en transition */
.degrade-anime {
background: linear-gradient(var(--angle-degrade), #2563eb, #22c55e);
transition: --angle-degrade 1s linear;
}
.degrade-anime:hover {
--angle-degrade: 360deg;
}| API Houdini | Rôle |
|---|---|
Paint API (registerPaint) | générer une image de fond/bordure procédurale |
| Properties and Values API | typer une custom property (interpolable, validée) |
| Layout API | définir un mode de layout personnalisé (support navigateur très limité) |
| Typed OM | manipuler les valeurs CSS en JS comme des objets typés, pas des chaînes |
Résumé
- Le Paint API dessine un
background-image/border-imageprocédural via un<canvas>virtuel dans un worklet. inputPropertiesdéclare les custom properties suivies : le repaint se redéclenche automatiquement si elles changent.CSS.registerPropertyest l'équivalent JS de@property: cela rend une variable typée et animable.- Le support navigateur de Houdini reste partiel (Paint API surtout sur Chromium) : prévoir un repli visuel simple.
Exercices pratiques
Mission : un motif de points qui disparaît sur certains navigateurs
Objectif : Sécuriser un usage du Paint API avec un repli de secours et diagnostiquer une variable non réactive.
Contexte
Sur landing.css, .fond-motif utilise uniquement background-image: paint(points); sans aucun background-image ni background-color de repli, et sans jamais tester "paintWorklet" in CSS avant d'appeler CSS.paintWorklet.addModule(...). Sur un navigateur qui ne supporte pas Houdini, la section apparaît complètement vide, sans fond du tout. Par ailleurs, le worklet PointsWorklet déclare static get inputProperties() { return ["--taille-point"]; } (en oubliant --couleur-point), et changer --couleur-point en JavaScript ne redessine jamais le motif.