W kosmosie stylowania React, Sass (Syntactically Awesome Style Sheets) jest jak statek kosmiczny nowej generacji - oferuje wszystkie możliwości standardowego CSS, ale z dodatkowymi mocami, które czynią stylowanie bardziej wydajnym, modułowym i łatwiejszym w utrzymaniu. Poruszając się w galaktyce React, warto opanować tę technologię, która może znacząco usprawnić tworzenie zaawansowanych stylizacji.
Sass to preprocesor CSS, który rozszerza standardowy CSS o zmienne, zagnieżdżone reguły, mixiny, dziedziczenie selektorów i wiele innych funkcji. Istnieją dwie składnie Sass:
W tym module skupimy się na SCSS, ponieważ jest bardziej popularna i łatwiejsza do nauki dla osób znających CSS. Wyobraźmy sobie SCSS jako ulepszone paliwo rakietowe, które zachowuje wszystkie właściwości standardowego paliwa, ale dodaje więcej mocy.
Aby używać Sass w projekcie React, najpierw musimy zainstalować odpowiednie pakiety:
Jeśli korzystasz z Create React App, instalacja jest prosta:
1npm install sassTo wszystko! CRA ma wbudowaną obsługę Sass, więc po instalacji możesz po prostu tworzyć pliki .scss/.sass i importować je do komponentów.
W projektach Vite, proces jest podobny:
1npm install sassVite ma natywne wsparcie dla Sass, więc nie potrzeba dodatkowej konfiguracji.
Jeśli tworzysz własną konfigurację, potrzebujesz:
1npm install sass sass-loaderI dodać odpowiednią konfigurację do pliku webpack.config.js:
1// Uproszczona konfiguracja webpack
2module.exports = {
3 //
4 module: {
5 rules: [
6 {
7 test: /.s[ac]ss$/i,
8 use: [
9 'style-loader',
10 'css-loader',
11 'sass-loader',
12 ],
13 },
14 ],
15 },
16};Po skonfigurowaniu, możesz tworzyć pliki SCSS i importować je do komponentów:
1// SpacePanel.jsx
2import React from 'react';
3import './SpacePanel.scss'; // Importujemy SCSS zamiast CSS
4
5function SpacePanel() {
6 return (
7 <div className="space-panel">
8 <h2 className="panel-title">Panel Sterowania Statkiem</h2>
9 <div className="control-area">
10 <div className="button-group">
11 <button className="control-button primary">Uruchom silniki</button>
12 <button className="control-button secondary">Nawigacja</button>
13 </div>
14 <div className="status-display">Status: Gotowy do startu</div>
15 </div>
16 </div>
17 );
18}
19
20export default SpacePanel;W kosmicznym interfejsie, często używamy tych samych kolorów, marginesów czy fontów. Zmienne SCSS pozwalają nam zdefiniować je raz i używać wielokrotnie:
1// variables.scss
2$primary-color: #8a2be2; // Kosmiczny fiolet
3$secondary-color: #4a90e2; // Niebieski kosmosu
4$danger-color: #ff4d4f; // Czerwony alarm
5$success-color: #52c41a; // Zielony "go"
6
7$border-radius-small: 4px;
8$border-radius-medium: 8px;
9$border-radius-large: 16px;
10
11$font-main: 'Space Grotesk', sans-serif;
12$font-display: 'Nova Mono', monospace;
13
14// Używanie zmiennych
15.control-button {
16 background-color: $secondary-color;
17 border-radius: $border-radius-medium;
18 font-family: $font-main;
19
20 &.primary {
21 background-color: $primary-color;
22 }
23
24 &.danger {
25 background-color: $danger-color;
26 }
27}Zagnieżdżanie selektorów pozwala na tworzenie bardziej czytelnej hierarchii, która odzwierciedla strukturę HTML:
1.space-panel {
2 background-color: #1a1a2e;
3 padding: 20px;
4 border-radius: 10px;
5
6 .panel-title {
7 color: white;
8 font-size: 24px;
9 margin-bottom: 15px;
10 }
11
12 .control-area {
13 display: flex;
14 justify-content: space-between;
15
16 .button-group {
17 display: flex;
18 gap: 10px;
19
20 .control-button {
21 padding: 10px 15px;
22 // Inne style przycisku...
23 }
24 }
25
26 .status-display {
27 background-color: rgba(0, 0, 0, 0.3);
28 padding: 10px;
29 border-radius: 5px;
30 }
31 }
32}Taki zapis jest nie tylko bardziej czytelny, ale również pomaga uniknąć duplikacji selektorów i czyni strukturę bardziej oczywistą.
Ampersand (&) reprezentuje bieżący selektor i jest szczególnie przydatny do:
1.control-button {
2 background-color: #4a90e2;
3 transition: all 0.3s;
4
5 &:hover {
6 background-color: darken(#4a90e2, 10%);
7 transform: translateY(-2px);
8 }
9
10 &:active {
11 transform: translateY(1px);
12 }
13
14 &::before {
15 content: "🚀";
16 margin-right: 5px;
17 }
18}1.control-button {
2 // Bazowe style...
3
4 &.primary {
5 background-color: #8a2be2;
6 }
7
8 &.secondary {
9 background-color: #4a90e2;
10 }
11
12 &.large {
13 padding: 12px 20px;
14 font-size: 18px;
15 }
16}1.space-theme {
2 .control-button {
3 // Style dla .space-theme .control-button
4 }
5
6 // Używając & możemy wybierać różne kombinacje selektorów
7 &__dark .control-button {
8 // Style dla .space-theme__dark .control-button
9 }
10}Mixiny to zbiory deklaracji CSS, które można wielokrotnie używać w kodzie. To jak reużywalne moduły statku kosmicznego:
1// mixins.scss
2@mixin flex-center {
3 display: flex;
4 justify-content: center;
5 align-items: center;
6}
7
8@mixin glass-effect {
9 background: rgba(255, 255, 255, 0.1);
10 backdrop-filter: blur(10px);
11 border: 1px solid rgba(255, 255, 255, 0.2);
12 box-shadow: 0 8px 32px rgba(31, 38, 135, 0.2);
13}
14
15@mixin button-variant($bg-color, $text-color) {
16 background-color: $bg-color;
17 color: $text-color;
18
19 &:hover {
20 background-color: darken($bg-color, 10%);
21 }
22
23 &:active {
24 background-color: darken($bg-color, 15%);
25 }
26}Użycie mixinów:
1.control-panel {
2 @include glass-effect;
3 padding: 20px;
4}
5
6.button-container {
7 @include flex-center;
8 gap: 10px;
9}
10
11.launch-button {
12 @include button-variant(#ff4d4f, white);
13 padding: 10px 20px;
14}
15
16.navigation-button {
17 @include button-variant(#4a90e2, white);
18 padding: 8px 15px;
19}Dziedziczenie pozwala jednej klasie dziedziczyć właściwości innej, redukując duplikację kodu:
1.base-button {
2 padding: 10px 15px;
3 border: none;
4 border-radius: 4px;
5 font-weight: bold;
6 cursor: pointer;
7 transition: all 0.3s;
8}
9
10.primary-button {
11 @extend .base-button;
12 background-color: #8a2be2;
13 color: white;
14}
15
16.secondary-button {
17 @extend .base-button;
18 background-color: #4a90e2;
19 color: white;
20}
21
22.ghost-button {
23 @extend .base-button;
24 background-color: transparent;
25 color: #8a2be2;
26 border: 1px solid currentColor;
27}SCSS oferuje wiele wbudowanych funkcji do manipulacji kolorami i wartościami:
1.panel {
2 // Matematyczne operacje
3 width: 100% - 40px;
4 padding: 10px * 2;
5
6 // Funkcje kolorów
7 background-color: lighten(#1a1a2e, 10%);
8 border: 1px solid darken(#1a1a2e, 5%);
9
10 .warning-label {
11 color: transparentize(#ff4d4f, 0.2); // Dodaje przezroczystość
12 }
13
14 .accent-element {
15 background-color: mix(#8a2be2, #4a90e2, 30%); // Miesza kolory
16 }
17}SCSS umożliwia dzielenie kodu na modułowe pliki, co jest kluczowe dla dużych projektów:
1// styles/variables.scss
2$primary-color: #8a2be2;
3// Inne zmienne...
4
5// styles/mixins.scss
6@mixin flex-center { /* ... */ }
7// Inne mixiny...
8
9// styles/buttons.scss
10@import 'variables';
11@import 'mixins';
12
13.button { /* ... */ }
14// Style przycisków...
15
16// styles/panels.scss
17@import 'variables';
18@import 'mixins';
19
20.panel { /* ... */ }
21// Style paneli...
22
23// styles/main.scss
24@import 'variables';
25@import 'mixins';
26@import 'buttons';
27@import 'panels';
28// Możemy importować wszystko razem lub wybiórczo w komponentachSCSS oferuje także konstrukcje kontrolne znane z języków programowania:
1// Warunki
2$theme: 'dark';
3
4.space-control {
5 @if $theme == 'dark' {
6 background-color: #121212;
7 color: white;
8 } @else {
9 background-color: #f5f5f5;
10 color: #333;
11 }
12}
13
14// Pętle
15$sizes: (
16 'small': 8px,
17 'medium': 16px,
18 'large': 24px,
19 'xlarge': 32px
20);
21
22@each $name, $size in $sizes {
23 .margin-#{$name} {
24 margin: $size;
25 }
26
27 .padding-#{$name} {
28 padding: $size;
29 }
30}
31
32// Generuje:
33// .margin-small { margin: 8px; }
34// .padding-small { padding: 8px; }
35// ...itd. dla wszystkich rozmiarówSCSS można doskonale połączyć z CSS Modules, tworząc potężne rozwiązanie stylowania:
1// SpacePanel.module.scss
2@import 'src/styles/variables';
3@import 'src/styles/mixins';
4
5.panel {
6 @include glass-effect;
7 background-color: $panel-bg-color;
8
9 .title {
10 color: $text-primary;
11 font-family: $font-display;
12 }
13
14 .controls {
15 display: flex;
16 gap: $spacing-md;
17
18 .button {
19 @include button-base;
20
21 &.primary {
22 @include button-variant($primary-color, white);
23 }
24
25 &.secondary {
26 @include button-variant($secondary-color, white);
27 }
28 }
29 }
30}Użycie w komponencie:
1import React from 'react';
2import styles from './SpacePanel.module.scss';
3
4function SpacePanel() {
5 return (
6 <div className={styles.panel}>
7 <h2 className={styles.title}>Panel kontrolny</h2>
8 <div className={styles.controls}>
9 <button className={`${styles.button} ${styles.primary}`}>
10 Uruchom
11 </button>
12 <button className={`${styles.button} ${styles.secondary}`}>
13 Anuluj
14 </button>
15 </div>
16 </div>
17 );
18}Organizuj swoje pliki SCSS w sposób, który odzwierciedla strukturę komponentów:
1src/
2├── styles/ # Globalne style i zasoby
3│ ├── _variables.scss # Konwencja: pliki z podkreśleniem są tylko do importowania
4│ ├── _mixins.scss
5│ ├── _functions.scss
6│ ├── _reset.scss
7│ └── global.scss # Główny plik globalnych stylów
8├── components/
9│ ├── Button/
10│ │ ├── Button.jsx
11│ │ └── Button.module.scss
12│ ├── Panel/
13│ │ ├── Panel.jsx
14│ │ └── Panel.module.scss
15│ └──
16└──Używaj konsekwentnych konwencji nazewniczych:
1// _variables.scss
2$primaryColor: #8a2be2;
3$secondaryColor: #4a90e2;
4
5// Button.module.scss (wykorzystuje BEM)
6.button {
7 // Block
8 background-color: $primaryColor;
9
10 &__icon {
11 // Element
12 margin-right: 8px;
13 }
14
15 &--large {
16 // Modifier
17 padding: 12px 24px;
18 }
19}Zbyt głębokie zagnieżdżenie może prowadzić do nadspecyficznych selektorów, co utrudnia ich nadpisywanie:
1// Unikaj:
2.panel {
3 .content {
4 .section {
5 .title {
6 .icon {
7 // 5 poziomów zagnieżdżenia!
8 color: red;
9 }
10 }
11 }
12 }
13}
14
15// Lepiej:
16.panel {
17 // Ogólne style panelu
18}
19
20.panel-content {
21 // Style dla zawartości
22}
23
24.section-title-icon {
25 color: red;
26}1// _spacing.scss
2$spacing-xs: 4px;
3$spacing-sm: 8px;
4$spacing-md: 16px;
5$spacing-lg: 24px;
6$spacing-xl: 32px;
7
8// _colors.scss
9$blue-100: #e3f2fd;
10$blue-200: #bbdefb;
11//
12$blue-900: #0d47a1;
13
14$color-primary: $blue-500;
15$color-secondary: $purple-500;1@mixin media-breakpoint-up($breakpoint) {
2 @if $breakpoint == sm {
3 @media (min-width: 576px) { @content; }
4 } @else if $breakpoint == md {
5 @media (min-width: 768px) { @content; }
6 } @else if $breakpoint == lg {
7 @media (min-width: 992px) { @content; }
8 } @else if $breakpoint == xl {
9 @media (min-width: 1200px) { @content; }
10 }
11}
12
13// Użycie:
14.responsive-element {
15 font-size: 14px;
16
17 @include media-breakpoint-up(md) {
18 font-size: 16px;
19 }
20
21 @include media-breakpoint-up(lg) {
22 font-size: 18px;
23 }
24}Zobaczmy, jak zastosować SCSS do stworzenia interaktywnego panelu sterowania:
1// _variables.scss
2$primary-color: #7b2cbf;
3$secondary-color: #3a86ff;
4$success-color: #06d6a0;
5$warning-color: #ffd166;
6$danger-color: #ef476f;
7$dark-bg: #1a1a2e;
8$panel-bg: #252941;
9$text-light: #e6e6ff;
10
11$border-radius-sm: 4px;
12$border-radius-md: 8px;
13$border-radius-lg: 12px;
14
15$transition-fast: 0.2s ease;
16$transition-normal: 0.3s ease;
17
18// _mixins.scss
19@mixin glass-morphism {
20 background: rgba(37, 41, 65, 0.8);
21 backdrop-filter: blur(10px);
22 border: 1px solid rgba(255, 255, 255, 0.1);
23 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
24}
25
26@mixin button-glow($color) {
27 box-shadow: 0 0 15px rgba($color, 0.5);
28 &:hover {
29 box-shadow: 0 0 25px rgba($color, 0.7);
30 }
31}
32
33// SpaceControlPanel.module.scss
34@import 'src/styles/variables';
35@import 'src/styles/mixins';
36
37.controlPanel {
38 @include glass-morphism;
39 padding: 24px;
40 border-radius: $border-radius-lg;
41 color: $text-light;
42
43 .panelHeader {
44 display: flex;
45 justify-content: space-between;
46 align-items: center;
47 margin-bottom: 24px;
48 padding-bottom: 16px;
49 border-bottom: 1px solid rgba(255, 255, 255, 0.1);
50
51 .title {
52 font-size: 28px;
53 font-weight: 600;
54
55 &::before {
56 content: "🚀";
57 margin-right: 10px;
58 }
59 }
60
61 .status {
62 padding: 6px 12px;
63 border-radius: $border-radius-sm;
64 font-size: 14px;
65 font-weight: 500;
66
67 &.ready {
68 background-color: rgba($success-color, 0.2);
69 color: $success-color;
70 }
71
72 &.standby {
73 background-color: rgba($warning-color, 0.2);
74 color: $warning-color;
75 }
76
77 &.error {
78 background-color: rgba($danger-color, 0.2);
79 color: $danger-color;
80 }
81 }
82 }
83
84 .controlGrid {
85 display: grid;
86 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
87 gap: 20px;
88
89 .controlSection {
90 background-color: rgba(0, 0, 0, 0.2);
91 border-radius: $border-radius-md;
92 padding: 16px;
93
94 .sectionTitle {
95 font-size: 18px;
96 margin-bottom: 16px;
97 color: rgba($text-light, 0.8);
98 }
99
100 .buttonGroup {
101 display: flex;
102 flex-direction: column;
103 gap: 12px;
104 }
105 }
106 }
107
108 .button {
109 padding: 12px 16px;
110 border: none;
111 border-radius: $border-radius-sm;
112 font-weight: 500;
113 letter-spacing: 0.5px;
114 cursor: pointer;
115 transition: all $transition-normal;
116 position: relative;
117 overflow: hidden;
118
119 &::before {
120 content: "";
121 position: absolute;
122 top: 0;
123 left: -100%;
124 width: 100%;
125 height: 100%;
126 background: linear-gradient(
127 90deg,
128 transparent,
129 rgba(255, 255, 255, 0.2),
130 transparent
131 );
132 transition: all 0.6s;
133 }
134
135 &:hover::before {
136 left: 100%;
137 }
138
139 &.primary {
140 background-color: $primary-color;
141 color: white;
142 @include button-glow($primary-color);
143 }
144
145 &.secondary {
146 background-color: $secondary-color;
147 color: white;
148 @include button-glow($secondary-color);
149 }
150
151 &.success {
152 background-color: $success-color;
153 color: black;
154 @include button-glow($success-color);
155 }
156
157 &.warning {
158 background-color: $warning-color;
159 color: black;
160 @include button-glow($warning-color);
161 }
162
163 &.danger {
164 background-color: $danger-color;
165 color: white;
166 @include button-glow($danger-color);
167 }
168
169 &.disabled {
170 opacity: 0.5;
171 cursor: not-allowed;
172 pointer-events: none;
173 }
174 }
175
176 .monitorDisplay {
177 margin-top: 24px;
178 padding: 16px;
179 background-color: rgba(0, 0, 0, 0.4);
180 border-radius: $border-radius-md;
181 font-family: monospace;
182 height: 120px;
183 overflow-y: auto;
184
185 .logLine {
186 margin: 4px 0;
187 font-size: 14px;
188
189 &.info {
190 color: $secondary-color;
191 }
192
193 &.success {
194 color: $success-color;
195 }
196
197 &.warning {
198 color: $warning-color;
199 }
200
201 &.error {
202 color: $danger-color;
203 }
204
205 &::before {
206 content: "> ";
207 }
208 }
209 }
210}
211
212// Użycie w komponencie React
213import React, { useState } from 'react';
214import styles from './SpaceControlPanel.module.scss';
215
216function SpaceControlPanel() {
217 const [status, setStatus] = useState('standby');
218 const [logs, setLogs] = useState([
219 { type: 'info', text: 'System initialized' },
220 { type: 'info', text: 'Waiting for commands' }
221 ]);
222
223 const addLog = (text, type = 'info') => {
224 setLogs(prev => [...prev, { type, text }]);
225 };
226
227 const initiateSequence = () => {
228 setStatus('ready');
229 addLog('Launch sequence initiated', 'success');
230 };
231
232 const abortSequence = () => {
233 setStatus('standby');
234 addLog('Launch sequence aborted', 'warning');
235 };
236
237 const triggerAlert = () => {
238 setStatus('error');
239 addLog('ALERT: Critical system failure detected', 'error');
240 };
241
242 return (
243 <div className={styles.controlPanel}>
244 <div className={styles.panelHeader}>
245 <h2 className={styles.title}>Kosmiczny Panel Sterowania</h2>
246 <div className={`${styles.status} ${styles[status]}`}>
247 Status: {status === 'standby' ? 'Oczekiwanie' :
248 status === 'ready' ? 'Gotowy' : 'Błąd'}
249 </div>
250 </div>
251
252 <div className={styles.controlGrid}>
253 <div className={styles.controlSection}>
254 <h3 className={styles.sectionTitle}>Kontrola Startu</h3>
255 <div className={styles.buttonGroup}>
256 <button
257 className={`${styles.button} ${styles.success}`}
258 onClick={initiateSequence}
259 >
260 Inicjuj Start
261 </button>
262 <button
263 className={`${styles.button} ${styles.warning}`}
264 onClick={abortSequence}
265 >
266 Przerwij Sekwencję
267 </button>
268 </div>
269 </div>
270
271 <div className={styles.controlSection}>
272 <h3 className={styles.sectionTitle}>Systemy Pokładowe</h3>
273 <div className={styles.buttonGroup}>
274 <button
275 className={`${styles.button} ${styles.primary}`}
276 onClick={() => addLog('Navigation systems online', 'info')}
277 >
278 System Nawigacji
279 </button>
280 <button
281 className={`${styles.button} ${styles.secondary}`}
282 onClick={() => addLog('Life support activated', 'info')}
283 >
284 Podtrzymanie Życia
285 </button>
286 </div>
287 </div>
288
289 <div className={styles.controlSection}>
290 <h3 className={styles.sectionTitle}>Bezpieczeństwo</h3>
291 <div className={styles.buttonGroup}>
292 <button
293 className={`${styles.button} ${styles.danger}`}
294 onClick={triggerAlert}
295 >
296 Alarm Awaryjny
297 </button>
298 <button
299 className={`${styles.button} ${styles.disabled}`}
300 >
301 Protokół Ewakuacji
302 </button>
303 </div>
304 </div>
305 </div>
306
307 <div className={styles.monitorDisplay}>
308 {logs.map((log, index) => (
309 <div key={index} className={`${styles.logLine} ${styles[log.type]}`}>
310 {log.text}
311 </div>
312 ))}
313 </div>
314 </div>
315 );
316}
317
318export default SpaceControlPanel;Sass (SCSS) to potężne narzędzie, które znacząco rozszerza możliwości CSS, pozostając przy tym bliskie jego oryginalnej składni. W ekosystemie React, SCSS doskonale integruje się zarówno z podejściem modułowym (CSS Modules), jak i z innymi technikami stylowania.
Dzięki zmiennym, mixinom, zagnieżdżaniu i innym funkcjom, SCSS pozwala na tworzenie bardziej modułowego, łatwiejszego w utrzymaniu kodu CSS, co jest szczególnie ważne w większych aplikacjach React.
Jak każdy zaawansowany statek kosmiczny, SCSS wymaga pewnego czasu na opanowanie, ale inwestycja w naukę szybko zwraca się w postaci bardziej wydajnej pracy i lepiej zorganizowanego kodu. W przypadku aplikacji React, gdzie zarządzanie komponentami i ich stylami jest kluczowe, SCSS stanowi jedną z najlepszych opcji stylowania, oferując doskonałą równowagę między mocą a prostotą.