Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

CSS Nesting - natywne zagnieżdżanie stylów

CSS Nesting to natywna funkcjonalność CSS, która pozwala na zagnieżdżanie selektorów wewnątrz innych selektorów, podobnie jak w preprocesorach takich jak Sass czy Less. To znacząco poprawia czytelność i organizację kodu CSS.

Podstawy CSS Nesting

Proste zagnieżdżanie

1/* Tradycyjny CSS */
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}

Użycie symbolu &

Symbol

&
reprezentuje selektor rodzica i pozwala na tworzenie bardziej złożonych selektorów:

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  /* Stany hover i 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  /* Warianty przycisków */
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  /* Modyfikatory stanu */
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}

Zaawansowane techniki zagnieżdżania

Kombinatory i pseudo-selektory

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      /* Dropdown menu */
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      /* Pokaż dropdown przy hover */
95      &:hover .dropdown {
96        opacity: 1;
97        visibility: visible;
98        transform: translateY(0);
99      }
100    }
101  }
102  
103  /* Responsive navigation */
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}

Zagnieżdżanie z Media Queries

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}

Praktyczne przykłady zagnieżdżania

Kompleksowy formularz

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}

Card component z wariantami

1.card {
2  background: white;
3  border-radius: 12px;
4  overflow: hidden;
5  transition: all 0.3s ease;
6  position: relative;
7  
8  /* Warianty cieni */
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  /* Warianty interaktywne */
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  /* Komponenty karty */
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  /* Stany specjalne */
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}

Najlepsze praktyki CSS Nesting

1. Ograniczaj głębokość zagnieżdżenia

1/* ✅ Dobrze - maksymalnie 3-4 poziomy */
2.header {
3  .navigation {
4    .nav-item {
5      .nav-link {
6        /* maksymalny poziom */
7      }
8    }
9  }
10}
11
12/* ❌ Źle - zbyt głębokie zagnieżdżenie */
13.page {
14  .content {
15    .section {
16      .container {
17        .row {
18          .column {
19            .card {
20              .header {
21                /* zbyt głęboko! */
22              }
23            }
24          }
25        }
26      }
27    }
28  }
29}

2. Używaj znaczących nazw selektorów

1/* ✅ Dobrze */
2.product-card {
3  .product-image {
4    /* style */
5  }
6  
7  .product-info {
8    .product-title { /* style */ }
9    .product-price { /* style */ }
10  }
11}
12
13/* ❌ Źle */
14.card {
15  .img {
16    /* style */
17  }
18  
19  .info {
20    .title { /* style */ }
21    .price { /* style */ }
22  }
23}

3. Grupuj powiązane style

1.modal {
2  /* Podstawowe style layoutu */
3  position: fixed;
4  top: 0;
5  left: 0;
6  width: 100%;
7  height: 100%;
8  z-index: 1000;
9  
10  /* Tło i overlay */
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  /* Zawartość modala */
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    /* Komponenty modala */
31    .modal-header {
32      /* style */
33    }
34    
35    .modal-body {
36      /* style */
37    }
38    
39    .modal-footer {
40      /* style */
41    }
42  }
43  
44  /* Stany i animacje */
45  &.fade-in {
46    animation: modalFadeIn 0.3s ease;
47  }
48  
49  &.fade-out {
50    animation: modalFadeOut 0.3s ease;
51  }
52}

CSS Nesting znacząco poprawia organizację i czytelność kodu CSS, umożliwiając tworzenie bardziej intuicyjnych i łatwiejszych w utrzymaniu arkuszy stylów. Kluczem jest zachowanie umiaru i przestrzeganie dobrych praktyk.

Ir a CodeWorlds