Chapitre 65 : Utilisation de @property pour le typage des variables
Définition de types, valeurs par défaut et héritage des propriétés personnalisées
Pendant des années, les variables CSS (Custom Properties) ont été traitées par les navigateurs comme de simples chaînes de caractères. Si vous assigniez --my-color: red et que vous tentiez de l'animer vers blue, le navigateur ne savait pas comment "interpolar" entre les deux couleurs, car pour lui, il s'agissait simplement de passer du mot "red" au mot "blue".
L'introduction de la règle @property (issue de l'API CSS Properties and Values) change radicalement la donne en permettant de typer explicitement ces variables.
Le Typage des Custom Properties
1. Quoi
La règle @property est une directive CSS qui permet d'enregistrer une propriété personnalisée avec des métadonnées spécifiques. Elle permet de définir trois paramètres critiques :
syntax: Le type de donnée attendu (ex:<color>,<length>,<percentage>,<number>, ou<custom-ident>).inherits: Un booléen indiquant si la propriété doit être héritée par les éléments enfants.initial-value: La valeur de repli si aucune valeur n'est définie.
Contrairement aux variables classiques déclarées dans :root, @property transforme une variable "aveugle" en une propriété consciente de son type.
2. Pourquoi
Dans un contexte de design system senior, le typage apporte trois avantages majeurs :
- Animations et Transitions fluides : C'est l'avantage principal. En sachant qu'une variable est de type
<color>, le navigateur peut calculer les étapes intermédiaires (interpolation) pour créer un dégradé fluide lors d'une transition, ce qui était impossible auparavant. - Robustesse du Design System : En définissant une
initial-value, vous garantissez que vos composants ne "cassent" pas visuellement si un développeur oublie de passer une variable ou passe une valeur invalide. - Contrôle de l'héritage : Vous pouvez désormais empêcher une variable de se propager dans tout l'arbre DOM, limitant ainsi les effets de bord et optimisant légèrement les performances de recalcul des styles.
3. Comment
A. Syntaxe de base
L'enregistrement se fait généralement au sommet du fichier CSS.
@property --main-bg-color {
syntax: '<color>';
inherits: false;
initial-value: #ffffff;
}
B. Cas concret : Animation de dégradés complexes
L'un des cas d'usage les plus puissants est l'animation de linear-gradient. Traditionnellement, on ne peut pas animer les couleurs d'un dégradé car le navigateur traite le linear-gradient() comme une image (image type), et non comme une couleur.
Voici comment résoudre ce problème avec @property :
/* 1. On définit les variables de couleur comme étant des types <color> */
@property --gradient-start {
syntax: '<color>';
inherits: false;
initial-value: #ff0000;
}
@property --gradient-end {
syntax: '<color>';
inherits: false;
initial-value: #0000ff;
}
.animated-card {
width: 300px;
height: 200px;
border-radius: 12px;
/* Utilisation des variables dans le dégradé */
background: linear-gradient(45deg, var(--gradient-start), var(--gradient-end));
/* On peut maintenant animer les variables elles-mêmes ! */
transition: --gradient-start 0.5s ease, --gradient-end 0.5s ease;
cursor: pointer;
}
.animated-card:hover {
/* Le navigateur interpole maintenant les couleurs car il connaît leur type */
--gradient-start: #00ff00;
--gradient-end: #ffff00;
}
C. Limitations
- Support Navigateur : Bien que supporté par Chrome, Edge et Safari, le support global (notamment Firefox) a été plus lent à arriver. Pour des projets critiques, un fallback est nécessaire.
- Performance : L'utilisation massive de
@propertysur des milliers d'éléments peut augmenter la charge de calcul lors des transitions, bien que cela reste marginal par rapport aux bénéfices. - Syntaxe stricte : Si vous assignez une valeur qui ne correspond pas à la
syntaxdéfinie (ex:10pxpour un type<color>), la variable sera ignorée et reviendra à sainitial-value.
4. Zone de Danger
❌ L'erreur classique : Confondre déclaration et enregistrement
Déclarer --color: red; dans :root ne type pas la variable. Cela assigne simplement une valeur. Sans @property, le navigateur voit --color comme une chaîne de caractères.
✅ La bonne pratique : Enregistrer puis assigner
Utilisez @property pour définir le "contrat" (le type) et utilisez :root ou les sélecteurs de composants pour assigner les valeurs spécifiques aux thèmes.
Différence de traitement : Variable Standard vs @property
Questions clés
1. Quelle est la différence fondamentale entre une variable CSS classique et une variable définie via @property ?
Découvrir la réponse
Une variable classique est traitée comme un token textuel (string). Le navigateur ne comprend pas sa nature sémantique. Une variable @property est typée (ex: <number>, <color>), ce qui permet au moteur de rendu d'effectuer des opérations mathématiques d'interpolation, rendant ainsi les transitions et animations possibles.
2. Que se passe-t-il si j'assigne une valeur invalide à une variable typée ?
Découvrir la réponse
Si la valeur assignée ne correspond pas à la syntax définie dans @property, le navigateur rejette la valeur et utilise automatiquement la initial-value spécifiée lors de l'enregistrement. C'est un mécanisme de sécurité puissant pour éviter les bugs visuels.
3. Pourquoi utiliser inherits: false ?
Découvrir la réponse
Par défaut, les custom properties sont héritées. En mettant inherits: false, on empêche la variable de descendre dans l'arbre DOM. Cela est utile pour les variables de composants internes (ex: un bouton) afin d'éviter que des styles globaux ne polluent accidentellement des sous-éléments ou pour optimiser les performances de recalcul du style.
4. Peut-on utiliser @property pour animer des nombres simples ?
Découvrir la réponse
Oui, en utilisant la syntaxe <number> ou <percentage>. Cela permet d'animer des propriétés qui ne sont normalement pas animables, comme le pourcentage d'un masque (mask-image) ou des valeurs utilisées dans des calculs calc().
Mise en pratique
Exercice 1 : Reproduction guidée
Objectif : Créer un cercle dont la couleur de bordure change progressivement au survol en utilisant @property.
- Enregistrez une propriété
--border-colorde type<color>. - Appliquez-la à un élément
divcirculaire. - Ajoutez une transition de 0.3s sur cette variable.
Découvrir la solution commentée
/* Enregistrement du type pour permettre l'interpolation */
@property --border-color {
syntax: '<color>';
inherits: false;
initial-value: #3498db;
}
.circle {
width: 100px;
height: 100px;
border-radius: 50%;
border: 5px solid var(--border-color);
/* On cible explicitement la variable dans la transition */
transition: --border-color 0.3s ease;
cursor: pointer;
}
.circle:hover {
--border-color: #e74c3c;
}
Exercice 2 : Adaptation
Objectif : Créer un système de "Progress Ring" où l'épaisseur du trait est animée.
- Utilisez
@propertypour définir--ring-thicknessde type<length>. - Le cercle doit passer d'une bordure de
2pxà8pxau survol. - Assurez-vous que la valeur initiale est de
2px. - Utilisez une courbe
cubic-bezierpour donner un effet élastique à l'animation.
La fonction cubic-bezier(x1, y1, x2, y2) définit une courbe de Bézier cubique pour contrôler la vitesse d'une animation. Elle prend 4 valeurs qui représentent les coordonnées de deux points de contrôle :
cubic-bezier(0.4, 0, 0.2, 1)→ courbe "Material Design" (accélération rapide, freinage doux)cubic-bezier(0, 0, 1, 1)→ identique àlinearcubic-bezier(0.4, 0, 1, 1)→ identique àease-incubic-bezier(0, 0, 0.2, 1)→ identique àease-out
💡 Vous pouvez visualiser et générer vos propres courbes sur cubic-bezier.com ou directement dans les DevTools Chrome/Firefox (cliquez sur l'icône de courbe à côté de cubic-bezier).
Découvrir la solution commentée
@property --ring-thickness {
syntax: '<length>';
inherits: false;
initial-value: 2px;
}
.progress-ring {
width: 150px;
height: 150px;
border-radius: 50%;
border: var(--ring-thickness) solid #2ecc71;
/* cubic-bezier(0.4, 0, 0.2, 1) : accélération rapide puis freinage progressif */
transition: --ring-thickness 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.progress-ring:hover {
--ring-thickness: 8px;
}
Exercice 3 : Conception (Cas métier)
Objectif : Implémenter un "Glassmorphism Card" dont l'intensité du flou et la couleur de l'éclat varient selon un état "actif".
- Créez une variable
--glow-color(<color>) et--blur-amount(<length>). - La carte doit avoir un
backdrop-filter: blur(var(--blur-amount))et unebox-shadowutilisant--glow-color. - Au survol, le flou doit augmenter et la couleur doit passer d'un bleu discret à un violet vif.
La propriété backdrop-filter applique des effets graphiques (flou, luminosité, contraste…) sur la zone derrière l'élément, et non sur l'élément lui-même. C'est la propriété qui donne l'effet "verre dépoli" (glassmorphism).
/* Flou sur le fond derrière l'élément */
backdrop-filter: blur(10px);
/* Autres fonctions disponibles */
backdrop-filter: brightness(0.8) contrast(1.2) saturate(1.5);
Prérequis important : Pour que backdrop-filter soit visible, l'élément doit avoir un fond semi-transparent (ex: background: rgba(255,255,255,0.1)). S'il est totalement opaque, le flou ne sera pas perceptible.
Support navigateur : Supporté dans tous les navigateurs modernes. Sur Safari, un préfixe -webkit-backdrop-filter peut être nécessaire pour les versions plus anciennes.
Découvrir la solution commentée
@property --glow-color {
syntax: '<color>';
inherits: false;
initial-value: rgba(0, 123, 255, 0.3);
}
@property --blur-amount {
syntax: '<length>';
inherits: false;
initial-value: 4px;
}
.glass-card {
width: 300px;
padding: 2rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 16px;
/* Application des variables typées */
backdrop-filter: blur(var(--blur-amount));
box-shadow: 0 8px 32px var(--glow-color);
/* Transition groupée */
transition:
--glow-color 0.6s ease,
--blur-amount 0.6s ease;
color: white;
font-family: sans-serif;
}
.glass-card:hover {
--glow-color: rgba(147, 51, 234, 0.6);
--blur-amount: 12px;
}