CodeWorlds

Optymalizacja czcionek i komponent Font w Next.js 16

Kurs Next.js · Moduł 3: Stylowanie i optymalizacja wizualna

W Metropolis Quantum, miejscy projektanci wiedzą, że typografia jest kluczowym elementem tożsamości wizualnej miasta. Każdy znak, informacja czy interfejs miejskich terminali wykorzystuje staranne dobrane kroje pisma, które są nie tylko estetyczne, ale również zoptymalizowane pod kątem szybkości wyświetlania i czytelności na różnych urządzeniach. W świecie webowym, czcionki pełnią podobną rolę - są istotnym elementem projektowym, ale mogą też znacząco wpływać na wydajność stron internetowych.

Next.js 16 oferuje zoptymalizowany sposób zarządzania czcionkami poprzez moduł next/font, dostępny od Next.js 13. Ten rozdział poświęcimy poznaniu tego rozwiązania oraz najlepszym praktykom optymalizacji fontów w aplikacjach Next.js.

Dlaczego optymalizacja czcionek jest ważna?

Niestandardowe czcionki internetowe mogą znacząco wpłynąć na wydajność stron:

  1. Wolniejsze ładowanie strony - czcionki internetowe to dodatkowe zasoby, które muszą zostać pobrane
  2. Migotanie tekstu (FOUT - Flash of Unstyled Text) - gdy tekst początkowo wyświetla się domyślną czcionką, a następnie zmienia się na niestandardową po jej załadowaniu
  3. Niewidoczny tekst (FOIT - Flash of Invisible Text) - gdy tekst jest ukryty do momentu załadowania czcionki
  4. Impact na Core Web Vitals - zwłaszcza na Largest Contentful Paint (LCP) i Cumulative Layout Shift (CLS)

System fontów w Next.js 16 rozwiązuje te problemy, zapewniając:

  • Zero dodatkowych żądań HTTP do zewnętrznych serwerów
  • Automatyczne samohostowanie plików czcionek
  • Automatyczny system fallbacków
  • Brak przesunięć układu (layout shift)
  • Wbudowane podpowiedzi podmiany czcionek dla przeglądarek

Komponent Font w Next.js 16

Next.js 16 dostarcza moduł next/font, który obsługuje:

  • Google Fonts (next/font/google)
  • Lokalne pliki czcionek (next/font/local)

Google Fonts

Oto jak używać czcionek Google w Next.js 16:

1// app/fonts.ts
2import { Inter, Roboto_Mono } from 'next/font/google';
3
4// Zmienne eksportowe pozwalają na ponowne wykorzystanie tych samych instancji fontów
5export const inter = Inter({
6  subsets: ['latin'],
7  display: 'swap',
8});
9
10export const roboto_mono = Roboto_Mono({
11  subsets: ['latin'],
12  display: 'swap',
13  weight: ['400', '700'],
14});

Następnie możesz użyć tych czcionek w swoim layoucie:

1// app/layout.tsx
2import { inter } from './fonts';
3import './globals.css';
4import type { Metadata } from 'next';
5
6export const metadata: Metadata = {
7  title: 'Metropolis Quantum',
8  description: 'Oficjalny portal informacyjny miasta przyszłości',
9};
10
11export default function RootLayout({
12  children,
13}: {
14  children: React.ReactNode;
15}) {
16  return (
17    <html lang="pl" className={inter.className}>
18      <body>{children}</body>
19    </html>
20  );
21}

Lokalne pliki czcionek

Możesz również używać lokalnych plików czcionek, co jest zalecane w przypadku własnych, niestandardowych krojów pisma:

1// app/fonts.ts
2import localFont from 'next/font/local';
3
4export const quantumSans = localFont({
5  src: [
6    {
7      path: '../public/fonts/QuantumSans-Regular.woff2',
8      weight: '400',
9      style: 'normal',
10    },
11    {
12      path: '../public/fonts/QuantumSans-Bold.woff2',
13      weight: '700',
14      style: 'normal',
15    },
16    {
17      path: '../public/fonts/QuantumSans-Italic.woff2',
18      weight: '400',
19      style: 'italic',
20    },
21  ],
22  display: 'swap',
23  variable: '--font-quantum-sans',
24});

Opcje konfiguracji fontów

Komponent Font w Next.js 16 oferuje szereg opcji konfiguracyjnych:

Dla Google Fonts:

1import { Roboto } from 'next/font/google';
2
3const roboto = Roboto({
4  weight: ['400', '700'],  // Wybranie konkretnych wag (grubości) czcionki
5  style: ['normal', 'italic'],  // Wybranie stylów czcionki
6  subsets: ['latin'],  // Wybranie podzbiorów znaków
7  display: 'swap',  // Strategia wyświetlania: 'auto', 'block', 'swap', 'fallback', 'optional'
8  preload: true,  // Czy preładować czcionkę
9  fallback: ['system-ui', 'Arial'],  // Czcionki zapasowe
10  adjustFontFallback: true,  // Automatyczne dostosowywanie czcionek zapasowych
11  variable: '--font-roboto',  // Zmienna CSS, jeśli używamy API zmiennych
12});

Dla lokalnych czcionek:

1import localFont from 'next/font/local';
2
3const quantumDisplay = localFont({
4  src: [
5    {
6      path: './fonts/QuantumDisplay-Regular.woff2',
7      weight: '400',
8      style: 'normal',
9    },
10    {
11      path: './fonts/QuantumDisplay-Bold.woff2',
12      weight: '700',
13      style: 'normal',
14    },
15  ],
16  display: 'swap',
17  preload: true,
18  variable: '--font-quantum-display',
19  fallback: ['system-ui', 'Arial'],
20  adjustFontFallback: true,
21});

Używanie fontów w aplikacji

1. Globalne użycie dla całej aplikacji

1// app/layout.tsx
2import { quantumSans } from './fonts';
3import './globals.css';
4
5export default function RootLayout({
6  children,
7}: {
8  children: React.ReactNode;
9}) {
10  return (
11    <html lang="pl" className={quantumSans.className}>
12      <body>{children}</body>
13    </html>
14  );
15}

2. Używanie zmiennych CSS dla większej elastyczności

1// app/fonts.ts
2import { Inter, Roboto_Mono } from 'next/font/google';
3
4export const inter = Inter({
5  subsets: ['latin'],
6  display: 'swap',
7  variable: '--font-inter',
8});
9
10export const roboto_mono = Roboto_Mono({
11  subsets: ['latin'],
12  display: 'swap',
13  weight: ['400', '700'],
14  variable: '--font-roboto-mono',
15});
1// app/layout.tsx
2import { inter, roboto_mono } from './fonts';
3import './globals.css';
4
5export default function RootLayout({
6  children,
7}: {
8  children: React.ReactNode;
9}) {
10  return (
11    <html lang="pl" className={`${inter.variable} ${roboto_mono.variable}`}>
12      <body>{children}</body>
13    </html>
14  );
15}
1/* app/globals.css */
2:root {
3  --font-inter: 'Inter', system-ui, sans-serif;
4  --font-roboto-mono: 'Roboto Mono', monospace;
5}
6
7body {
8  font-family: var(--font-inter);
9}
10
11code {
12  font-family: var(--font-roboto-mono);
13}

3. Używanie dla konkretnych komponentów

1// components/Heading.tsx
2import { quantumDisplay } from '@/app/fonts';
3
4export function Heading({ children }: { children: React.ReactNode }) {
5  return (
6    <h1 className={quantumDisplay.className}>
7      {children}
8    </h1>
9  );
10}

Zaawansowane techniki optymalizacji czcionek

1. Optymalizacja subsets dla różnych języków

Jeśli Twoja aplikacja obsługuje wiele języków, możesz zoptymalizować pliki fontów, ładując tylko potrzebne podzestawy znaków:

1import { Roboto } from 'next/font/google';
2
3// Dla strony po polsku
4export const robotoPolish = Roboto({
5  subsets: ['latin', 'latin-ext'],  // latin-ext zawiera znaki używane w języku polskim
6  weight: ['400', '700'],
7});
8
9// Dla strony po japońsku
10export const robotoJapanese = Roboto({
11  subsets: ['latin', 'japanese'],
12  weight: ['400', '700'],
13});

2. Używanie czcionek zmiennych (variable fonts)

Czcionki zmienne to nowoczesny format fontów, który pozwala na zapisanie wielu stylów i wag w jednym pliku, co znacznie zmniejsza ilość danych do pobrania:

1import { Roboto_Flex } from 'next/font/google';
2
3export const roboto = Roboto_Flex({
4  subsets: ['latin'],
5  display: 'swap',
6  // Variable fonts często oferują szerszy zakres wag
7});

3. Preload vs. Lazy loading

Domyślnie Next.js preładuje czcionki, ale możesz wyłączyć tę funkcję dla czcionek, które nie są używane natychmiast:

1import { Roboto } from 'next/font/google';
2
3// Główna czcionka, preładowana
4export const robotoMain = Roboto({
5  subsets: ['latin'],
6  weight: ['400', '700'],
7  preload: true,
8});
9
10// Czcionka używana tylko w niektórych komponentach, bez preładowania
11export const robotoSpecial = Roboto({
12  subsets: ['latin'],
13  weight: ['900'],
14  preload: false,
15});

4. Strategie wyświetlania (font-display)

Właściwość display określa, jak przeglądarka powinna obsługiwać etap ładowania czcionki:

  • swap (domyślne): Natychmiast pokazuje tekst używając czcionki fallback, zamienia ją na niestandardową czcionkę, gdy ta się załaduje.
  • block: Krótko ukrywa tekst, a następnie pokazuje go z czcionką fallback, zamienia na docelową po załadowaniu.
  • fallback: Podobne do swap, ale z krótkim okresem ukrytego tekstu. Jeśli ładowanie trwa zbyt długo, używana jest czcionka fallback na czas sesji.
  • optional: Podobne do fallback, ale przeglądarka może całkowicie pominąć niestandardową czcionkę, bazując na warunkach sieciowych.
1import { Roboto } from 'next/font/google';
2
3// Dla głównego tekstu, używamy swap, aby zapewnić natychmiastową czytelność
4export const robotoBody = Roboto({
5  subsets: ['latin'],
6  weight: ['400'],
7  display: 'swap',
8});
9
10// Dla ozdobnych nagłówków, możemy użyć optional, gdyż nie są one krytyczne
11export const robotoHeading = Roboto({
12  subsets: ['latin'],
13  weight: ['900'],
14  display: 'optional',
15});

Implementacja w różnych układach

1. Podstawowy układ z dwiema czcionkami

1// app/fonts.ts
2import { Inter, Playfair_Display } from 'next/font/google';
3
4export const inter = Inter({
5  subsets: ['latin'],
6  display: 'swap',
7  variable: '--font-sans',
8});
9
10export const playfair = Playfair_Display({
11  subsets: ['latin'],
12  display: 'swap',
13  variable: '--font-serif',
14});
1// app/layout.tsx
2import { inter, playfair } from './fonts';
3import './globals.css';
4
5export default function RootLayout({
6  children,
7}: {
8  children: React.ReactNode;
9}) {
10  return (
11    <html lang="pl" className={`${inter.variable} ${playfair.variable}`}>
12      <body>
13        <header className={playfair.className}>
14          <h1>Metropolis Quantum</h1>
15        </header>
16        <main className={inter.className}>
17          {children}
18        </main>
19      </body>
20    </html>
21  );
22}
1/* app/globals.css */
2:root {
3  --font-sans: 'Inter', system-ui, sans-serif;
4  --font-serif: 'Playfair Display', Georgia, serif;
5}
6
7h1, h2, h3, h4, h5, h6 {
8  font-family: var(--font-serif);
9}
10
11body {
12  font-family: var(--font-sans);
13}

2. Dashboard z czcionką monospacjową dla danych

1// app/dashboard/layout.tsx
2import { roboto_mono } from '../fonts';
3
4export default function DashboardLayout({
5  children,
6}: {
7  children: React.ReactNode;
8}) {
9  return (
10    <div className="dashboard-layout">
11      <div className="sidebar">
12        {/* Sidebar content */}
13      </div>
14      <div className={`content ${roboto_mono.className}`}>
15        {children}
16      </div>
17    </div>
18  );
19}

3. System komponentów typograficznych

1// components/Typography.tsx
2import { ReactNode } from 'react';
3import { inter, playfair, roboto_mono } from '@/app/fonts';
4
5type FontVariant = 'sans' | 'serif' | 'mono';
6
7interface TypographyProps {
8  children: ReactNode;
9  variant?: FontVariant;
10  as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span' | 'div';
11  className?: string;
12}
13
14export function Typography({
15  children,
16  variant = 'sans',
17  as: Component = 'p',
18  className = '',
19}: TypographyProps) {
20  let fontClass = '';
21
22  switch (variant) {
23    case 'sans':
24      fontClass = inter.className;
25      break;
26    case 'serif':
27      fontClass = playfair.className;
28      break;
29    case 'mono':
30      fontClass = roboto_mono.className;
31      break;
32  }
33
34  return (
35    <Component className={`${fontClass} ${className}`}>
36      {children}
37    </Component>
38  );
39}

Przykład użycia:

1// app/page.tsx
2import { Typography } from '@/components/Typography';
3
4export default function HomePage() {
5  return (
6    <div>
7      <Typography as="h1" variant="serif" className="text-4xl mb-4">
8        Witaj w Metropolis Quantum
9      </Typography>
10
11      <Typography variant="sans" className="mb-6">
12        Miasto przyszłości, w którym technologia spotyka się z codziennością.
13      </Typography>
14
15      <div className="code-example">
16        <Typography variant="mono" className="text-sm">
17          console.log("Hello, Quantum World!");
18        </Typography>
19      </div>
20    </div>
21  );
22}

Wpływ na Core Web Vitals

Odpowiednia implementacja fontów ma bezpośredni wpływ na kluczowe metryki wydajności:

Largest Contentful Paint (LCP)

Czcionki niestandardowe mogą opóźnić rendering największego elementu treści. Komponent Font w Next.js 16 minimalizuje ten problem przez:

  • Automatyczne samohostowanie fontów
  • Preładowanie krytycznych fontów
  • Strategie wyświetlania umożliwiające natychmiastowe wyświetlanie tekstu

Cumulative Layout Shift (CLS)

Automatyczne obliczanie rozmiarów fontów fallbackowych minimalizuje przesunięcia układu, gdy niestandardowa czcionka jest ostatecznie ładowana.

First Input Delay (FID) i Interaction to Next Paint (INP)

Czcionki nie mają bezpośredniego wpływu na te metryki, ale efektywne ładowanie fontów może zmniejszyć obciążenie wątku głównego przeglądarki.

Analizowanie wydajności czcionek

1. Lighthouse

Używaj narzędzia Lighthouse do analizy wydajności czcionek:

  1. Otwórz narzędzia deweloperskie Chrome (F12)
  2. Przejdź do zakładki "Lighthouse"
  3. Zaznacz "Performance" i uruchom test
  4. Sprawdź sekcję "Opportunities" i "Diagnostics" pod kątem potencjalnych problemów z czcionkami

2. Network panel w DevTools

Analizuj ładowanie czcionek w zakładce Network:

  1. Filtruj po "Font" lub "Woff2"
  2. Sprawdź czas pobierania, rozmiar plików i priorytet ładowania

3. WebPageTest

Narzędzie WebPageTest (webpagetest.org) oferuje szczegółową analizę ładowania zasobów, w tym czcionek, na różnych urządzeniach i łączach internetowych.

Najlepsze praktyki

  1. Używaj komponentu Font z Next.js 16 zamiast importowania czcionek bezpośrednio z Google Fonts lub innych zewnętrznych źródeł
  2. Ogranicz liczbę używanych fontów do 2-3 rodzin (np. jedna dla nagłówków, jedna dla tekstu, ewentualnie jedna monospacjowa)
  3. Wybieraj tylko potrzebne wagi i style - każda dodatkowa waga to dodatkowe dane do pobrania
  4. Używaj subsets specyficznych dla języków Twojej aplikacji
  5. Rozważ czcionki zmienne dla rodzin z wieloma wagami
  6. Dobieraj odpowiednią strategię wyświetlania (display) w zależności od przeznaczenia czcionki
  7. Używaj zmiennych CSS dla większej elastyczności w stylizacji
  8. Testuj wydajność na różnych urządzeniach i połączeniach

Przykład kompleksowej implementacji

Poniżej znajduje się przykład kompleksowej implementacji systemu fontów w aplikacji Next.js 16:

1// app/fonts.ts
2import { Inter, Playfair_Display, Roboto_Mono } from 'next/font/google';
3import localFont from 'next/font/local';
4
5// Główna czcionka dla tekstu
6export const inter = Inter({
7  subsets: ['latin', 'latin-ext'],
8  display: 'swap',
9  variable: '--font-sans',
10  weight: ['400', '500', '700'],
11});
12
13// Czcionka dla nagłówków
14export const playfair = Playfair_Display({
15  subsets: ['latin', 'latin-ext'],
16  display: 'optional',
17  variable: '--font-serif',
18  weight: ['700', '900'],
19});
20
21// Czcionka dla kodu
22export const roboto_mono = Roboto_Mono({
23  subsets: ['latin'],
24  display: 'swap',
25  variable: '--font-mono',
26  weight: ['400', '700'],
27});
28
29// Własna, niestandardowa czcionka (logo i wyróżnione elementy)
30export const quantumDisplay = localFont({
31  src: [
32    {
33      path: '../public/fonts/QuantumDisplay-Regular.woff2',
34      weight: '400',
35      style: 'normal',
36    },
37    {
38      path: '../public/fonts/QuantumDisplay-Bold.woff2',
39      weight: '700',
40      style: 'normal',
41    },
42  ],
43  display: 'swap',
44  variable: '--font-quantum',
45});
1// app/layout.tsx
2import { inter, playfair, roboto_mono, quantumDisplay } from './fonts';
3import './globals.css';
4
5export default function RootLayout({
6  children,
7}: {
8  children: React.ReactNode;
9}) {
10  return (
11    <html lang="pl" className={`
12      ${inter.variable}
13      ${playfair.variable}
14      ${roboto_mono.variable}
15      ${quantumDisplay.variable}
16    `}>
17      <body className={inter.className}>{children}</body>
18    </html>
19  );
20}
1/* app/globals.css */
2:root {
3  --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
4  --font-serif: 'Playfair Display', Georgia, Cambria, 'Times New Roman', Times, serif;
5  --font-mono: 'Roboto Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
6  --font-quantum: 'Quantum Display', var(--font-sans);
7
8  /* Rozmiary fontów */
9  --fs-xs: 0.75rem;
10  --fs-sm: 0.875rem;
11  --fs-base: 1rem;
12  --fs-lg: 1.125rem;
13  --fs-xl: 1.25rem;
14  --fs-2xl: 1.5rem;
15  --fs-3xl: 1.875rem;
16  --fs-4xl: 2.25rem;
17  --fs-5xl: 3rem;
18
19  /* Line-heights */
20  --lh-tight: 1.25;
21  --lh-snug: 1.375;
22  --lh-normal: 1.5;
23  --lh-relaxed: 1.625;
24  --lh-loose: 2;
25}
26
27body {
28  font-family: var(--font-sans);
29  font-size: var(--fs-base);
30  line-height: var(--lh-normal);
31}
32
33h1, h2, h3, h4, h5, h6 {
34  font-family: var(--font-serif);
35  line-height: var(--lh-tight);
36  font-weight: 700;
37}
38
39h1 {
40  font-size: var(--fs-4xl);
41  margin-bottom: 1.5rem;
42}
43
44h2 {
45  font-size: var(--fs-3xl);
46  margin-bottom: 1.25rem;
47}
48
49.logo {
50  font-family: var(--font-quantum);
51  font-weight: 700;
52}
53
54code, pre {
55  font-family: var(--font-mono);
56  font-size: var(--fs-sm);
57}
58
59.title-quantum {
60  font-family: var(--font-quantum);
61  font-weight: 700;
62  letter-spacing: -0.025em;
63}

Typowe problemy i ich rozwiązania

Problem 1: FOUT (Flash of Unstyled Text)

Rozwiązanie: Używaj display: 'swap' i upewnij się, że czcionki są preładowane.

Problem 2: Duży rozmiar plików czcionek

Rozwiązanie:

  • Ograniczaj liczbę wag i stylów
  • Używaj tylko niezbędnych subsetów
  • Rozważ czcionki zmienne
  • Optymalizuj pliki WOFF2 (Next.js robi to automatycznie)

Problem 3: Czcionki opóźniają ładowanie strony

Rozwiązanie:

  • Dla mniej krytycznych czcionek używaj display: 'optional'
  • Dla czcionek używanych tylko w określonych komponentach wyłącz preładowanie

Problem 4: Różnice w wyglądzie czcionek fallbackowych i niestandardowych

Rozwiązanie: Next.js 16 automatycznie dopasowuje metryki czcionek fallbackowych do docelowych czcionek, minimalizując przesunięcia. Możesz też użyć adjustFontFallback: true.

Kompatybilność z przeglądarkami

Komponent Font w Next.js 16 automatycznie dostarcza czcionki w formacie WOFF2, który jest wspierany przez wszystkie nowoczesne przeglądarki. Dla starszych przeglądarek, które nie obsługują WOFF2, wyświetlane są czcionki fallbackowe zdefiniowane w konfiguracji.

Wsparcie dla języków z niestandardowymi systemami pisma

Przy pracy z aplikacjami wielojęzycznymi, pamiętaj o:

  1. Odpowiednich subsetach dla każdego języka
  2. Większych rozmiarach plików dla języków z rozbudowanymi systemami znaków (np. chiński, japoński)
  3. Wydajności renderingu - niektóre złożone systemy pisma mogą obciążać przeglądarkę
1// Przykład dla aplikacji wielojęzycznej
2import { Noto_Sans_JP, Noto_Sans } from 'next/font/google';
3
4// Dla języka łacińskiego (np. angielski, polski)
5export const notoSansLatin = Noto_Sans({
6  subsets: ['latin', 'latin-ext'],
7  weight: ['400', '700'],
8  variable: '--font-latin',
9});
10
11// Dla języka japońskiego
12export const notoSansJP = Noto_Sans_JP({
13  subsets: ['japanese'],
14  weight: ['400', '700'],
15  variable: '--font-japanese',
16});

Podsumowanie

Komponent Font w Next.js 16 zapewnia kompleksowe rozwiązanie dla zarządzania i optymalizacji czcionek w aplikacjach webowych:

  1. Automatyczne samohostowanie - eliminuje zewnętrzne żądania HTTP
  2. Zoptymalizowany format WOFF2 - mniejsze pliki do pobrania
  3. Zapobieganie layout shift - lepsze CLS
  4. Strategie wyświetlania - kontrola nad sposobem ładowania czcionek
  5. Preload i lazy loading - optymalne wykorzystanie przepustowości sieci

Podobnie jak w Metropolis Quantum, gdzie każdy aspekt wizualnej komunikacji jest starannie zaprojektowany i zoptymalizowany, dobrze zaimplementowany system czcionek w Next.js 16 zapewnia idealną równowagę między estetyką a wydajnością. Dzięki komponentowi Font, aplikacje Next.js mogą oferować spójne, piękne i efektywne doświadczenie typograficzne bez uszczerbku dla wydajności.

W następnym rozdziale omówimy zarządzanie zasobami statycznymi w katalogu public, co pozwoli nam lepiej zrozumieć, jak efektywnie organizować i obsługiwać różne typy zasobów w aplikacjach Next.js 16.

Kod do tej lekcji: app/components/DesignSystem.tsx
1// Design System - Quantum UI Library
2'use client';
3
4import styles from './DesignSystem.module.css';
5
6// Design Tokens jako const
7export const tokens = {
8  colors: {
9    primary: { main: '#64ffda', light: '#96ffe6', dark: '#32cc99' },
10    secondary: { main: '#448aff', light: '#80b3ff', dark: '#0056cc' },
11    accent: { main: '#7c4dff', light: '#b47cff', dark: '#4400cc' },
12    neutral: { gray900: '#0f0f23', gray700: '#1a1a2e', gray500: '#3d3d55' }
13  },
14  spacing: { xs: '0.25rem', sm: '0.5rem', md: '1rem', lg: '1.5rem', xl: '2rem' },
15  typography: {
16    fontFamily: { primary: 'Inter, sans-serif', mono: 'Fira Code, monospace' },
17    fontSize: { sm: '0.875rem', base: '1rem', lg: '1.125rem', xl: '1.25rem', '2xl': '1.5rem' },
18    fontWeight: { regular: 400, medium: 500, semibold: 600, bold: 700 }
19  },
20  borderRadius: { sm: '0.25rem', md: '0.5rem', lg: '0.75rem', xl: '1rem' },
21  shadows: {
22    sm: '0 2px 8px rgba(0,0,0,0.15)',
23    md: '0 4px 16px rgba(0,0,0,0.25)',
24    lg: '0 8px 32px rgba(0,0,0,0.35)',
25    neon: '0 0 20px rgba(100,255,218,0.5)'
26  }
27};
28
29export default function DesignSystemDemo() {
30  return (
31    <div className={styles.container}>
32      <h1 className={styles.title}>Quantum Design System</h1>
33
34      {/* Color Palette */}
35      <section className={styles.section}>
36        <h2 className={styles.sectionTitle}>Color Palette</h2>
37        <div className={styles.colorGrid}>
38          <div className={styles.colorCard}>
39            <div className={styles.colorSwatch} style={{background: tokens.colors.primary.main}}></div>
40            <p>Primary</p>
41            <code>{tokens.colors.primary.main}</code>
42          </div>
43          <div className={styles.colorCard}>
44            <div className={styles.colorSwatch} style={{background: tokens.colors.secondary.main}}></div>
45            <p>Secondary</p>
46            <code>{tokens.colors.secondary.main}</code>
47          </div>
48          <div className={styles.colorCard}>
49            <div className={styles.colorSwatch} style={{background: tokens.colors.accent.main}}></div>
50            <p>Accent</p>
51            <code>{tokens.colors.accent.main}</code>
52          </div>
53        </div>
54      </section>
55
56      {/* Typography */}
57      <section className={styles.section}>
58        <h2 className={styles.sectionTitle}>Typography Scale</h2>
59        <div className={styles.typographyDemo}>
60          <p style={{fontSize: tokens.typography.fontSize['2xl'], fontWeight: tokens.typography.fontWeight.bold}}>
61            Heading 2XL
62          </p>
63          <p style={{fontSize: tokens.typography.fontSize.xl, fontWeight: tokens.typography.fontWeight.semibold}}>
64            Heading XL
65          </p>
66          <p style={{fontSize: tokens.typography.fontSize.lg, fontWeight: tokens.typography.fontWeight.medium}}>
67            Heading LG
68          </p>
69          <p style={{fontSize: tokens.typography.fontSize.base}}>
70            Body Text Base
71          </p>
72          <p style={{fontSize: tokens.typography.fontSize.sm}}>
73            Small Text
74          </p>
75        </div>
76      </section>
77
78      {/* Component Library */}
79      <section className={styles.section}>
80        <h2 className={styles.sectionTitle}>Component Library</h2>
81
82        {/* Buttons */}
83        <div className={styles.componentGroup}>
84          <h3>Buttons</h3>
85          <div className={styles.buttonShowcase}>
86            <button className={styles.btnPrimary}>Primary</button>
87            <button className={styles.btnSecondary}>Secondary</button>
88            <button className={styles.btnOutline}>Outline</button>
89            <button className={styles.btnGhost}>Ghost</button>
90          </div>
91        </div>
92
93        {/* Cards */}
94        <div className={styles.componentGroup}>
95          <h3>Cards</h3>
96          <div className={styles.cardShowcase}>
97            <div className={styles.card}>
98              <h4>Standard Card</h4>
99              <p>This is a standard card component with consistent padding and styling.</p>
100            </div>
101            <div className={styles.cardElevated}>
102              <h4>Elevated Card</h4>
103              <p>This card has elevated shadow for emphasis.</p>
104            </div>
105            <div className={styles.cardNeon}>
106              <h4>Neon Card</h4>
107              <p>Cyberpunk-styled card with neon glow effect.</p>
108            </div>
109          </div>
110        </div>
111
112        {/* Form Elements */}
113        <div className={styles.componentGroup}>
114          <h3>Form Elements</h3>
115          <div className={styles.formShowcase}>
116            <input type="text" placeholder="Text Input" className={styles.input} />
117            <select className={styles.select}>
118              <option>Select Option</option>
119              <option>Option 1</option>
120              <option>Option 2</option>
121            </select>
122            <textarea placeholder="Textarea" className={styles.textarea}></textarea>
123          </div>
124        </div>
125      </section>
126
127      {/* Spacing System */}
128      <section className={styles.section}>
129        <h2 className={styles.sectionTitle}>Spacing System</h2>
130        <div className={styles.spacingDemo}>
131          {Object.entries(tokens.spacing).map(([key, value]) => (
132            <div key={key} className={styles.spacingItem}>
133              <div className={styles.spacingBar} style={{width: value}}></div>
134              <span>{key}: {value}</span>
135            </div>
136          ))}
137        </div>
138      </section>
139
140      {/* Shadow System */}
141      <section className={styles.section}>
142        <h2 className={styles.sectionTitle}>Shadow System</h2>
143        <div className={styles.shadowGrid}>
144          <div className={styles.shadowBox} style={{boxShadow: tokens.shadows.sm}}>Small</div>
145          <div className={styles.shadowBox} style={{boxShadow: tokens.shadows.md}}>Medium</div>
146          <div className={styles.shadowBox} style={{boxShadow: tokens.shadows.lg}}>Large</div>
147          <div className={styles.shadowBox} style={{boxShadow: tokens.shadows.neon}}>Neon</div>
148        </div>
149      </section>
150    </div>
151  );
152}

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. CSS Grid służy do:

  2. 2. Flexbox najlepiej nadaje się do:

Zadania praktyczne w grze

  • Klikanie w kolejności

    Ułóż składnię klasy Tailwind z responsive breakpoint:

  • Edytor kodu

    Utwórz card component używając tylko klas Tailwind CSS (padding, shadow, border-radius, etc.)

  • Układanie w poziomie

    Ułóż składnię importu CSS Module w Next.js:

  • Klikanie w kolejności

    Ułóż składnię importu CSS Module w Next.js

  • Edytor kodu

    Utwórz CSS z techniques optymalizacji: will-change, transform, contain, content-visibility

Przydatne artykuły