Kurs JavaScript i React · Moduł 14: Zaawansowane stylowanie
Porównanie podejść do stylowania - Mapa Galaktyki
W tej lekcji7
Mamy już w arsenale trzy potężne narzędzia do stylowania. Pora porównać je ze sobą i zrozumieć, kiedy które stosować. To jak wybór statku kosmicznego -- każdy ma swoje mocne strony i najlepsze zastosowania.
Tabela porównawcza
| Cecha | CSS Modules | Styled-components | Tailwind CSS |
|---|---|---|---|
| Izolacja | Automatyczna (unikalne klasy) | Automatyczna (unikalne klasy) | Brak (klasy globalne) |
| Dynamiczne style | Ograniczone (zmienne CSS) | Pełne (props + JS) | clsx/cn + warunki |
| Rozmiar bundla | Mały | Większy (runtime JS) | Mały (purge) |
| Krzywa uczenia | Niska | Średnia | Średnia |
| Theming | Zmienne CSS | ThemeProvider | tailwind.config |
| Pseudo-klasy | Tak (zwykły CSS) | Tak (& syntax) | Tak (prefiksy) |
| Responsywność | Media queries | Media queries | Prefiksy (md:, lg:) |
| SSR | Bezproblemowo | Wymaga konfiguracji | Bezproblemowo |
Kiedy używać CSS Modules?
CSS Modules są idealne gdy:
- Chcesz prostego rozwiązania bez dodatkowych bibliotek
- Zespół dobrze zna zwykły CSS
- Projekt używa Create React App lub Next.js (wsparcie wbudowane)
- Zależy ci na wydajności (zero runtime JS)
- Migrujesz z istniejącego projektu CSS
1// CSS Modules -- prosty, znajomy CSS
2import styles from './Card.module.css';
3
4function Card({ title }) {
5 return <div className={styles.card}><h2>{title}</h2></div>;
6}Kiedy używać Styled-components?
Styled-components są idealne gdy:
- Potrzebujesz bardzo dynamicznych stylów (zależnych od propsów, stanu)
- Budujesz bibliotekę komponentów z wbudowanymi stylami
- Chcesz zaawansowany theming z przełączaniem motywów
- Projekt jest komponentowy (style = część komponentu)
1// Styled-components -- style jako część komponentu
2const Card = styled.div`
3 background: ${({ variant }) => variant === 'dark' ? '#0a0a2e' : '#ffffff'};
4 padding: ${({ theme }) => theme.spacing.lg};
5`;Kiedy używać Tailwind CSS?
Tailwind CSS jest idealny gdy:
- Chcesz szybko prototypować interfejsy
- Cenisz spójność designu (design system w config)
- Zespół lubi utility-first podejście
- Zależy ci na małym bundlu (purge nieużywanych klas)
- Używasz Next.js lub Vite (świetna integracja)
1// Tailwind -- szybkie prototypowanie
2function Card({ title }) {
3 return (
4 <div className="bg-slate-900 p-6 rounded-xl border border-indigo-700">
5 <h2 className="text-xl font-bold text-purple-400">{title}</h2>
6 </div>
7 );
8}Mieszanie podejść
W praktyce wiele projektów łączy różne podejścia:
- Tailwind dla layoutu i szybkiego prototypowania
- CSS Modules dla złożonych animacji i specyficznych komponentów
- Styled-components dla komponentów z dynamicznymi stylami
To jest całkowicie dopuszczalne! Klucz to ustalenie konwencji w zespole i trzymanie się jej.
Popularność i trendy
Według ankiet programistów React:
- Tailwind CSS -- rosnąca popularność, dominuje w nowych projektach
- CSS Modules -- stabilna popularność, często w Next.js
- Styled-components -- nadal popularne, ale nieco spadające
- Zero-runtime CSS-in-JS (vanilla-extract, Panda CSS) -- nowy trend, rosnące zainteresowanie
Praktyczne porady przy wyborze
Zanim wybierzesz podejście do stylowania, zadaj sobie kilka pytań:
- Jaki jest rozmiar zespołu? -- duży zespół może preferować Tailwind (spójne wartości) lub CSS Modules (znajomy CSS)
- Czy potrzebujesz dynamicznych stylów? -- jeśli tak, styled-components lub Emotion będą najlepsze
- Jak ważna jest wydajność? -- dla aplikacji krytycznych czasowo rozważ CSS Modules lub zero-runtime CSS-in-JS
- Czy używasz SSR? -- CSS Modules i Tailwind działają bezproblemowo, styled-components wymagają dodatkowej konfiguracji
- Czy masz istniejący CSS? -- jeśli tak, CSS Modules oferują najłatwiejszą migrację
Nie ma jednego "najlepszego" rozwiązania -- każde podejście to inny statek kosmiczny, zaprojektowany do innego typu misji. Kluczem jest zrozumienie kompromisów i wybranie tego, co najlepiej pasuje do Twojego projektu i zespołu.
Kod do tej lekcji: App.jsx
1import React, { useState } from 'react';
2import styled from 'styled-components';
3
4const Container = styled.div`
5 font-family: 'Segoe UI', sans-serif;
6 padding: 20px;
7 background: #0a0a2e;
8 color: #e0e0e0;
9 min-height: 100vh;
10`;
11
12const Heading = styled.h1`
13 color: #64b5f6;
14 text-align: center;
15 margin-bottom: 24px;
16`;
17
18const ComparisonGrid = styled.div`
19 display: grid;
20 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
21 gap: 16px;
22 margin-bottom: 24px;
23`;
24
25const ApproachCard = styled.div`
26 background: ${props => props.active ? '#1a1a5e' : '#1a1a4e'};
27 border: 2px solid ${props => props.active ? '#7c4dff' : '#3949ab'};
28 border-radius: 12px;
29 padding: 20px;
30 cursor: pointer;
31 transition: all 0.3s ease;
32 &:hover { border-color: #7c4dff; transform: translateY(-2px); }
33`;
34
35const ApproachName = styled.h3`
36 color: #7c4dff;
37 margin-bottom: 8px;
38 font-size: 18px;
39`;
40
41const ProsList = styled.ul`
42 list-style: none;
43 padding: 0;
44 margin: 8px 0;
45 li {
46 padding: 4px 0;
47 font-size: 14px;
48 &::before { content: '√ '; color: #00e676; }
49 }
50`;
51
52const ConsList = styled.ul`
53 list-style: none;
54 padding: 0;
55 margin: 8px 0;
56 li {
57 padding: 4px 0;
58 font-size: 14px;
59 &::before { content: '× '; color: #f44336; }
60 }
61`;
62
63const Label = styled.span`
64 font-size: 12px;
65 font-weight: bold;
66 color: ${props => props.type === 'pro' ? '#00e676' : '#f44336'};
67 text-transform: uppercase;
68`;
69
70const approaches = [
71 {
72 name: 'CSS Modules',
73 desc: 'Izolowane style z automatycznie unikalnymi klasami',
74 pros: ['Zero runtime JS', 'Zwykly CSS', 'Wbudowane w CRA/Next.js', 'Latwa migracja'],
75 cons: ['Ograniczone dynamiczne style', 'Trudniejsze laczenie klas', 'Brak natywnego themingu'],
76 bestFor: 'Proste projekty, migracja z CSS, aplikacje Next.js',
77 },
78 {
79 name: 'Styled-components',
80 desc: 'CSS-in-JS z tagged template literals i propsami',
81 pros: ['Dynamiczne style (props)', 'ThemeProvider', 'Pelne pseudo-klasy', 'Rozszerzanie komponentow'],
82 cons: ['Runtime JS', 'Wiekszy bundle', 'SSR wymaga konfiguracji', 'Krzywa uczenia'],
83 bestFor: 'Biblioteki komponentow, dynamiczne UI, theming',
84 },
85 {
86 name: 'Tailwind CSS',
87 desc: 'Utility-first framework z klasami bezposrednio w JSX',
88 pros: ['Szybkie prototypowanie', 'Maly bundle (purge)', 'Spojny design system', 'Responsywnosc (prefiksy)'],
89 cons: ['Dluzsze classNames', 'Krzywa uczenia klas', 'Mniej czytelny JSX', 'Konfiguracja wymagana'],
90 bestFor: 'Szybki rozwoj, prototypy, projekty z design systemem',
91 },
92];
93
94export default function App() {
95 const [selected, setSelected] = useState(0);
96 const approach = approaches[selected];
97
98 return (
99 <Container>
100 <Heading>Porownanie podejsc do stylowania</Heading>
101
102 <ComparisonGrid>
103 {approaches.map((a, i) => (
104 <ApproachCard
105 key={a.name}
106 active={i === selected}
107 onClick={() => setSelected(i)}
108 >
109 <ApproachName>{a.name}</ApproachName>
110 <p style={{ fontSize: '14px', color: '#b0bec5' }}>{a.desc}</p>
111 </ApproachCard>
112 ))}
113 </ComparisonGrid>
114
115 <div style={{ background: '#1a1a4e', borderRadius: '12px', padding: '24px', border: '1px solid #3949ab' }}>
116 <h2 style={{ color: '#7c4dff', marginBottom: '16px' }}>{approach.name}</h2>
117
118 <Label type="pro">Zalety</Label>
119 <ProsList>{approach.pros.map(p => <li key={p}>{p}</li>)}</ProsList>
120
121 <Label type="con">Wady</Label>
122 <ConsList>{approach.cons.map(c => <li key={c}>{c}</li>)}</ConsList>
123
124 <div style={{ marginTop: '16px', padding: '12px', background: '#0d1137', borderRadius: '8px' }}>
125 <span style={{ color: '#64b5f6', fontWeight: 'bold' }}>Najlepsze dla: </span>
126 <span style={{ color: '#e0e0e0' }}>{approach.bestFor}</span>
127 </div>
128 </div>
129 </Container>
130 );
131}Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Które podejście NIE wymaga dodatkowego runtime JavaScript w przeglądarce?
2. Które podejście jest najlepsze do szybkiego prototypowania interfejsów?
Zadania praktyczne w grze
- Klikanie w kolejności
Ułóż składnię composes w CSS Modules:
- Układanie w poziomie
Ułóż składnię łączenia klas CSS Modules z warunkiem za pomocą template literal:
- Edytor kodu
Zaimplementuj przełącznik trzech motywów kosmicznych