Kurs JavaScript i React · Moduł 14: Zaawansowane stylowanie

Porównanie podejść do stylowania - Mapa Galaktyki

3 min czytania
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

CechaCSS ModulesStyled-componentsTailwind CSS
IzolacjaAutomatyczna (unikalne klasy)Automatyczna (unikalne klasy)Brak (klasy globalne)
Dynamiczne styleOgraniczone (zmienne CSS)Pełne (props + JS)clsx/cn + warunki
Rozmiar bundlaMałyWiększy (runtime JS)Mały (purge)
Krzywa uczeniaNiskaŚredniaŚrednia
ThemingZmienne CSSThemeProvidertailwind.config
Pseudo-klasyTak (zwykły CSS)Tak (& syntax)Tak (prefiksy)
ResponsywnośćMedia queriesMedia queriesPrefiksy (md:, lg:)
SSRBezproblemowoWymaga konfiguracjiBezproblemowo

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:

  1. Tailwind CSS -- rosnąca popularność, dominuje w nowych projektach
  2. CSS Modules -- stabilna popularność, często w Next.js
  3. Styled-components -- nadal popularne, ale nieco spadające
  4. 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ń:

  1. Jaki jest rozmiar zespołu? -- duży zespół może preferować Tailwind (spójne wartości) lub CSS Modules (znajomy CSS)
  2. Czy potrzebujesz dynamicznych stylów? -- jeśli tak, styled-components lub Emotion będą najlepsze
  3. Jak ważna jest wydajność? -- dla aplikacji krytycznych czasowo rozważ CSS Modules lub zero-runtime CSS-in-JS
  4. Czy używasz SSR? -- CSS Modules i Tailwind działają bezproblemowo, styled-components wymagają dodatkowej konfiguracji
  5. 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. 1. Które podejście NIE wymaga dodatkowego runtime JavaScript w przeglądarce?

  2. 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

Przydatne artykuły