Kurs Next.js · Moduł 3: Stylowanie i optymalizacja wizualna
Globalne style i CSS Modules w Next.js 16
W tej lekcji9
Witaj w Metropolis Quantum, mieście przyszłości, gdzie każdy element przestrzeni miejskiej ma swój własny, unikalny styl, a jednocześnie całe miasto utrzymuje spójną estetykę. Podobnie jak w tym futurystycznym mieście, w aplikacjach Next.js 16 potrzebujemy zarówno globalnych reguł stylowania, które definiują ogólny wygląd, jak i lokalnych, modułowych stylów, które zapobiegają konfliktom i pozwalają na utrzymanie porządku.
W tym rozdziale poznamy dwa kluczowe podejścia do stylowania w Next.js 16:
- Style globalne - wpływające na całą aplikację
- CSS Modules - pozwalające na lokalne, hermetyczne stylowanie komponentów
Style globalne w Next.js 16
W Metropolis Quantum podstawowe elementy infrastruktury, takie jak system transportu, oświetlenie czy oznakowanie, mają jednolity wygląd w całym mieście. Podobnie, w aplikacjach webowych potrzebujemy globalnych reguł stylowania dla podstawowych elementów, takich jak typografia, kolory, marginesy czy siatka.
Plik global.css
W Next.js 16 globalne style są najczęściej definiowane w pliku app/globals.css, który następnie jest importowany w głównym layoucie aplikacji. Tak wygląda typowa struktura:
1// app/layout.tsx
2import './globals.css';
3import type { Metadata } from 'next';
4
5export const metadata: Metadata = {
6 title: 'Metropolis Quantum',
7 description: 'Twoja brama do miasta przyszłości',
8};
9
10export default function RootLayout({
11 children,
12}: {
13 children: React.ReactNode;
14}) {
15 return (
16 <html lang="pl">
17 <body>{children}</body>
18 </html>
19 );
20}Zawartość pliku globals.css może wyglądać tak:
1/* app/globals.css */
2:root {
3 --primary-color: #3a86ff;
4 --secondary-color: #8338ec;
5 --accent-color: #ff006e;
6 --background-light: #f8f9fa;
7 --background-dark: #212529;
8 --text-light: #f8f9fa;
9 --text-dark: #212529;
10 --spacing-unit: 0.5rem;
11 --border-radius: 0.375rem;
12}
13
14* {
15 box-sizing: border-box;
16 padding: 0;
17 margin: 0;
18}
19
20html,
21body {
22 max-width: 100vw;
23 overflow-x: hidden;
24 font-family: 'Quantum Sans', sans-serif;
25}
26
27body {
28 color: var(--text-dark);
29 background: var(--background-light);
30}
31
32a {
33 color: var(--primary-color);
34 text-decoration: none;
35}
36
37h1, h2, h3, h4, h5, h6 {
38 font-family: 'Quantum Display', sans-serif;
39 margin-bottom: var(--spacing-unit);
40}
41
42.container {
43 width: 100%;
44 max-width: 1200px;
45 margin: 0 auto;
46 padding: 0 var(--spacing-unit);
47}
48
49@media (prefers-color-scheme: dark) {
50 body {
51 color: var(--text-light);
52 background: var(--background-dark);
53 }
54}CSS Reset i Normalizacja
Często dobrą praktyką jest rozpoczęcie od tzw. CSS Reset lub normalizacji, które niwelują różnice między domyślnymi stylami różnych przeglądarek. Możesz użyć popularnych rozwiązań, takich jak:
normalize.cssreset.css- Własne rozwiązanie
Przykładowo, można zainstalować normalize.css za pomocą npm i zaimportować go na początku pliku globals.css:
1npm install normalize.css1/* app/globals.css */
2@import 'normalize.css';
3
4/* Reszta globalnych stylów */Wykorzystanie zmiennych CSS
Jak widać w powyższym przykładzie, zmienne CSS (custom properties) są potężnym narzędziem do definiowania globalnych wartości, które można następnie wykorzystać w całej aplikacji:
1:root {
2 --primary-color: #3a86ff;
3 --spacing-large: 2rem;
4}
5
6.button {
7 background-color: var(--primary-color);
8 padding: var(--spacing-large);
9}To podejście pozwala na łatwe zarządzanie paletą kolorów, odstępami, rozmiarami czcionek i innymi powtarzającymi się wartościami.
Globalne style dla określonych ścieżek
W Next.js 16 możesz również definiować style globalne, które będą stosowane tylko do określonych ścieżek lub layoutów. Służą do tego pliki layout.tsx w poszczególnych katalogach:
1// app/dashboard/layout.tsx
2import '../dashboard-styles.css';
3
4export default function DashboardLayout({
5 children,
6}: {
7 children: React.ReactNode;
8}) {
9 return <div className="dashboard-container">{children}</div>;
10}Te style będą stosowane tylko do stron wewnątrz katalogu /dashboard.
CSS Modules: Hermetyzacja stylów
W Metropolis Quantum każda dzielnica ma swoją unikalną estetykę - Dystrykt Kwantowy pełen jest futurystycznych, holograficznych elementów, podczas gdy Stare Miasto łączy nowoczesność z architekturą retro. Mimo tych różnic, wszystkie dzielnice stanowią spójną całość.
CSS Modules działa podobnie - pozwala na tworzenie lokalnych, hermetycznych stylów dla komponentów, które nie będą wpływać na resztę aplikacji.
Podstawy CSS Modules
CSS Modules to technika, która automatycznie tworzy unikalne nazwy klas CSS, zapobiegając konfliktom. W Next.js 16 każdy plik z rozszerzeniem .module.css jest traktowany jako moduł CSS.
Tworzenie i używanie modułu CSS:
1/* Button.module.css */
2.button {
3 padding: 0.75rem 1.5rem;
4 border-radius: 0.375rem;
5 font-weight: 600;
6 transition: all 0.2s;
7}
8
9.primary {
10 background-color: var(--primary-color);
11 color: white;
12}
13
14.secondary {
15 background-color: var(--secondary-color);
16 color: white;
17}
18
19.button:hover {
20 transform: translateY(-2px);
21 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
22}1// components/Button.tsx
2import styles from './Button.module.css';
3
4interface ButtonProps {
5 variant?: 'primary' | 'secondary';
6 children: React.ReactNode;
7 onClick?: () => void;
8}
9
10export default function Button({ variant = 'primary', children, onClick }: ButtonProps) {
11 return (
12 <button
13 className={`${styles.button} ${styles[variant]}`}
14 onClick={onClick}
15 >
16 {children}
17 </button>
18 );
19}Jak działają CSS Modules
Gdy aplikacja jest budowana, Next.js przetwarza pliki .module.css i generuje unikalne nazwy klas. Na przykład, klasa .button z powyższego przykładu może zostać przekształcona w .Button_button__XYZ123. Dzięki temu, nawet jeśli w innym komponencie zdefiniujesz klasę o tej samej nazwie, nie dojdzie do konfliktu.
Dostęp do globalnych zmiennych CSS
CSS Modules mogą korzystać z globalnych zmiennych CSS zdefiniowanych w globals.css:
1/* Card.module.css */
2.card {
3 background-color: white;
4 border-radius: var(--border-radius);
5 padding: calc(var(--spacing-unit) * 2);
6 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
7}Komponowanie klas
CSS Modules pozwala na komponowanie klas za pomocą składni composes:
1/* Text.module.css */
2.base {
3 font-family: 'Quantum Sans', sans-serif;
4 line-height: 1.5;
5}
6
7.heading {
8 composes: base;
9 font-weight: 700;
10 line-height: 1.2;
11}
12
13.paragraph {
14 composes: base;
15 margin-bottom: 1rem;
16}Dynamiczne style z CSS Modules
Możesz dynamicznie stosować klasy w zależności od stanu komponentu:
1// components/Alert.tsx
2import { useState } from 'react';
3import styles from './Alert.module.css';
4
5interface AlertProps {
6 type: 'info' | 'success' | 'warning' | 'error';
7 message: string;
8}
9
10export default function Alert({ type, message }: AlertProps) {
11 const [isVisible, setIsVisible] = useState(true);
12
13 if (!isVisible) return null;
14
15 return (
16 <div className={`${styles.alert} ${styles[type]} ${isVisible ? styles.visible : ''}`}>
17 <p>{message}</p>
18 <button
19 className={styles.closeButton}
20 onClick={() => setIsVisible(false)}
21 >
22 ×
23 </button>
24 </div>
25 );
26}1/* Alert.module.css */
2.alert {
3 padding: 1rem;
4 border-radius: var(--border-radius);
5 margin-bottom: 1rem;
6 display: flex;
7 justify-content: space-between;
8 align-items: center;
9}
10
11.info {
12 background-color: #e3f2fd;
13 color: #0d47a1;
14}
15
16.success {
17 background-color: #e8f5e9;
18 color: #1b5e20;
19}
20
21.warning {
22 background-color: #fff3e0;
23 color: #e65100;
24}
25
26.error {
27 background-color: #ffebee;
28 color: #b71c1c;
29}
30
31.visible {
32 opacity: 1;
33 transition: opacity 0.3s ease-in;
34}
35
36.closeButton {
37 background: none;
38 border: none;
39 font-size: 1.5rem;
40 cursor: pointer;
41 opacity: 0.7;
42}
43
44.closeButton:hover {
45 opacity: 1;
46}Łączenie globalnych stylów z CSS Modules
W rzeczywistych aplikacjach najczęściej stosuje się oba podejścia:
Globalne style dla:
- Resetowania CSS
- Podstawowych elementów typografii
- Zmiennych (kolory, odstępy, cienie, itp.)
- Klas użytkowych (utilities)
CSS Modules dla:
- Konkretnych komponentów
- Elementów, które mogą mieć różne style w zależności od kontekstu
Przykład: Struktura projektu
1app/
2├── layout.tsx
3├── page.tsx
4├── globals.css
5├── dashboard/
6│ ├── layout.tsx
7│ ├── page.tsx
8│ └── dashboard.module.css
9└── components/
10 ├── Button/
11 │ ├── Button.tsx
12 │ └── Button.module.css
13 ├── Card/
14 │ ├── Card.tsx
15 │ └── Card.module.css
16 └──Alternatywne podejścia do stylowania w Next.js 16
Oprócz globalnych stylów i CSS Modules, Next.js 16 wspiera również inne podejścia do stylowania:
1. Styled JSX
Next.js ma wbudowane wsparcie dla Styled JSX, które pozwala na tworzenie stylów lokalnych bezpośrednio w komponentach:
1export default function Button() {
2 return (
3 <>
4 <button>Kliknij mnie</button>
5 <style jsx>{`
6 button {
7 padding: 0.5rem 1rem;
8 background-color: #3a86ff;
9 color: white;
10 border: none;
11 border-radius: 0.25rem;
12 }
13 button:hover {
14 background-color: #2563eb;
15 }
16 `}</style>
17 </>
18 );
19}2. Sass/SCSS
Next.js wspiera również Sass/SCSS. Wystarczy zainstalować sass:
1npm install sassNastępnie możesz tworzyć pliki .scss lub .sass, w tym moduły CSS z rozszerzeniem .module.scss:
1// Button.module.scss
2$primary-color: #3a86ff;
3
4.button {
5 padding: 0.75rem 1.5rem;
6 background-color: $primary-color;
7
8 &:hover {
9 background-color: darken($primary-color, 10%);
10 }
11
12 .icon {
13 margin-right: 0.5rem;
14 }
15}3. CSS-in-JS
Next.js wspiera również biblioteki CSS-in-JS, takie jak styled-components czy emotion. W przypadku tych bibliotek, potrzebne jest dodatkowe ustawienie, aby działały z SSR (Server-Side Rendering):
1// components/StyledButton.tsx
2import styled from 'styled-components';
3
4const StyledButton = styled.button`
5 padding: 0.75rem 1.5rem;
6 background-color: #3a86ff;
7 color: white;
8 border: none;
9 border-radius: 0.25rem;
10
11 &:hover {
12 background-color: #2563eb;
13 }
14`;
15
16export default function Button({ children }) {
17 return <StyledButton>{children}</StyledButton>;
18}Aby styled-components działały z SSR, musisz utworzyć własny dokument w Next.js:
1// app/registry.tsx
2'use client';
3
4import { useState } from 'react';
5import { useServerInsertedHTML } from 'next/navigation';
6import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
7
8export default function StyledComponentsRegistry({
9 children,
10}: {
11 children: React.ReactNode;
12}) {
13 const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
14
15 useServerInsertedHTML(() => {
16 const styles = styledComponentsStyleSheet.getStyleElement();
17 styledComponentsStyleSheet.instance.clearTag();
18 return <>{styles}</>;
19 });
20
21 if (typeof window !== 'undefined') return <>{children}</>;
22
23 return (
24 <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
25 {children}
26 </StyleSheetManager>
27 );
28}1// app/layout.tsx
2import StyledComponentsRegistry from './registry';
3import './globals.css';
4
5export default function RootLayout({
6 children,
7}: {
8 children: React.ReactNode;
9}) {
10 return (
11 <html>
12 <body>
13 <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
14 </body>
15 </html>
16 );
17}Praktyczne przykłady
Przykład 1: Tworzenie systemu siatki (grid)
1/* app/globals.css */
2.row {
3 display: flex;
4 flex-wrap: wrap;
5 margin: 0 -1rem;
6}
7
8.col {
9 padding: 0 1rem;
10}
11
12.col-1 { flex: 0 0 8.333%; max-width: 8.333%; }
13.col-2 { flex: 0 0 16.666%; max-width: 16.666%; }
14.col-3 { flex: 0 0 25%; max-width: 25%; }
15.col-4 { flex: 0 0 33.333%; max-width: 33.333%; }
16.col-5 { flex: 0 0 41.666%; max-width: 41.666%; }
17.col-6 { flex: 0 0 50%; max-width: 50%; }
18.col-7 { flex: 0 0 58.333%; max-width: 58.333%; }
19.col-8 { flex: 0 0 66.666%; max-width: 66.666%; }
20.col-9 { flex: 0 0 75%; max-width: 75%; }
21.col-10 { flex: 0 0 83.333%; max-width: 83.333%; }
22.col-11 { flex: 0 0 91.666%; max-width: 91.666%; }
23.col-12 { flex: 0 0 100%; max-width: 100%; }
24
25@media (max-width: 768px) {
26 .col-md-12 { flex: 0 0 100%; max-width: 100%; }
27 .col-md-6 { flex: 0 0 50%; max-width: 50%; }
28 .col-md-4 { flex: 0 0 33.333%; max-width: 33.333%; }
29}
30
31@media (max-width: 576px) {
32 .col-sm-12 { flex: 0 0 100%; max-width: 100%; }
33 .col-sm-6 { flex: 0 0 50%; max-width: 50%; }
34}Wykorzystanie w komponencie:
1// app/page.tsx
2export default function Home() {
3 return (
4 <div className="container">
5 <div className="row">
6 <div className="col col-4 col-md-6 col-sm-12">
7 <div className="card">Kolumna 1</div>
8 </div>
9 <div className="col col-4 col-md-6 col-sm-12">
10 <div className="card">Kolumna 2</div>
11 </div>
12 <div className="col col-4 col-md-12 col-sm-12">
13 <div className="card">Kolumna 3</div>
14 </div>
15 </div>
16 </div>
17 );
18}Przykład 2: Stylowanie formularzy
1/* Form.module.css */
2.form {
3 margin-bottom: 2rem;
4}
5
6.formGroup {
7 margin-bottom: 1rem;
8}
9
10.label {
11 display: block;
12 margin-bottom: 0.5rem;
13 font-weight: 500;
14}
15
16.input {
17 display: block;
18 width: 100%;
19 padding: 0.75rem;
20 font-size: 1rem;
21 border: 1px solid #ddd;
22 border-radius: var(--border-radius);
23 background-color: white;
24}
25
26.input:focus {
27 outline: none;
28 border-color: var(--primary-color);
29 box-shadow: 0 0 0 3px rgba(58, 134, 255, 0.2);
30}
31
32.error {
33 color: var(--accent-color);
34 font-size: 0.875rem;
35 margin-top: 0.25rem;
36}
37
38.submitButton {
39 composes: button from './Button.module.css';
40 composes: primary from './Button.module.css';
41}Najlepsze praktyki
Utrzymuj spójną strukturę
- Globalne style w
app/globals.css - Moduły CSS obok komponentów
- Konsekwentne nazewnictwo plików (np.
ComponentName.module.css)
- Globalne style w
Używaj zmiennych CSS dla wartości powtarzających się
- Kolory
- Odstępy
- Rozmiary czcionek
- Zaokrąglenia
- Cienie
Twórz klasy użytkowe dla często używanych stylów
.text-center,.mb-2, itp.
Używaj komentarzy dla lepszej organizacji
1/* 2 * TYPOGRAPHY 3 * ------------- 4 */ 5 6/* 7 * LAYOUT 8 * ------------- 9 */Stosuj metodologie CSS (jeśli potrzebne)
- BEM (Block Element Modifier)
- SMACSS (Scalable and Modular Architecture for CSS)
- OOCSS (Object-Oriented CSS)
Preferuj kompozycję nad dziedziczeniem
- Łącz małe, jednocelowe klasy zamiast tworzyć duże, złożone selektory
Wydajność a stylowanie
Next.js 16 automatycznie optymalizuje CSS:
- Podczas produkcyjnej kompilacji CSS jest minimalizowany
- CSS Modules są przetwarzane i generowane są unikalne nazwy klas
- Style krytyczne są automatycznie wstrzykiwane do znacznika
<head>
W przypadku większych aplikacji, warto rozważyć:
Code splitting CSS
- CSS jest dzielony na mniejsze pliki i ładowany tylko wtedy, gdy jest potrzebny
- W Next.js dzieje się to automatycznie dla CSS Modules
Używanie narzędzi do eliminacji nieużywanego CSS
- PurgeCSS
- Unused CSS (ucss)
Rozbudowane przykłady i zastosowania
System komponentów z wykorzystaniem CSS Modules
Poniżej przedstawiamy przykład rozwiniętego systemu komponentów wykorzystujących CSS Modules w aplikacji Next.js 16:
- Struktura katalogów:
1components/
2├── ui/
3│ ├── Button/
4│ │ ├── Button.tsx
5│ │ └── Button.module.css
6│ ├── Card/
7│ │ ├── Card.tsx
8│ │ └── Card.module.css
9│ ├── Input/
10│ │ ├── Input.tsx
11│ │ └── Input.module.css
12│ └──
13├── layout/
14│ ├── Header/
15│ │ ├── Header.tsx
16│ │ └── Header.module.css
17│ ├── Footer/
18│ │ ├── Footer.tsx
19│ │ └── Footer.module.css
20│ └──
21└──- Komponent Button z różnymi wariantami:
1// components/ui/Button/Button.tsx
2import styles from './Button.module.css';
3import { ReactNode } from 'react';
4
5type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'outline' | 'text';
6type ButtonSize = 'sm' | 'md' | 'lg';
7
8interface ButtonProps {
9 variant?: ButtonVariant;
10 size?: ButtonSize;
11 children: ReactNode;
12 onClick?: () => void;
13 disabled?: boolean;
14 fullWidth?: boolean;
15 icon?: ReactNode;
16 iconPosition?: 'left' | 'right';
17 className?: string;
18}
19
20export function Button({
21 variant = 'primary',
22 size = 'md',
23 children,
24 onClick,
25 disabled = false,
26 fullWidth = false,
27 icon,
28 iconPosition = 'left',
29 className = '',
30}: ButtonProps) {
31 const buttonClasses = [
32 styles.button,
33 styles[variant],
34 styles[size],
35 fullWidth ? styles.fullWidth : '',
36 icon ? styles.hasIcon : '',
37 icon && iconPosition === 'right' ? styles.iconRight : '',
38 disabled ? styles.disabled : '',
39 className
40 ].filter(Boolean).join(' ');
41
42 return (
43 <button
44 className={buttonClasses}
45 onClick={onClick}
46 disabled={disabled}
47 >
48 {icon && iconPosition === 'left' && (
49 <span className={styles.icon}>{icon}</span>
50 )}
51 <span className={styles.text}>{children}</span>
52 {icon && iconPosition === 'right' && (
53 <span className={styles.icon}>{icon}</span>
54 )}
55 </button>
56 );
57}1/* components/ui/Button/Button.module.css */
2.button {
3 display: inline-flex;
4 align-items: center;
5 justify-content: center;
6 font-weight: 500;
7 border: none;
8 border-radius: var(--border-radius);
9 cursor: pointer;
10 transition: all 0.2s ease;
11 font-family: inherit;
12}
13
14/* Variants */
15.primary {
16 background-color: var(--primary-color);
17 color: white;
18}
19
20.primary:hover {
21 background-color: var(--primary-dark);
22}
23
24.secondary {
25 background-color: var(--secondary-color);
26 color: white;
27}
28
29.secondary:hover {
30 background-color: var(--secondary-dark);
31}
32
33.tertiary {
34 background-color: var(--tertiary-color);
35 color: white;
36}
37
38.tertiary:hover {
39 background-color: var(--tertiary-dark);
40}
41
42.outline {
43 background-color: transparent;
44 border: 1px solid var(--primary-color);
45 color: var(--primary-color);
46}
47
48.outline:hover {
49 background-color: rgba(58, 134, 255, 0.1);
50}
51
52.text {
53 background-color: transparent;
54 color: var(--primary-color);
55 padding-left: 0;
56 padding-right: 0;
57}
58
59.text:hover {
60 background-color: transparent;
61 color: var(--primary-dark);
62 text-decoration: underline;
63}
64
65/* Sizes */
66.sm {
67 font-size: 0.875rem;
68 padding: 0.5rem 1rem;
69}
70
71.md {
72 font-size: 1rem;
73 padding: 0.75rem 1.5rem;
74}
75
76.lg {
77 font-size: 1.125rem;
78 padding: 1rem 2rem;
79}
80
81/* Modifiers */
82.fullWidth {
83 width: 100%;
84}
85
86.hasIcon {
87 gap: 0.5rem;
88}
89
90.iconRight {
91 flex-direction: row-reverse;
92}
93
94.disabled {
95 opacity: 0.6;
96 cursor: not-allowed;
97}
98
99.disabled:hover {
100 background-color: var(--primary-color);
101}
102
103.outline.disabled:hover {
104 background-color: transparent;
105}
106
107.text.disabled:hover {
108 background-color: transparent;
109 text-decoration: none;
110}
111
112.icon {
113 display: flex;
114 align-items: center;
115 justify-content: center;
116}
117
118.button .text {
119 display: inline-block;
120}- Użycie komponentu Button:
1// app/examples/page.tsx
2import { Button } from '@/components/ui/Button/Button';
3import { FiDownload, FiHeart, FiArrowRight } from 'react-icons/fi';
4
5export default function ButtonExamplesPage() {
6 return (
7 <div className="container">
8 <h1>Przykłady przycisków</h1>
9
10 <h2>Warianty</h2>
11 <div className="button-group">
12 <Button variant="primary">Primary</Button>
13 <Button variant="secondary">Secondary</Button>
14 <Button variant="tertiary">Tertiary</Button>
15 <Button variant="outline">Outline</Button>
16 <Button variant="text">Text</Button>
17 </div>
18
19 <h2>Rozmiary</h2>
20 <div className="button-group">
21 <Button size="sm">Small</Button>
22 <Button size="md">Medium</Button>
23 <Button size="lg">Large</Button>
24 </div>
25
26 <h2>Z ikonami</h2>
27 <div className="button-group">
28 <Button icon={<FiDownload />}>Pobierz</Button>
29 <Button icon={<FiHeart />} variant="outline">Polub</Button>
30 <Button icon={<FiArrowRight />} iconPosition="right">
31 Przejdź dalej
32 </Button>
33 </div>
34
35 <h2>Pełna szerokość</h2>
36 <Button fullWidth>Przycisk na pełną szerokość</Button>
37
38 <h2>Wyłączony</h2>
39 <div className="button-group">
40 <Button disabled>Wyłączony przycisk</Button>
41 <Button variant="outline" disabled>Wyłączony outline</Button>
42 </div>
43 </div>
44 );
45}Podsumowanie
Stylowanie w Next.js 16 jest elastyczne i umożliwia stosowanie różnych podejść, od globalnych stylów po hermetyczne CSS Modules. Podobnie jak w Metropolis Quantum, gdzie estetyka całego miasta jest spójna, ale każda dzielnica zachowuje swoją unikalność, w aplikacjach Next.js możemy łączyć globalne reguły z lokalnymi stylami komponentów.
Najważniejsze punkty:
- Globalne style są idealne do definiowania ogólnych reguł, zmiennych i klas użytkowych
- CSS Modules zapewniają lokalne stylowanie bez konfliktów, idealne dla komponentów
- Next.js 16 wspiera również Sass, Styled JSX i biblioteki CSS-in-JS
- Dobre praktyki organizacji stylów zwiększają czytelność i ułatwiają utrzymanie kodu
W następnym rozdziale przyjrzymy się integracji z Tailwind CSS, popularnym frameworkiem CSS opartym na klasach użytkowych, który może znacząco przyspieszyć proces stylowania w Next.js 16.
Kod do tej lekcji: app/layout.tsx
1// Root Layout with Global Styles
2import { ReactNode } from 'react';
3import './globals.css';
4
5interface RootLayoutProps {
6 children: ReactNode;
7}
8
9export const metadata = {
10 title: 'Metropolis Quantum - Styling System',
11 description: 'CSS Modules and Global Styles in Next.js 16',
12};
13
14export default function RootLayout({ children }: RootLayoutProps) {
15 console.log("Styling system initialized");
16
17 return (
18 <html lang="pl" className="theme-system">
19 <body>
20 <div className="metropolis-app">
21 <header className="global-header">
22 <div className="header-container">
23 <h1 className="logo">Metropolis Quantum</h1>
24 <nav className="main-nav">
25 <a href="/" className="nav-link">Home</a>
26 <a href="/themes" className="nav-link">Themes</a>
27 <a href="/components" className="nav-link">Components</a>
28 <a href="/animations" className="nav-link">Animations</a>
29 </nav>
30 <button className="theme-toggle" onclick="toggleTheme()">
31 Dark Mode
32 </button>
33 </div>
34 </header>
35
36 <main className="main-content">
37 {children}
38 </main>
39
40 <footer className="global-footer">
41 <p>Styled with CSS Modules & Global CSS • Next.js 16</p>
42 </footer>
43 </div>
44 </body>
45 </html>
46 );
47}Widzisz błąd w tej lekcji?