Tworzymy UI - własny Spotify: Odtwarzacz Muzyki w Next.js
Praktyczne ćwiczenie z budowania interfejsów. Wykorzystamy komponenty Shadcn UI i magię Tailwinda, aby stworzyć responsywną kartę odtwarzacza muzyki.

Witajcie! 👋
Ostatnio poznaliśmy moim zdaniem trzy najważniejsze technologie nowoczesnego frontendu: Next.js, Tailwind CSS i shadcn/ui. Wiemy już, jak skonfigurować środowisko w chmurze.
Dzisiaj przechodzimy do praktyki. Zamiast nudnych formularzy, zbudujemy coś, co każdy z nas ma w kieszeni – Odtwarzacz Muzyki.
Dlaczego to ćwiczenie jest świetne na start?
- Świetny element portfolio: Niewielkim nakładem pracy stworzymy coś, co wygląda profesjonalnie.
- Praca z układem: Przećwiczymy Flexboxa wewnątrz komponentów.
- Hierarchia wizualna: Nauczymy się sterować uwagą użytkownika za pomocą rozmiaru tekstu i elementów (możesz później sam dodać kolory).
🛠️ Krok 1: Przygotowanie "Klocków"
Na początku musimy założyć konto w serwisie: stackblitz.com
Pamiętacie, że shadcn nie jest biblioteką, którą instalujemy w całości? Pobieramy tylko to, czego potrzebujemy.
W naszym odtwarzaczu użyjemy trzech elementów:
- Card (Karta) – jako nasz główny kontener.
- Button (Przycisk) – do sterowania (Play, Pause, Serduszko).
- Slider (Suwak) – do paska postępu utworu.
Otwórzcie terminal w swoim projekcie (CodeSandbox/StackBlitz) i wpiszcie:
npx shadcn@latest add card button slider
Ikony (Heart, Play, StepBack itp.) mamy dostępne "z pudełka" dzięki bibliotece lucide-react, która instaluje się automatycznie z shadcn.
🧬 Krok 2: Tworzymy strukturę pliku
Zbudujemy nasz komponent w pliku app/page.tsx. Poniżej znajduje się kompletny kod struktury pliku.
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/card"
import { Heart, StepBack, Play, StepForward, ListMusic } from "lucide-react"
import { Slider } from "@/components/ui/slider"
import { Button } from "@/components/ui/button"
export default function Home() {
return (
<main className="p-4">
<Card>
<CardHeader>
<img
src="https://static.asfaltshop.pl/uploads2/photos/big_webp/2021/07/07/20210707033324_morza_poludniowe_cd_real_size_3000px.webp"
alt="Żyto Morza Południowe"
/>
<CardTitle>ŻYTO/NOON - „Przypływ”</CardTitle>
<CardDescription>
NoweNagrania • 14 tys. subskrybentów
</CardDescription>
<Heart />
</CardHeader>
<CardContent>
<Slider defaultValue={[50]} max={100} step={1} />
<div>
<span>2:15</span>
<span>5:00</span>
</div>
<div>
<Button>
<StepBack />
</Button>
<Button>
<Play />
</Button>
<Button>
<StepForward />
</Button>
</div>
</CardContent>
<CardFooter>
<ListMusic />
<p>Odtwarzane z playlisty "Hip-Hop"</p>
</CardFooter>
</Card>
</main>
)
}
🎨 Krok 3: Tworzymy style pliku za pomocą tailwind
Teraz ostylujmy cały plik, aby odtwarzacz wyglądał efektowanie.
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
} from "@/components/ui/card"
import { Heart, StepBack, Play, StepForward, ListMusic } from "lucide-react"
import { Slider } from "@/components/ui/slider"
import { Button } from "@/components/ui/button"
export default function Home() {
return (
// 1. TŁO I CENTROWANIE (Pełny ekran)
<main className="min-h-[100vh] w-full p-4 flex justify-center items-center bg-slate-50">
{/* 2. GŁÓWNY KONTENER KARTY */}
<Card className="min-w-[350px] max-w-[450px] shadow-xl">
{/* A. NAGŁÓWEK: Okładka + Tytuł + Serce */}
<CardHeader className="px-4 -my-2 space-y-4">
<img
src="https://static.asfaltshop.pl/uploads2/photos/big_webp/2021/07/07/20210707033324_morza_poludniowe_cd_real_size_3000px.webp"
alt="Żyto Morza Południowe"
className="rounded-lg aspect-square object-cover shadow-sm"
/>
<div className="flex justify-between items-start">
<div className="space-y-1">
<CardTitle>ŻYTO/NOON - „Przypływ"</CardTitle>
<CardDescription className="text-xs">
NoweNagrania • 14 tys. subskrybentów
</CardDescription>
</div>
{/* Interaktywne serduszko */}
<Button
className="hover:text-red-500 hover:bg-transparent transition-colors"
variant="outline"
size="icon"
>
<Heart />
</Button>
</div>
</CardHeader>
{/* B. TREŚĆ: Suwak + Przyciski sterowania */}
<CardContent className="px-4 space-y-4 pt-4">
<div className="space-y-2">
<Slider defaultValue={[50]} max={100} step={1} />
<div className="flex justify-between text-sm text-slate-500 font-medium">
<span>2:15</span>
<span>5:00</span>
</div>
</div>
<div className="flex items-center justify-center gap-4">
<Button className="rounded-full" variant="outline" size="icon">
<StepBack className="w-4 h-4" />
</Button>
{/* Duży przycisk Play */}
<Button
className="rounded-full w-14 h-14"
variant="outline"
size="icon"
>
<Play className="w-8 h-8 ml-1" />
</Button>
<Button className="rounded-full" variant="outline" size="icon">
<StepForward className="w-4 h-4" />
</Button>
</div>
</CardContent>
{/* C. STOPKA: Info o playliście */}
<CardFooter className="flex justify-center items-center gap-2 pt-2 pb-6">
<ListMusic className="w-4 h-4 text-slate-500" />
<p className="text-xs text-slate-500 font-medium">
Odtwarzane z playlisty "Hip-Hop"
</p>
</CardFooter>
</Card>
</main>
)
}
🧠 Analiza: Dlaczego to wygląda dobrze?
Przyjrzyjmy się kluczowym technikom, których użyliśmy w tym projekcie. To one odróżniają "zwykłą stronę" od "nowoczesnego UI".
1. Hierarchia Typografii (Ważne vs Mniej Ważne)
Zauważcie, że nie każdy tekst jest taki sam.
- Tytuł utworu: Używamy
CardTitle, który jest domyślnie pogrubiony i większy. - Autor/Subskrypcje: Użyliśmy klasy
text-xs(bardzo mały tekst) wCardDescription. Dzięki temu oko użytkownika od razu wie, co jest najważniejsze. - Czasy utworów: Są w kolorze szarym (
text-slate-500), aby nie odciągały uwagi od suwaka.
2. Układ "Space-Between"
To najczęściej używany trik w UI. Spójrzcie na sekcję z tytułem i serduszkiem:
<div className="flex justify-between">
<div>Tytuł...</div>
<Button>Serce</Button>
</div>
Dzięki flex justify-between, tytuł "przykleja się" do lewej krawędzi, a serce do prawej, niezależnie od szerokości karty.
3. Detale Interakcji (Micro-interactions)
Spójrzcie na przycisk "Ulubione" (Serce). Dodałem tam klasę: hover:text-red-500 hover:bg-transparent. To sprawia, że po najechaniu myszką ikona zmienia kolor na czerwony, co daje użytkownikowi natychmiastową informację zwrotną. To te małe smaczki budują jakość.
4. Triki z Marginesami
W CardHeader użyliśmy klasy -my-2 (ujemny margines pionowy). Shadcn domyślnie daje spore odstępy. Czasami, tak jak tutaj, chcemy, aby okładka i tytuł były bliżej siebie. Ujemny margines pozwala nam "złamać" domyślne zasady i zagęścić układ bez psucia reszty karty.
⚔️ Wyzwanie dla Was
Macie działający kod. Teraz przeróbcie go tak, żeby wyrażał utwór, który prezentuje wasz player:
- Zmieńcie utwór: Podmieńcie
srcobrazka na okładkę Waszego ulubionego albumu. - Tryb "Aktywny": Spróbujcie zmienić główny przycisk Play (
variant="outline") na wypełniony kolorem (variant="default"), aby bardziej rzucał się w oczy. - Eksperyment z kolorem: Czy potraficie sprawić, by pasek postępu (Slider) był zielony (jak w Spotify)? Podpowiedź: Będziecie musieli zajrzeć do pliku konfiguracyjnego Tailwinda lub nadpisać klasę wewnątrz komponentu.
Pamiętajcie, frontend to sztuka układania gotowych klocków w estetyczną całość. Właśnie zrobiliście duży krok w tę stronę! 🚀
Powodzenia w kodowaniu!