Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Integracja z Tailwind CSS i konfiguracja w Next.js 15

W Metropolis Quantum, najnowocześniejszym mieście przyszłości, inżynierowie opracowali modułowy system budowy, który pozwala tworzyć zaawansowane struktury z gotowych, prefabrykowanych komponentów. Ten system umożliwia szybkie wznoszenie budynków, przy jednoczesnym zachowaniu spójnej estetyki całego miasta. W świecie programowania frontendowego, Tailwind CSS pełni podobną rolę - jest to narzędzie, które pozwala na szybkie tworzenie interfejsów użytkownika za pomocą klas użytkowych.

Czym jest Tailwind CSS?

Tailwind CSS to framework CSS typu "utility-first", który dostarcza zestaw klas użytkowych pozwalających na tworzenie projektów bezpośrednio w HTML, bez pisania CSS. Zamiast definiować własne klasy, używasz gotowych, niskopoziomowych klas, które odpowiadają konkretnym stylom CSS.

Na przykład, zamiast pisać:

1.button {
2  display: flex;
3  align-items: center;
4  padding: 0.5rem 1rem;
5  background-color: #3b82f6;
6  color: white;
7  border-radius: 0.25rem;
8}

W Tailwind używasz kombinacji klas:

1<button class="flex items-center px-4 py-2 bg-blue-500 text-white rounded">
2  Kliknij mnie
3</button>

Zalety używania Tailwind CSS

  1. Szybkość rozwoju - Pisanie styli bezpośrednio w HTML przyspiesza proces tworzenia interfejsu
  2. Spójność - Predefiniowane wartości dla kolorów, odstępów, rozmiarów czcionek zapewniają spójność projektową
  3. Responsywność - Wbudowane prefiksy dla różnych breakpointów (np.
    md:w-1/2
    )
  4. Konfigurowalność - Łatwe dostosowanie do potrzeb projektu przez modyfikację pliku konfiguracyjnego
  5. Mniejszy rozmiar produkcyjnego CSS - Generuje tylko style, które są faktycznie używane w projekcie
  6. Mniejsza bariera poznawcza - Nie trzeba wymyślać nazw klas
  7. Łatwość utrzymania - Modyfikacje bezpośrednio w HTML zamiast przełączania się między plikami HTML i CSS

Integracja Tailwind CSS z Next.js 15

Next.js 15 ma doskonałą integrację z Tailwind CSS. Oto jak skonfigurować Tailwind w projekcie Next.js 15:

Krok 1: Instalacja wymaganych pakietów

1npm install -D tailwindcss postcss autoprefixer

Krok 2: Inicjalizacja konfiguracji Tailwind

1npx tailwindcss init -p

To polecenie utworzy dwa pliki:

  • tailwind.config.js
    - konfiguracja Tailwind CSS
  • postcss.config.js
    - konfiguracja PostCSS

Krok 3: Konfiguracja ścieżek zawartości

Następnie należy zaktualizować plik

tailwind.config.js
, aby wskazać, gdzie Tailwind powinien szukać klas do uwzględnienia w ostatecznym CSS:

1// tailwind.config.js
2/** @type {import('tailwindcss').Config} */
3module.exports = {
4  content: [
5    "./app/**/*.{js,ts,jsx,tsx,mdx}",
6    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
7    "./components/**/*.{js,ts,jsx,tsx,mdx}",
8  ],
9  theme: {
10    extend: {},
11  },
12  plugins: [],
13}

Krok 4: Dodanie dyrektyw Tailwind do głównego pliku CSS

Teraz należy dodać dyrektywy Tailwind do głównego pliku CSS (zwykle

app/globals.css
):

1/* app/globals.css */
2@tailwind base;
3@tailwind components;
4@tailwind utilities;

Te dyrektywy importują:

  • @tailwind base
    - style resetujące i podstawowe
  • @tailwind components
    - style komponentów
  • @tailwind utilities
    - klasy użytkowe

Krok 5: Importowanie pliku CSS w głównym layoucie

Upewnij się, że

globals.css
jest importowany w głównym layoucie aplikacji:

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}

Krok 6: Użycie Tailwind w komponentach

Teraz możesz zacząć używać klas Tailwind w swoich komponentach:

1// app/page.tsx
2export default function Home() {
3  return (
4    <main className="min-h-screen bg-gray-100 py-12 px-4 sm:px-6 lg:px-8">
5      <div className="max-w-7xl mx-auto">
6        <h1 className="text-4xl font-bold text-gray-900 mb-6">
7          Witaj w Metropolis Quantum
8        </h1>
9        <p className="text-xl text-gray-700 mb-8">
10          Miasto przyszłości, gdzie innowacja spotyka się z tradycją.
11        </p>
12        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
13          <div className="bg-white rounded-lg shadow-md p-6">
14            <h2 className="text-2xl font-semibold text-gray-900 mb-4">
15              Kwantowa Mobilność
16            </h2>
17            <p className="text-gray-600">
18              Poznaj nasz zaawansowany system transportu, który łączy różne
19              wymiary przestrzeni.
20            </p>
21            <button className="mt-4 bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded transition duration-200">
22              Dowiedz się więcej
23            </button>
24          </div>
25          {/* Więcej kart */}
26        </div>
27      </div>
28    </main>
29  );
30}

Zaawansowana konfiguracja Tailwind CSS

Tailwind oferuje rozbudowane możliwości konfiguracji, które pozwalają dostosować framework do specyficznych potrzeb projektu.

Dostosowywanie motywu

Możesz dostosować kolory, odstępy, breakpointy i inne wartości w

tailwind.config.js
:

1// tailwind.config.js
2/** @type {import('tailwindcss').Config} */
3module.exports = {
4  content: [
5    "./app/**/*.{js,ts,jsx,tsx,mdx}",
6    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
7    "./components/**/*.{js,ts,jsx,tsx,mdx}",
8  ],
9  theme: {
10    extend: {
11      colors: {
12        quantum: {
13          50: '#f0f9ff',
14          100: '#e0f2fe',
15          200: '#bae6fd',
16          300: '#7dd3fc',
17          400: '#38bdf8',
18          500: '#0ea5e9',
19          600: '#0284c7',
20          700: '#0369a1',
21          800: '#075985',
22          900: '#0c4a6e',
23          950: '#082f49',
24        },
25        neon: {
26          500: '#39ff14',
27          600: '#32e613',
28        },
29      },
30      spacing: {
31        '72': '18rem',
32        '84': '21rem',
33        '96': '24rem',
34      },
35      borderRadius: {
36        'quantum': '0.75rem',
37      },
38      fontFamily: {
39        'quantum': ['Quantum Sans', 'sans-serif'],
40        'display': ['Quantum Display', 'sans-serif'],
41      },
42      boxShadow: {
43        'quantum': '0 4px 14px 0 rgba(0, 118, 255, 0.39)',
44      },
45    },
46    // Nadpisanie domyślnych wartości
47    screens: {
48      'sm': '640px',
49      'md': '768px',
50      'lg': '1024px',
51      'xl': '1280px',
52      '2xl': '1536px',
53    },
54  },
55  plugins: [],
56}

Używanie niestandardowych wartości

Po zdefiniowaniu niestandardowych wartości w konfiguracji, możesz ich używać w klasach:

1<button className="bg-quantum-600 hover:bg-quantum-700 text-white font-medium py-2 px-4 rounded-quantum shadow-quantum transition duration-200">
2  Kwantowa Akcja
3</button>

Prefiksy responsywne

Tailwind ma wbudowany system responsywności. Możesz używać prefiksów dla różnych breakpointów:

1<div className="w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/5">
2  Responsywny element
3</div>

Ten div będzie zajmował:

  • 100% szerokości na małych ekranach (domyślnie)
  • 50% szerokości na ekranach SM i większych
  • 33.33% szerokości na ekranach MD i większych
  • 25% szerokości na ekranach LG i większych
  • 20% szerokości na ekranach XL i większych

Dodawanie własnych klas użytkowych

Możesz definiować własne klasy użytkowe za pomocą dyrektywy

@layer utilities
:

1/* app/globals.css */
2@tailwind base;
3@tailwind components;
4@tailwind utilities;
5
6@layer utilities {
7  .text-shadow-quantum {
8    text-shadow: 0 0 8px rgba(0, 118, 255, 0.5);
9  }
10  .scrollbar-hide {
11    -ms-overflow-style: none;
12    scrollbar-width: none;
13  }
14  .scrollbar-hide::-webkit-scrollbar {
15    display: none;
16  }
17}

Definiowanie komponentów

Mimo że Tailwind jest frameworkiem typu "utility-first", czasami warto stworzyć powtarzalne komponenty. Możesz to zrobić za pomocą dyrektywy

@layer components
:

1/* app/globals.css */
2@tailwind base;
3@tailwind components;
4@tailwind utilities;
5
6@layer components {
7  .quantum-card {
8    @apply bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow duration-300;
9  }
10  .quantum-button {
11    @apply bg-quantum-600 hover:bg-quantum-700 text-white font-medium py-2 px-4 rounded-quantum shadow-quantum transition duration-200;
12  }
13  .quantum-input {
14    @apply w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-quantum-500 focus:border-transparent;
15  }
16}

Teraz możesz używać tych klas bezpośrednio:

1<div className="quantum-card">
2  <h2 className="text-2xl font-semibold mb-4">Mój komponent</h2>
3  <input type="text" className="quantum-input mb-4" />
4  <button className="quantum-button">Akcja</button>
5</div>

Dodawanie elementów bazowych

Możesz również dostosować style bazowe, używając dyrektywy

@layer base
:

1/* app/globals.css */
2@tailwind base;
3@tailwind components;
4@tailwind utilities;
5
6@layer base {
7  h1 {
8    @apply text-4xl font-bold mb-6;
9  }
10  h2 {
11    @apply text-3xl font-semibold mb-4;
12  }
13  h3 {
14    @apply text-2xl font-medium mb-3;
15  }
16  a {
17    @apply text-quantum-600 hover:text-quantum-800 hover:underline;
18  }
19}

Wtyczki Tailwind CSS

Tailwind ma bogaty ekosystem wtyczek, które dodają nowe funkcjonalności. Oto kilka popularnych:

1. @tailwindcss/forms

Dodaje lepsze style domyślne dla elementów formularzy.

1npm install -D @tailwindcss/forms
1// tailwind.config.js
2module.exports = {
3  //
4  plugins: [
5    require('@tailwindcss/forms'),
6    //
7  ],
8}

2. @tailwindcss/typography

Dodaje klasę

prose
, która zapewnia dobre style typograficzne dla zawartości generowanej z CMS, Markdown, itp.

1npm install -D @tailwindcss/typography
1// tailwind.config.js
2module.exports = {
3  //
4  plugins: [
5    require('@tailwindcss/typography'),
6    //
7  ],
8}

Użycie:

1<article className="prose lg:prose-xl">
2  <h1>Kwantowa teoria wszystkiego</h1>
3  <p>Długi artykuł z wieloma paragrafami, listami, itp.</p>
4  {/* ... */}
5</article>

3. @tailwindcss/aspect-ratio

Zapewnia utrzymanie proporcji elementów (np. dla obrazów, wideo).

1npm install -D @tailwindcss/aspect-ratio
1// tailwind.config.js
2module.exports = {
3  //
4  plugins: [
5    require('@tailwindcss/aspect-ratio'),
6    //
7  ],
8}

Użycie:

1<div className="aspect-w-16 aspect-h-9">
2  <iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen></iframe>
3</div>

4. @tailwindcss/line-clamp

Pozwala na ograniczenie liczby wierszy tekstu (np. do tytułów w kartach).

1npm install -D @tailwindcss/line-clamp
1// tailwind.config.js
2module.exports = {
3  //
4  plugins: [
5    require('@tailwindcss/line-clamp'),
6    //
7  ],
8}

Użycie:

1<p className="line-clamp-2">
2  Bardzo długi tekst, który zostanie przycięty po dwóch liniach i zakończony wielokropkiem...
3</p>

Optymalizacja Tailwind CSS dla produkcji

Tailwind generuje tylko style dla klas, które faktycznie używasz w swoim projekcie. To znaczy, że plik CSS używany w produkcji jest zazwyczaj bardzo mały. Jednak w trybie developerskim, Tailwind może generować duży plik CSS, co może spowalniać kompilację.

PurgeCSS (wbudowany)

W Tailwind CSS v2.0 i nowszych, PurgeCSS jest wbudowany. Konfiguracja

content
w
tailwind.config.js
informuje Tailwind, gdzie szukać klas do uwzględnienia w ostatecznym CSS:

1// tailwind.config.js
2module.exports = {
3  content: [
4    "./app/**/*.{js,ts,jsx,tsx,mdx}",
5    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6    "./components/**/*.{js,ts,jsx,tsx,mdx}",
7  ],
8  //
9}

Minifikacja CSS

Next.js automatycznie minifikuje CSS w trybie produkcyjnym.

Dzielenie kodu CSS

Next.js automatycznie dzieli CSS na mniejsze pliki i ładuje je tylko wtedy, gdy są potrzebne.

Praktyczne przykłady

Przykład 1: Komponent karty

1// components/QuantumCard.tsx
2import { ReactNode } from 'react';
3
4interface QuantumCardProps {
5  title: string;
6  description: string;
7  icon?: ReactNode;
8  actionText?: string;
9  onAction?: () => void;
10}
11
12export function QuantumCard({
13  title,
14  description,
15  icon,
16  actionText,
17  onAction,
18}: QuantumCardProps) {
19  return (
20    <div className="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow duration-300">
21      {icon && (
22        <div className="flex justify-center items-center w-12 h-12 rounded-full bg-quantum-100 text-quantum-600 mb-4">
23          {icon}
24        </div>
25      )}
26      <h3 className="text-xl font-semibold text-gray-900 mb-2">{title}</h3>
27      <p className="text-gray-600 mb-4">{description}</p>
28      {actionText && onAction && (
29        <button
30          onClick={onAction}
31          className="inline-flex items-center text-quantum-600 hover:text-quantum-800 font-medium"
32        >
33          {actionText}
34          <svg
35            className="ml-1 w-4 h-4"
36            fill="none"
37            stroke="currentColor"
38            viewBox="0 0 24 24"
39            xmlns="http://www.w3.org/2000/svg"
40          >
41            <path
42              strokeLinecap="round"
43              strokeLinejoin="round"
44              strokeWidth={2}
45              d="M9 5l7 7-7 7"
46            />
47          </svg>
48        </button>
49      )}
50    </div>
51  );
52}

Przykład 2: Komponent nawigacji

1// components/QuantumNav.tsx
2'use client';
3
4import { useState } from 'react';
5import Link from 'next/link';
6import { usePathname } from 'next/navigation';
7
8const navItems = [
9  { name: 'Strona główna', href: '/' },
10  { name: 'Funkcje', href: '/funkcje' },
11  { name: 'Cennik', href: '/cennik' },
12  { name: 'O nas', href: '/o-nas' },
13  { name: 'Kontakt', href: '/kontakt' },
14];
15
16export function QuantumNav() {
17  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
18  const pathname = usePathname();
19
20  return (
21    <nav className="bg-white shadow-sm">
22      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
23        <div className="flex justify-between h-16">
24          <div className="flex items-center">
25            <div className="flex-shrink-0 flex items-center">
26              <span className="text-quantum-600 font-display text-2xl font-bold">Quantum</span>
27            </div>
28            <div className="hidden sm:ml-6 sm:flex sm:space-x-8">
29              {navItems.map((item) => {
30                const isActive = pathname === item.href;
31                return (
32                  <Link
33                    key={item.name}
34                    href={item.href}
35                    className={`inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium ${
36                      isActive
37                        ? 'border-quantum-500 text-gray-900'
38                        : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'
39                    }`}
40                  >
41                    {item.name}
42                  </Link>
43                );
44              })}
45            </div>
46          </div>
47          <div className="hidden sm:ml-6 sm:flex sm:items-center">
48            <button className="bg-quantum-500 hover:bg-quantum-600 text-white py-2 px-4 rounded-md text-sm font-medium">
49              Rozpocznij
50            </button>
51          </div>
52          <div className="-mr-2 flex items-center sm:hidden">
53            <button
54              type="button"
55              className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-quantum-500"
56              aria-controls="mobile-menu"
57              aria-expanded="false"
58              onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
59            >
60              <span className="sr-only">Otwórz menu główne</span>
61              {/* Ikona hamburgera */}
62              <svg
63                className={`${mobileMenuOpen ? 'hidden' : 'block'} h-6 w-6`}
64                xmlns="http://www.w3.org/2000/svg"
65                fill="none"
66                viewBox="0 0 24 24"
67                stroke="currentColor"
68                aria-hidden="true"
69              >
70                <path
71                  strokeLinecap="round"
72                  strokeLinejoin="round"
73                  strokeWidth={2}
74                  d="M4 6h16M4 12h16M4 18h16"
75                />
76              </svg>
77              {/* Ikona X */}
78              <svg
79                className={`${mobileMenuOpen ? 'block' : 'hidden'} h-6 w-6`}
80                xmlns="http://www.w3.org/2000/svg"
81                fill="none"
82                viewBox="0 0 24 24"
83                stroke="currentColor"
84                aria-hidden="true"
85              >
86                <path
87                  strokeLinecap="round"
88                  strokeLinejoin="round"
89                  strokeWidth={2}
90                  d="M6 18L18 6M6 6l12 12"
91                />
92              </svg>
93            </button>
94          </div>
95        </div>
96      </div>
97
98      {/* Menu mobilne */}
99      <div
100        className={`${mobileMenuOpen ? 'block' : 'hidden'} sm:hidden`}
101        id="mobile-menu"
102      >
103        <div className="pt-2 pb-3 space-y-1">
104          {navItems.map((item) => {
105            const isActive = pathname === item.href;
106            return (
107              <Link
108                key={item.name}
109                href={item.href}
110                className={`block pl-3 pr-4 py-2 border-l-4 text-base font-medium ${
111                  isActive
112                    ? 'bg-quantum-50 border-quantum-500 text-quantum-700'
113                    : 'border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800'
114                }`}
115              >
116                {item.name}
117              </Link>
118            );
119          })}
120        </div>
121        <div className="pt-4 pb-3 border-t border-gray-200">
122          <div className="mt-3 px-2 space-y-1">
123            <button className="w-full block bg-quantum-500 hover:bg-quantum-600 text-white py-2 px-4 rounded-md text-base font-medium">
124              Rozpocznij
125            </button>
126          </div>
127        </div>
128      </div>
129    </nav>
130  );
131}

Przykład 3: Zakładka ustawień profilu z Tailwind

1// app/dashboard/profile/page.tsx
2'use client';
3
4import { useState } from 'react';
5
6type Tab = 'general' | 'security' | 'notifications' | 'billing';
7
8export default function ProfilePage() {
9  const [activeTab, setActiveTab] = useState<Tab>('general');
10
11  const tabs = [
12    { id: 'general', name: 'Informacje ogólne' },
13    { id: 'security', name: 'Bezpieczeństwo' },
14    { id: 'notifications', name: 'Powiadomienia' },
15    { id: 'billing', name: 'Płatności' },
16  ];
17
18  return (
19    <div className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
20      <div className="md:grid md:grid-cols-3 md:gap-6">
21        <div className="md:col-span-1">
22          <div className="px-4 sm:px-0">
23            <h2 className="text-lg font-medium leading-6 text-gray-900">Profil</h2>
24            <p className="mt-1 text-sm text-gray-600">
25              Zarządzaj swoim profilem i ustawieniami w systemie Metropolis Quantum.
26            </p>
27          </div>
28          
29          <div className="mt-6">
30            <nav className="space-y-1" aria-label="Tabs">
31              {tabs.map((tab) => (
32                <button
33                  key={tab.id}
34                  onClick={() => setActiveTab(tab.id as Tab)}
35                  className={`${
36                    activeTab === tab.id
37                      ? 'bg-quantum-50 border-quantum-500 text-quantum-700'
38                      : 'border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800'
39                  } w-full flex items-center px-3 py-2 text-sm font-medium border-l-4`}
40                  aria-current={activeTab === tab.id ? 'page' : undefined}
41                >
42                  {tab.name}
43                </button>
44              ))}
45            </nav>
46          </div>
47        </div>
48        
49        <div className="mt-5 md:mt-0 md:col-span-2">
50          <div className="bg-white shadow sm:rounded-lg">
51            {activeTab === 'general' && (
52              <div className="p-6">
53                <h3 className="text-lg font-medium leading-6 text-gray-900 mb-4">
54                  Informacje o profilu
55                </h3>
56                
57                <div className="space-y-6">
58                  <div>
59                    <label htmlFor="name" className="block text-sm font-medium text-gray-700">
60                      Imię i nazwisko
61                    </label>
62                    <input
63                      type="text"
64                      name="name"
65                      id="name"
66                      className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-quantum-500 focus:ring-quantum-500 sm:text-sm"
67                      defaultValue="Maria Nowak"
68                    />
69                  </div>
70                  
71                  <div>
72                    <label htmlFor="email" className="block text-sm font-medium text-gray-700">
73                      Email
74                    </label>
75                    <input
76                      type="email"
77                      name="email"
78                      id="email"
79                      className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-quantum-500 focus:ring-quantum-500 sm:text-sm"
80                      defaultValue="maria.nowak@quantum.com"
81                    />
82                  </div>
83                  
84                  <div>
85                    <label htmlFor="about" className="block text-sm font-medium text-gray-700">
86                      O mnie
87                    </label>
88                    <textarea
89                      id="about"
90                      name="about"
91                      rows={3}
92                      className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-quantum-500 focus:ring-quantum-500 sm:text-sm"
93                      defaultValue="Jestem inżynierem kwantowym mieszkającym w Nowym Dystrykie. Pracuję nad rozwojem nowych technologii transportu w Metropolis Quantum."
94                    />
95                  </div>
96                  
97                  <div className="flex justify-end">
98                    <button
99                      type="button"
100                      className="inline-flex justify-center py-2 px-4 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-quantum-500"
101                    >
102                      Anuluj
103                    </button>
104                    <button
105                      type="submit"
106                      className="ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-quantum-600 hover:bg-quantum-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-quantum-500"
107                    >
108                      Zapisz
109                    </button>
110                  </div>
111                </div>
112              </div>
113            )}
114            
115            {activeTab === 'security' && (
116              <div className="p-6">
117                <h3 className="text-lg font-medium leading-6 text-gray-900 mb-4">
118                  Bezpieczeństwo
119                </h3>
120                {/* Zawartość zakładki bezpieczeństwa */}
121              </div>
122            )}
123            
124            {activeTab === 'notifications' && (
125              <div className="p-6">
126                <h3 className="text-lg font-medium leading-6 text-gray-900 mb-4">
127                  Powiadomienia
128                </h3>
129                {/* Zawartość zakładki powiadomień */}
130              </div>
131            )}
132            
133            {activeTab === 'billing' && (
134              <div className="p-6">
135                <h3 className="text-lg font-medium leading-6 text-gray-900 mb-4">
136                  Płatności
137                </h3>
138                {/* Zawartość zakładki płatności */}
139              </div>
140            )}
141          </div>
142        </div>
143      </div>
144    </div>
145  );
146}

Narzędzia i zasoby dla Tailwind CSS

Edytory i rozszerzenia

  1. VS Code: Rozszerzenie "Tailwind CSS IntelliSense" zapewnia autouzupełnianie, podgląd kolorów i inne funkcje.
  2. Tailwind CSS Playground: Online playground do eksperymentowania z Tailwind.

Komponenty i biblioteki

  1. Tailwind UI: Płatna biblioteka komponentów od twórców Tailwind.
  2. Headless UI: Bezpłatna biblioteka dostępnych, nieostylowanych komponentów, które dobrze współpracują z Tailwind.
  3. Tailwind Components: Społecznościowa kolekcja darmowych komponentów Tailwind.

Generatory i narzędzia

  1. Tailwind Config Viewer: Narzędzie do wizualizacji konfiguracji Tailwind.
  2. Tailwind CSS Cheat Sheet: Ściągawka z wszystkimi klasami Tailwind.

Typowe problemy i rozwiązania

Problem 1: Klasy dynamiczne nie są uwzględniane w produkcji

Jeśli używasz dynamicznych klas, PurgeCSS może je pominąć:

1// To może nie działać w produkcji
2<div className={`text-${color}-500`}>Tekst</div>

Rozwiązanie:

1// Definiuj wszystkie możliwe klasy
2<div className={color === 'red' ? 'text-red-500' : color === 'blue' ? 'text-blue-500' : 'text-black'}>Tekst</div>

Problem 2: Konfiguracja JIT (Just-In-Time)

Tailwind v3.0 i nowsze domyślnie używają silnika JIT, co przyspiesza kompilację. Jeśli korzystasz ze starszej wersji, możesz włączyć JIT w konfiguracji:

1// tailwind.config.js (dla starszych wersji)
2module.exports = {
3  mode: 'jit',
4  // inne opcje
5}

Problem 3: Konflikt z innymi bibliotekami CSS

Jeśli masz konflikty stylów z innymi bibliotekami, możesz użyć prefiksu dla wszystkich klas Tailwind:

1// tailwind.config.js
2module.exports = {
3  prefix: 'tw-',
4  // inne opcje
5}

Teraz wszystkie klasy będą miały prefiks

tw-
:

1<div className="tw-flex tw-items-center tw-p-4">
2  Tekst z prefiksem
3</div>

Podsumowanie

Tailwind CSS to potężne narzędzie, które może znacząco przyspieszyć proces tworzenia interfejsów użytkownika w Next.js 15. Dzięki podejściu "utility-first", możesz tworzyć niestandardowe projekty bez opuszczania HTML i bez pisania własnego CSS.

Najważniejsze punkty:

  1. Integracja z Next.js 15 jest prosta i wymaga tylko kilku kroków konfiguracyjnych
  2. Tailwind jest wysoce konfigurowalny, co pozwala dostosować go do potrzeb projektu
  3. PurgeCSS jest wbudowany, co zapewnia małe rozmiary plików CSS w produkcji
  4. Bogaty ekosystem wtyczek i narzędzi rozszerza możliwości frameworka
  5. Podejście "utility-first" przyspiesza tworzenie interfejsów i ułatwia utrzymanie kodu

W następnym rozdziale przyjrzymy się komponentom Shadcn/UI, które są zbudowane na bazie Tailwind CSS i dostarczają gotowe komponenty interfejsu użytkownika, które można łatwo dostosować do specyficznych potrzeb projektu.

Przejdź do CodeWorlds