Cours HTML et CSS · Module 9: CSS moderne
CSS Nesting - L'imbrication native des styles
Dans cette leçon4
En CSS classique, tu commences chaque règle pour l'intérieur d'une carte par le même sélecteur : .card h2, .card p, .card .button. Tu le répètes comme un scribe qui recopie le même titre sur chaque rouleau, et quand tu changes le nom de la classe, tu dois corriger tous les endroits. Pendant des années, seul Sass résolvait ce problème. CSS Nesting est une fonctionnalité native de CSS qui permet d'imbriquer des sélecteurs à l'intérieur d'autres sélecteurs, à la manière des préprocesseurs comme Sass ou Less, sans aucune compilation. Cela améliore considérablement la lisibilité et l'organisation du code, et depuis 2023, cela fonctionne dans tous les principaux navigateurs.
Les bases du CSS Nesting
Imbrication simple
Commençons par une comparaison. Nous avons écrit les mêmes styles de carte d'abord de façon traditionnelle, puis avec l'imbrication :
1/* CSS traditionnel */
2.card {
3 padding: 1rem;
4 background: white;
5 border-radius: 8px;
6}
7
8.card h2 {
9 font-size: 1.5rem;
10 margin-bottom: 1rem;
11 color: #333;
12}
13
14.card p {
15 line-height: 1.6;
16 color: #666;
17}
18
19.card .button {
20 padding: 0.5rem 1rem;
21 background: #007bff;
22 color: white;
23 border: none;
24 border-radius: 4px;
25}
26
27/* CSS Nesting */
28.card {
29 padding: 1rem;
30 background: white;
31 border-radius: 8px;
32
33 h2 {
34 font-size: 1.5rem;
35 margin-bottom: 1rem;
36 color: #333;
37 }
38
39 p {
40 line-height: 1.6;
41 color: #666;
42 }
43
44 .button {
45 padding: 0.5rem 1rem;
46 background: #007bff;
47 color: white;
48 border: none;
49 border-radius: 4px;
50 }
51}Les deux versions donnent un résultat identique. Le h2 imbriqué signifie exactement .card h2, c'est-à-dire le titre à l'intérieur de la carte, et il a la même spécificité. Seule l'écriture change : le nom .card n'apparaît qu'une fois, et tout ce qui concerne la carte se trouve dans un seul bloc.
Utiliser le symbole &
Le symbole & représente le sélecteur parent et permet de créer des sélecteurs plus complexes, comme des états, des variantes et des pseudo-éléments :
1.button {
2 padding: 0.75rem 1.5rem;
3 background: #007bff;
4 color: white;
5 border: none;
6 border-radius: 4px;
7 cursor: pointer;
8 transition: all 0.3s ease;
9
10 /* États hover et focus */
11 &:hover {
12 background: #0056b3;
13 transform: translateY(-1px);
14 }
15
16 &:focus {
17 outline: none;
18 box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
19 }
20
21 &:active {
22 transform: translateY(0);
23 }
24
25 /* Variantes de bouton */
26 &.secondary {
27 background: #6c757d;
28
29 &:hover {
30 background: #545b62;
31 }
32 }
33
34 &.danger {
35 background: #dc3545;
36
37 &:hover {
38 background: #c82333;
39 }
40 }
41
42 /* Modificateurs d'état */
43 &.loading {
44 position: relative;
45 color: transparent;
46
47 &::after {
48 content: '';
49 position: absolute;
50 top: 50%;
51 left: 50%;
52 width: 16px;
53 height: 16px;
54 border: 2px solid #ffffff;
55 border-top: 2px solid transparent;
56 border-radius: 50%;
57 animation: spin 1s linear infinite;
58 transform: translate(-50%, -50%);
59 }
60 }
61
62 &:disabled {
63 opacity: 0.6;
64 cursor: not-allowed;
65
66 &:hover {
67 background: #007bff;
68 transform: none;
69 }
70 }
71}
72
73@keyframes spin {
74 0% { transform: translate(-50%, -50%) rotate(0deg); }
75 100% { transform: translate(-50%, -50%) rotate(360deg); }
76}&:hover sans espace désigne le même bouton à l'état survolé, et &.secondary un bouton qui possède en même temps la classe secondary. Attention à l'espace : & :hover désignerait n'importe quel descendant du bouton situé sous le curseur. Il y a aussi une différence importante avec Sass : en CSS natif, tu ne peux pas coller un nom de classe avec &__title, cela ne fonctionne que dans le préprocesseur.
Techniques avancées d'imbrication
Combinateurs et pseudo-sélecteurs
Tu peux tout imbriquer : des sélecteurs descendants, des pseudo-classes, des combinateurs, et même des media queries. Voici toute une navigation avec un menu déroulant dans un seul bloc :
1.navigation {
2 display: flex;
3 align-items: center;
4 padding: 1rem 2rem;
5 background: #ffffff;
6 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
7
8 .logo {
9 font-size: 1.5rem;
10 font-weight: bold;
11 color: #007bff;
12 text-decoration: none;
13
14 &:hover {
15 color: #0056b3;
16 }
17 }
18
19 .nav-list {
20 display: flex;
21 list-style: none;
22 margin: 0 0 0 auto;
23 padding: 0;
24 gap: 2rem;
25
26 .nav-item {
27 position: relative;
28
29 .nav-link {
30 display: block;
31 padding: 0.5rem 0;
32 color: #333;
33 text-decoration: none;
34 font-weight: 500;
35 transition: color 0.3s ease;
36
37 &:hover {
38 color: #007bff;
39 }
40
41 &.active {
42 color: #007bff;
43
44 &::after {
45 content: '';
46 position: absolute;
47 bottom: -8px;
48 left: 0;
49 right: 0;
50 height: 2px;
51 background: #007bff;
52 }
53 }
54 }
55
56 /* Menu déroulant */
57 .dropdown {
58 position: absolute;
59 top: 100%;
60 left: 0;
61 min-width: 200px;
62 background: white;
63 border-radius: 8px;
64 box-shadow: 0 4px 12px rgba(0,0,0,0.15);
65 opacity: 0;
66 visibility: hidden;
67 transform: translateY(-10px);
68 transition: all 0.3s ease;
69 z-index: 1000;
70
71 .dropdown-item {
72 .dropdown-link {
73 display: block;
74 padding: 0.75rem 1rem;
75 color: #333;
76 text-decoration: none;
77
78 &:hover {
79 background: #f8f9fa;
80 color: #007bff;
81 }
82
83 &:first-child {
84 border-radius: 8px 8px 0 0;
85 }
86
87 &:last-child {
88 border-radius: 0 0 8px 8px;
89 }
90 }
91 }
92 }
93
94 /* Afficher le menu déroulant au survol */
95 &:hover .dropdown {
96 opacity: 1;
97 visibility: visible;
98 transform: translateY(0);
99 }
100 }
101 }
102
103 /* Navigation réactive */
104 .mobile-toggle {
105 display: none;
106 flex-direction: column;
107 gap: 4px;
108 background: none;
109 border: none;
110 cursor: pointer;
111
112 .bar {
113 width: 25px;
114 height: 3px;
115 background: #333;
116 transition: all 0.3s ease;
117 }
118
119 &.active {
120 .bar:nth-child(1) {
121 transform: rotate(45deg) translate(5px, 5px);
122 }
123
124 .bar:nth-child(2) {
125 opacity: 0;
126 }
127
128 .bar:nth-child(3) {
129 transform: rotate(-45deg) translate(7px, -6px);
130 }
131 }
132 }
133
134 @media (max-width: 768px) {
135 .nav-list {
136 position: fixed;
137 top: 70px;
138 left: 0;
139 right: 0;
140 flex-direction: column;
141 background: white;
142 padding: 1rem 2rem;
143 transform: translateY(-100%);
144 transition: transform 0.3s ease;
145 box-shadow: 0 4px 12px rgba(0,0,0,0.15);
146
147 &.active {
148 transform: translateY(0);
149 }
150
151 .nav-item {
152 .dropdown {
153 position: static;
154 opacity: 1;
155 visibility: visible;
156 transform: none;
157 box-shadow: none;
158 background: #f8f9fa;
159 margin-top: 0.5rem;
160 }
161 }
162 }
163
164 .mobile-toggle {
165 display: flex;
166 }
167 }
168}La règle &:hover .dropdown affiche le menu quand le curseur se trouve au-dessus d'un élément de la liste. Fais toutefois attention à la profondeur : .dropdown-link se trouve six niveaux plus bas, donc le sélecteur obtenu a la spécificité de six classes et il est difficile à surcharger. C'est un exemple de ce que nous éviterons dans les bonnes pratiques.
Imbrication avec les Media Queries
Une media query imbriquée dans une règle concerne précisément cette règle. Les déclarations écrites directement à l'intérieur de @media s'appliquent au sélecteur parent :
1.hero-section {
2 display: flex;
3 align-items: center;
4 min-height: 100vh;
5 padding: 2rem;
6 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
7 color: white;
8
9 .hero-content {
10 max-width: 1200px;
11 margin: 0 auto;
12 text-align: center;
13
14 .hero-title {
15 font-size: 3.5rem;
16 font-weight: 700;
17 margin-bottom: 1.5rem;
18 line-height: 1.2;
19
20 @media (max-width: 768px) {
21 font-size: 2.5rem;
22 }
23
24 @media (max-width: 480px) {
25 font-size: 2rem;
26 }
27 }
28
29 .hero-subtitle {
30 font-size: 1.5rem;
31 font-weight: 300;
32 margin-bottom: 2rem;
33 opacity: 0.9;
34
35 @media (max-width: 768px) {
36 font-size: 1.25rem;
37 }
38
39 @media (max-width: 480px) {
40 font-size: 1.1rem;
41 }
42 }
43
44 .hero-buttons {
45 display: flex;
46 gap: 1rem;
47 justify-content: center;
48 flex-wrap: wrap;
49
50 @media (max-width: 480px) {
51 flex-direction: column;
52 align-items: center;
53 }
54
55 .btn-primary {
56 padding: 1rem 2rem;
57 font-size: 1.1rem;
58 background: rgba(255, 255, 255, 0.2);
59 border: 2px solid white;
60 color: white;
61 border-radius: 50px;
62 text-decoration: none;
63 transition: all 0.3s ease;
64 backdrop-filter: blur(10px);
65
66 &:hover {
67 background: white;
68 color: #667eea;
69 transform: translateY(-2px);
70 box-shadow: 0 8px 25px rgba(0,0,0,0.2);
71 }
72
73 @media (max-width: 480px) {
74 width: 200px;
75 }
76 }
77
78 .btn-secondary {
79 padding: 1rem 2rem;
80 font-size: 1.1rem;
81 background: transparent;
82 border: 2px solid rgba(255, 255, 255, 0.5);
83 color: white;
84 border-radius: 50px;
85 text-decoration: none;
86 transition: all 0.3s ease;
87
88 &:hover {
89 border-color: white;
90 background: rgba(255, 255, 255, 0.1);
91 }
92
93 @media (max-width: 480px) {
94 width: 200px;
95 }
96 }
97 }
98 }
99
100 @media (max-width: 768px) {
101 padding: 1rem;
102 min-height: 80vh;
103 }
104}Le titre et le sous-titre rétrécissent à 768px et à 480px, et la dernière media query modifie les espacements de la section elle-même. Chaque breakpoint se trouve à côté du style qu'il modifie, tu n'as donc pas besoin de faire défiler le fichier jusqu'à un bloc @media séparé à la fin.
Imbriquer @container
Tu imbriqueras de la même façon les Container Queries de la leçon précédente. Rappelons l'ordre : un conteneur avec container-type et un nom facultatif, la requête @container, les styles des enfants, puis un test à différentes largeurs :
1.card-wrapper {
2 container: card / inline-size;
3}
4
5.card-title {
6 font-size: 1.25rem;
7
8 @container card (min-width: 400px) {
9 font-size: 5cqw;
10 }
11}Quand le conteneur card mesure au moins 400px, le titre reçoit 5cqw, soit 5% de la largeur du conteneur. Les autres unités fonctionnent de la même manière : cqh se base sur la hauteur, et cqi et cqb sur les dimensions inline et block.
Exemples pratiques d'imbrication
Formulaire complexe
Dans un gros composant, l'imbrication montre la structure comme un plan des chambres. Tout le formulaire de contact, avec ses champs, ses états et ses boutons, tient dans une seule règle :
1.contact-form {
2 max-width: 600px;
3 margin: 2rem auto;
4 padding: 2rem;
5 background: white;
6 border-radius: 12px;
7 box-shadow: 0 8px 25px rgba(0,0,0,0.1);
8
9 .form-header {
10 text-align: center;
11 margin-bottom: 2rem;
12
13 h2 {
14 font-size: 2rem;
15 color: #333;
16 margin-bottom: 0.5rem;
17 }
18
19 p {
20 color: #666;
21 font-size: 1.1rem;
22 }
23 }
24
25 .form-row {
26 display: flex;
27 gap: 1rem;
28 margin-bottom: 1.5rem;
29
30 @media (max-width: 600px) {
31 flex-direction: column;
32 }
33
34 .form-group {
35 flex: 1;
36
37 &.full-width {
38 flex-basis: 100%;
39 }
40 }
41 }
42
43 .form-group {
44 margin-bottom: 1.5rem;
45
46 label {
47 display: block;
48 margin-bottom: 0.5rem;
49 font-weight: 600;
50 color: #333;
51
52 .required {
53 color: #e74c3c;
54 margin-left: 0.25rem;
55 }
56 }
57
58 input, textarea, select {
59 width: 100%;
60 padding: 0.75rem;
61 border: 2px solid #e1e5e9;
62 border-radius: 8px;
63 font-size: 1rem;
64 transition: all 0.3s ease;
65 background: #fafbfc;
66
67 &:focus {
68 outline: none;
69 border-color: #007bff;
70 background: white;
71 box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
72 }
73
74 &::placeholder {
75 color: #adb5bd;
76 }
77
78 &.error {
79 border-color: #e74c3c;
80 background: #fff5f5;
81
82 &:focus {
83 box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.1);
84 }
85 }
86
87 &.success {
88 border-color: #28a745;
89 background: #f8fff9;
90
91 &:focus {
92 box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.1);
93 }
94 }
95 }
96
97 textarea {
98 min-height: 120px;
99 resize: vertical;
100 }
101
102 select {
103 cursor: pointer;
104 background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
105 background-position: right 0.5rem center;
106 background-repeat: no-repeat;
107 background-size: 1.5em 1.5em;
108 padding-right: 2.5rem;
109 }
110
111 .error-message {
112 display: block;
113 margin-top: 0.5rem;
114 color: #e74c3c;
115 font-size: 0.875rem;
116 }
117
118 .help-text {
119 display: block;
120 margin-top: 0.5rem;
121 color: #6c757d;
122 font-size: 0.875rem;
123 }
124 }
125
126 .checkbox-group {
127 margin: 1.5rem 0;
128
129 .checkbox-item {
130 display: flex;
131 align-items: center;
132 gap: 0.75rem;
133 margin-bottom: 1rem;
134
135 input[type="checkbox"] {
136 width: auto;
137 margin: 0;
138
139 &:checked + label::before {
140 background-color: #007bff;
141 border-color: #007bff;
142 }
143 }
144
145 label {
146 margin: 0;
147 cursor: pointer;
148 user-select: none;
149
150 a {
151 color: #007bff;
152 text-decoration: none;
153
154 &:hover {
155 text-decoration: underline;
156 }
157 }
158 }
159 }
160 }
161
162 .form-actions {
163 display: flex;
164 gap: 1rem;
165 justify-content: flex-end;
166 margin-top: 2rem;
167
168 @media (max-width: 480px) {
169 flex-direction: column;
170 }
171
172 .btn {
173 padding: 0.75rem 2rem;
174 border: none;
175 border-radius: 8px;
176 font-size: 1rem;
177 font-weight: 600;
178 cursor: pointer;
179 transition: all 0.3s ease;
180 text-decoration: none;
181 display: inline-block;
182 text-align: center;
183
184 &.btn-primary {
185 background: #007bff;
186 color: white;
187
188 &:hover:not(:disabled) {
189 background: #0056b3;
190 transform: translateY(-1px);
191 }
192
193 &:disabled {
194 opacity: 0.6;
195 cursor: not-allowed;
196 }
197 }
198
199 &.btn-secondary {
200 background: #f8f9fa;
201 color: #6c757d;
202 border: 1px solid #dee2e6;
203
204 &:hover {
205 background: #e9ecef;
206 color: #495057;
207 }
208 }
209
210 @media (max-width: 480px) {
211 width: 100%;
212 }
213 }
214 }
215
216 .loading-overlay {
217 position: absolute;
218 top: 0;
219 left: 0;
220 right: 0;
221 bottom: 0;
222 background: rgba(255, 255, 255, 0.9);
223 display: flex;
224 align-items: center;
225 justify-content: center;
226 border-radius: 12px;
227
228 .spinner {
229 width: 40px;
230 height: 40px;
231 border: 4px solid #f3f3f3;
232 border-top: 4px solid #007bff;
233 border-radius: 50%;
234 animation: spin 1s linear infinite;
235 }
236 }
237}La liste input, textarea, select reçoit des styles communs, et &:focus, &::placeholder et &.error décrivent leurs états. L'écriture &:checked + label::before montre qu'après &, tu peux utiliser n'importe quel combinateur, ici celui du voisin direct.
Composant carte avec variantes
Les variantes et les états d'un composant sont l'endroit naturel pour &. Remarque la règle dans laquelle & se trouve à la fin du sélecteur :
1.card {
2 background: white;
3 border-radius: 12px;
4 overflow: hidden;
5 transition: all 0.3s ease;
6 position: relative;
7
8 /* Variantes d'ombre */
9 &.shadow-sm {
10 box-shadow: 0 1px 3px rgba(0,0,0,0.1);
11 }
12
13 &.shadow-md {
14 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
15 }
16
17 &.shadow-lg {
18 box-shadow: 0 10px 25px rgba(0,0,0,0.15);
19 }
20
21 /* Variantes interactives */
22 &.hoverable {
23 cursor: pointer;
24
25 &:hover {
26 transform: translateY(-4px);
27 box-shadow: 0 12px 30px rgba(0,0,0,0.2);
28 }
29 }
30
31 &.clickable {
32 cursor: pointer;
33 user-select: none;
34
35 &:active {
36 transform: scale(0.98);
37 }
38 }
39
40 /* Composants de la carte */
41 .card-header {
42 padding: 1.5rem;
43 border-bottom: 1px solid #f0f0f0;
44
45 &.with-image {
46 padding: 0;
47 position: relative;
48
49 .header-image {
50 width: 100%;
51 height: 200px;
52 object-fit: cover;
53 }
54
55 .header-content {
56 position: absolute;
57 bottom: 0;
58 left: 0;
59 right: 0;
60 padding: 1.5rem;
61 background: linear-gradient(transparent, rgba(0,0,0,0.7));
62 color: white;
63 }
64 }
65
66 .card-title {
67 font-size: 1.5rem;
68 font-weight: 600;
69 margin: 0 0 0.5rem 0;
70 color: #333;
71
72 .card-header.with-image & {
73 color: white;
74 }
75 }
76
77 .card-subtitle {
78 font-size: 1rem;
79 color: #666;
80 margin: 0;
81
82 .card-header.with-image & {
83 color: rgba(255,255,255,0.9);
84 }
85 }
86 }
87
88 .card-body {
89 padding: 1.5rem;
90
91 .card-text {
92 line-height: 1.6;
93 color: #555;
94 margin-bottom: 1rem;
95
96 &:last-child {
97 margin-bottom: 0;
98 }
99 }
100
101 .card-meta {
102 display: flex;
103 align-items: center;
104 gap: 1rem;
105 margin-bottom: 1rem;
106 font-size: 0.9rem;
107 color: #666;
108
109 .meta-item {
110 display: flex;
111 align-items: center;
112 gap: 0.25rem;
113
114 .icon {
115 width: 16px;
116 height: 16px;
117 }
118 }
119 }
120
121 .card-tags {
122 display: flex;
123 flex-wrap: wrap;
124 gap: 0.5rem;
125 margin-bottom: 1rem;
126
127 .tag {
128 padding: 0.25rem 0.75rem;
129 background: #f8f9fa;
130 color: #495057;
131 border-radius: 20px;
132 font-size: 0.8rem;
133 font-weight: 500;
134
135 &.primary {
136 background: #e3f2fd;
137 color: #1976d2;
138 }
139
140 &.success {
141 background: #e8f5e8;
142 color: #2e7d32;
143 }
144
145 &.warning {
146 background: #fff3e0;
147 color: #f57c00;
148 }
149
150 &.danger {
151 background: #ffebee;
152 color: #d32f2f;
153 }
154 }
155 }
156 }
157
158 .card-footer {
159 padding: 1rem 1.5rem;
160 background: #f8f9fa;
161 border-top: 1px solid #f0f0f0;
162
163 .card-actions {
164 display: flex;
165 justify-content: space-between;
166 align-items: center;
167 gap: 1rem;
168
169 @media (max-width: 480px) {
170 flex-direction: column;
171 align-items: stretch;
172 }
173
174 .action-group {
175 display: flex;
176 gap: 0.5rem;
177
178 @media (max-width: 480px) {
179 justify-content: center;
180 }
181 }
182
183 .btn {
184 padding: 0.5rem 1rem;
185 border: none;
186 border-radius: 6px;
187 font-size: 0.9rem;
188 cursor: pointer;
189 text-decoration: none;
190 transition: all 0.2s ease;
191
192 &.btn-primary {
193 background: #007bff;
194 color: white;
195
196 &:hover {
197 background: #0056b3;
198 }
199 }
200
201 &.btn-outline {
202 background: transparent;
203 color: #007bff;
204 border: 1px solid #007bff;
205
206 &:hover {
207 background: #007bff;
208 color: white;
209 }
210 }
211
212 &.btn-ghost {
213 background: transparent;
214 color: #6c757d;
215
216 &:hover {
217 background: #e9ecef;
218 color: #495057;
219 }
220 }
221 }
222 }
223 }
224
225 /* États spéciaux */
226 &.loading {
227 pointer-events: none;
228
229 &::after {
230 content: '';
231 position: absolute;
232 top: 0;
233 left: 0;
234 right: 0;
235 bottom: 0;
236 background: rgba(255,255,255,0.8);
237 display: flex;
238 align-items: center;
239 justify-content: center;
240 }
241 }
242
243 &.disabled {
244 opacity: 0.6;
245 pointer-events: none;
246 }
247}L'écriture .card-header.with-image & signifie : ce titre, mais seulement lorsqu'il se trouve à l'intérieur d'un en-tête avec une image. Le symbole & n'a donc pas besoin d'être au début.
Bonnes pratiques du CSS Nesting
1. Limiter la profondeur d'imbrication
Chaque niveau d'imbrication ajoute une classe au sélecteur et augmente la spécificité. Compare une profondeur raisonnable et une profondeur excessive :
1/* Bien - 3 à 4 niveaux maximum */
2.header {
3 .navigation {
4 .nav-item {
5 .nav-link {
6 /* niveau maximum */
7 }
8 }
9 }
10}
11
12/* Mauvais - imbrication trop profonde */
13.page {
14 .content {
15 .section {
16 .container {
17 .row {
18 .column {
19 .card {
20 .header {
21 /* trop profond ! */
22 }
23 }
24 }
25 }
26 }
27 }
28 }
29}Trois ou quatre niveaux, c'est la limite. Tu imposeras automatiquement la règle max-nesting-depth dans la leçon sur Stylelint.
2. Utiliser des noms de sélecteurs explicites
Des noms courts comme .title ou .img se confondent facilement avec les classes d'autres composants. Mieux vaut les préfixer par le nom du composant :
1/* Bien */
2.product-card {
3 .product-image {
4 /* styles */
5 }
6
7 .product-info {
8 .product-title { /* styles */ }
9 .product-price { /* styles */ }
10 }
11}
12
13/* Mauvais */
14.card {
15 .img {
16 /* styles */
17 }
18
19 .info {
20 .title { /* styles */ }
21 .price { /* styles */ }
22 }
23}Les noms préfixés par product- indiquent immédiatement à quoi ils appartiennent, et n'entrent pas en conflit avec d'autres parties de la page.
3. Regrouper les styles liés
À l'intérieur d'un même bloc, garde un ordre : d'abord la mise en page, puis les parties du composant, et enfin les états et les animations, séparés par des commentaires :
1.modal {
2 /* Styles de mise en page de base */
3 position: fixed;
4 top: 0;
5 left: 0;
6 width: 100%;
7 height: 100%;
8 z-index: 1000;
9
10 /* Arrière-plan et superposition */
11 .modal-backdrop {
12 position: absolute;
13 top: 0;
14 left: 0;
15 width: 100%;
16 height: 100%;
17 background: rgba(0,0,0,0.5);
18 backdrop-filter: blur(4px);
19 }
20
21 /* Contenu de la modale */
22 .modal-content {
23 position: relative;
24 max-width: 500px;
25 margin: 5% auto;
26 background: white;
27 border-radius: 12px;
28 overflow: hidden;
29
30 /* Composants de la modale */
31 .modal-header {
32 /* styles */
33 }
34
35 .modal-body {
36 /* styles */
37 }
38
39 .modal-footer {
40 /* styles */
41 }
42 }
43
44 /* États et animations */
45 &.fade-in {
46 animation: modalFadeIn 0.3s ease;
47 }
48
49 &.fade-out {
50 animation: modalFadeOut 0.3s ease;
51 }
52}CSS Nesting améliore considérablement l'organisation et la lisibilité du code CSS, permettant de créer des feuilles de style plus intuitives et plus faciles à maintenir. La clé est de faire preuve de modération et de suivre les bonnes pratiques. Mon conseil : dans les nouveaux projets sans préprocesseur, utilise sans hésiter l'imbrication native, mais garde en tête les différences avec Sass. Dans la prochaine leçon, tu découvriras les Cascade Layers, qui mettent de l'ordre dans des groupes entiers de styles.
Souviens-toi : l'imbrication ressemble à la descente dans les chambres d'une pyramide, car un ou deux étages plus bas, c'est pratique, mais celui qui descend trop profond perd son chemin.
Code de cette leçon : index.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Sélecteur :has()</title>
6 <link rel="stylesheet" href="style.css">
7</head>
8<body>
9 <h1>Sélecteur :has() - le parent réagit aux enfants</h1>
10 <article>
11 <h2>Pyramide de Khéops</h2>
12 <div class="img-placeholder">IMG</div>
13 <p>L'une des sept merveilles du monde.</p>
14 </article>
15 <article>
16 <h2>Sphinx</h2>
17 <p>Gardien des pyramides, sans illustration.</p>
18 </article>
19 <article>
20 <h2>Temple de Karnak</h2>
21 <div class="video-placeholder">VIDEO</div>
22 <p>Le plus grand complexe de temples.</p>
23 </article>
24</body>
25</html>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. Qu'est-ce que le CSS Nesting natif ?
2. Quelle unité de container query fait référence à la largeur du conteneur ?
Tâches pratiques dans le jeu
- Tri vertical
Ordonne la syntaxe correcte du CSS Nesting :
- Clique dans l'ordre
Ordonne la syntaxe correcte d'un pseudo-sélecteur imbriqué en CSS Nesting :
- Tri vertical
Ordonne les étapes d'utilisation des Container Queries :