Cours JavaScript et React · Module 14: Styles avancés
CSS-in-JS - Systèmes de navigation avancés
Dans cette leçon5
Styled-components n'est pas la seule bibliothèque CSS-in-JS. Il en existe d'autres, chacune avec des avantages uniques. Explorons deux des plus importantes : Emotion et vanilla-extract.
Emotion
Emotion est une bibliothèque CSS-in-JS qui propose deux façons de l'utiliser :
Object API (@emotion/react)
1/** @jsxImportSource @emotion/react */
2import { css } from '@emotion/react';
3
4function SpacePanel({ isActive }) {
5 return (
6 <div css={css`
7 background: #0d1137;
8 border: 2px solid ${isActive ? '#00e676' : '#3949ab'};
9 border-radius: 12px;
10 padding: 20px;
11 transition: border-color 0.3s ease;
12 `}>
13 <h2>Navigation Panel</h2>
14 </div>
15 );
16}Styled API (@emotion/styled)
1import styled from '@emotion/styled';
2
3const Card = styled.div`
4 background: #1a1a4e;
5 padding: 20px;
6 border-radius: 12px;
7`;
8
9// Ou la syntaxe objet :
10const CardObj = styled.div({
11 background: '#1a1a4e',
12 padding: 20,
13 borderRadius: 12,
14});Avantages d'Emotion
- Deux API -- tu peux utiliser des template literals ou des objets JS
- Léger -- bundle plus petit que styled-components
- Hautes performances -- runtime optimisé
- css prop -- tu peux ajouter des styles directement via le prop
css
Vanilla-extract -- CSS-in-JS zero-runtime
Vanilla-extract adopte une approche complètement différente : les styles sont générés au moment de la compilation, et non à l'exécution. Zéro JavaScript dans le bundle client !
1// styles.css.ts
2import { style, globalStyle } from '@vanilla-extract/css';
3
4export const panel = style({
5 background: '#0d1137',
6 border: '2px solid #3949ab',
7 borderRadius: 12,
8 padding: 20,
9 ':hover': {
10 borderColor: '#7c4dff',
11 },
12});
13
14export const title = style({
15 color: '#7c4dff',
16 fontSize: 24,
17});1// Component.tsx
2import { panel, title } from './styles.css.ts';
3
4function SpacePanel() {
5 return (
6 <div className={panel}>
7 <h2 className={title}>Panel</h2>
8 </div>
9 );
10}Avantages de vanilla-extract
- Zéro runtime -- aucun JS supplémentaire dans le navigateur
- TypeScript-first -- typage complet, autocomplétion
- Type-safe -- erreur de compilation lors de l'utilisation d'un style inexistant
- Theming -- système de thèmes intégré avec des variables CSS
Comparaison des CSS-in-JS
| Caractéristique | Styled-components | Emotion | Vanilla-extract |
|---|---|---|---|
| Runtime | Oui | Oui | Non (zero-runtime) |
| Bundle | Plus grand | Moyen | Minimal |
| TypeScript | Bon support | Bon support | TS natif |
| SSR | Nécessite une configuration | Nécessite une configuration | Sans accroc |
| DX | Très bonne | Très bonne | Nécessite un build |
Quand utiliser quelle bibliothèque ?
Le choix de la bibliothèque CSS-in-JS dépend de plusieurs facteurs :
Choisis styled-components quand :
- Ton équipe connaît déjà cette bibliothèque -- c'est la plus populaire et celle qui a la plus grande communauté
- Tu as besoin d'un riche écosystème de plugins et d'outils
- Tu te soucies de la lisibilité du code -- les tagged template literals sont intuitifs pour les développeurs qui connaissent le CSS
Choisis Emotion quand :
- Tu veux de la flexibilité -- tu peux utiliser à la fois des template literals et des objets JS
- Tu te soucies d'une taille de bundle plus petite
- Tu utilises Next.js ou un autre framework où Emotion a une meilleure intégration
- Tu as besoin du prop
csspour ajouter rapidement des styles de type inline avec toute la puissance du CSS
Choisis vanilla-extract quand :
- La performance est la priorité numéro un -- zéro JS dans le bundle client
- Tu utilises TypeScript et veux une sécurité de typage complète
- Tu construis une grande application où la taille du bundle compte
- Tu n'as pas besoin de styles dynamiques basés sur des props à l'exécution
Tendance : le CSS-in-JS zero-runtime
De plus en plus de projets migrent vers des solutions zero-runtime comme vanilla-extract, Panda CSS ou Linaria. La raison ? Dans les grandes applications, le CSS-in-JS runtime (styled-components, Emotion) peut ralentir le rendu car il génère les styles en JavaScript pendant que l'application tourne.
Le zero-runtime génère les classes CSS au moment de la compilation -- c'est comme préparer une carte de navigation avant le lancement plutôt que de calculer l'itinéraire en plein vol. Le résultat : un chargement de page plus rapide et de meilleurs scores Core Web Vitals.
1// Panda CSS -- une autre alternative zero-runtime populaire
2import { css } from '../styled-system/css';
3
4function Panel() {
5 return (
6 <div className={css({
7 bg: 'slate.900',
8 p: '4',
9 rounded: 'xl',
10 border: '2px solid',
11 borderColor: 'indigo.700'
12 })}>
13 Panel content
14 </div>
15 );
16}Quelle que soit la bibliothèque que tu choisis, le plus important est de t'en tenir à une seule approche au sein d'un projet et d'établir des conventions avec ton équipe.
Code de cette leçon : App.jsx
1import React from 'react';
2import './styles.css';
3
4// Démonstration de différents CSS-in-JS : Emotion, vanilla-extract
5// (Dans Sandpack, on ne peut pas utiliser Emotion/vanilla-extract,
6// donc on illustre la syntaxe sous forme de comparaison)
7
8function CodeBlock({ title, code, language }) {
9 return (
10 <div className="code-block">
11 <div className="code-header">
12 <span className="code-lang">{language}</span>
13 <span className="code-title">{title}</span>
14 </div>
15 <pre className="code-content">{code}</pre>
16 </div>
17 );
18}
19
20function ComparisonSection({ title, children }) {
21 return (
22 <div className="section">
23 <h2 className="section-title">{title}</h2>
24 {children}
25 </div>
26 );
27}
28
29export default function App() {
30 return (
31 <div className="app">
32 <h1 className="heading">CSS-in-JS : Emotion vs Vanilla-extract</h1>
33 <p className="subtitle">Bibliothèques de stylage avancées</p>
34
35 <ComparisonSection title="Emotion -- prop css">
36 <CodeBlock
37 title="Styles inline avec le prop css"
38 language="JSX"
39 code={`/** @jsxImportSource @emotion/react */
40import { css } from '@emotion/react';
41
42function Panel({ isActive }) {
43 return (
44 <div css={css\`
45 background: #0d1137;
46 border: 2px solid \${isActive ? '#00e676' : '#3949ab'};
47 padding: 20px;
48 \`}>
49 Panel content
50 </div>
51 );
52}`}
53 />
54 <div className="feature-list">
55 <div className="feature">
56 <span className="feature-icon good">+</span>
57 <span>Prop css -- des styles directement sur l'élément</span>
58 </div>
59 <div className="feature">
60 <span className="feature-icon good">+</span>
61 <span>Bundle plus petit que styled-components</span>
62 </div>
63 <div className="feature">
64 <span className="feature-icon good">+</span>
65 <span>Prend en charge la syntaxe objet et les template literals</span>
66 </div>
67 </div>
68 </ComparisonSection>
69
70 <ComparisonSection title="Vanilla-extract -- zero runtime">
71 <CodeBlock
72 title="Styles générés au moment de la compilation"
73 language="TypeScript"
74 code={`// styles.css.ts
75import { style } from '@vanilla-extract/css';
76
77export const panel = style({
78 background: '#0d1137',
79 border: '2px solid #3949ab',
80 padding: 20,
81 ':hover': {
82 borderColor: '#7c4dff',
83 },
84});
85
86// Component.tsx
87import { panel } from './styles.css.ts';
88<div className={panel}>Panel</div>`}
89 />
90 <div className="feature-list">
91 <div className="feature">
92 <span className="feature-icon good">+</span>
93 <span>Zero runtime -- aucun JS de stylage dans le bundle</span>
94 </div>
95 <div className="feature">
96 <span className="feature-icon good">+</span>
97 <span>TypeScript-first -- typage complet</span>
98 </div>
99 <div className="feature">
100 <span className="feature-icon warn">!</span>
101 <span>Nécessite une configuration de build</span>
102 </div>
103 </div>
104 </ComparisonSection>
105
106 <div className="comparison-table">
107 <h2 className="section-title">Comparaison</h2>
108 <table>
109 <thead>
110 <tr>
111 <th>Caractéristique</th>
112 <th>Styled-comp.</th>
113 <th>Emotion</th>
114 <th>Vanilla-extract</th>
115 </tr>
116 </thead>
117 <tbody>
118 <tr><td>Runtime JS</td><td>Oui</td><td>Oui</td><td className="highlight">Non</td></tr>
119 <tr><td>Taille du bundle</td><td>Gros</td><td>Moyen</td><td className="highlight">Minimal</td></tr>
120 <tr><td>TypeScript</td><td>Bon</td><td>Bon</td><td className="highlight">Natif</td></tr>
121 <tr><td>SSR</td><td>Configuration</td><td>Configuration</td><td className="highlight">Sans accroc</td></tr>
122 <tr><td>DX</td><td className="highlight">Excellent</td><td className="highlight">Excellent</td><td>Bon</td></tr>
123 </tbody>
124 </table>
125 </div>
126 </div>
127 );
128}Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Quelle bibliothèque CSS-in-JS génère les styles au moment de la compilation (zero runtime) ?
2. En quoi Emotion avec le prop css diffère-t-il de styled-components ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Tri vertical
Ordonne les étapes du traitement des styles dans vanilla-extract du début à la fin :
- Tri horizontal
Ordonne les éléments nécessaires pour utiliser le prop css dans Emotion :
- Éditeur de code
Implémente un accordéon dépliable avec un clsx conditionnel
- Clique dans l'ordre
Ordonne la syntaxe de définition d'un style dans vanilla-extract :