Jak stworzyć galerię zdjęć bez wtyczek

Własna galeria zdjęć bez wtyczek to pełna kontrola nad kodem, szybkością i wyglądem. W tym poradniku zbudujesz od podstaw lekki komponent, który działa w każdej nowoczesnej przeglądarce, jest łatwy do utrzymania i rozszerzania. Przejdziemy przez planowanie, strukturę HTML, styl CSS, interakcje w czystym JS, a także optymalizację, tak by obrazy ładowały się błyskawicznie i wyglądały świetnie na każdym ekranie. Zero zbędnych bibliotek – tylko zrozumiały, przewidywalny kod.

Plan i struktura: minimalny HTML oraz przygotowanie obrazów

Wymagania i porządkowanie plików

Zanim napiszesz choć jedną linijkę, uporządkuj zasoby. Przygotuj katalogi: <project/>, w nim <img/> na obrazy i <assets/> na style oraz skrypty. Dobre nazewnictwo plików ułatwia automatyzację i wersjonowanie. Stosuj spójną konwencję: np. <img/portret-01-800.jpg> dla wersji średniej, <img/portret-01-1600.jpg> dla dużej oraz <img/portret-01-320.jpg> dla miniatury. Dzięki temu szybko podmienisz ścieżki lub wygenerujesz warianty skryptem.

Zadbaj o metadane i opisy. Każde zdjęcie powinno mieć krótki, rzeczowy opis alternatywny (atrybut alt), zgodny z kontekstem publikacji. To zwiększa dostępność i ułatwia nawigację osobom korzystającym z czytników ekranu, a jednocześnie pomaga wyszukiwarkom lepiej zrozumieć treść.

Najprostszy szkielet HTML

Podstawowa struktura może wyglądać tak (zwróć uwagę na semantykę linku do dużego pliku i miniaturę w elemencie img):

<div class="gallery">
   <a class="gallery__item" href="img/portret-01-1600.jpg" data-title="Portret 01">
     <img src="img/portret-01-320.jpg" alt="Portret w świetle dziennym" width="320" height="213" loading="lazy">
   </a>
   <a class="gallery__item" href="img/portret-02-1600.jpg" data-title="Portret 02">
     <img src="img/portret-02-320.jpg" alt="Portret w cieniu drzew" width="320" height="213" loading="lazy">
   </a>
</div>

Wariant ten jest lekki, atrybut loading ułatwia lazy-loading, a dane opisujące (np. data-title) przydadzą się w modalu. Klasami nazwij logikę: "gallery" dla kontenera, "gallery__item" dla elementu klikalnego. Jeśli przewidujesz podpisy, możesz dodać <span class="gallery__caption"> wewnątrz linku (ale niech nie przeszkadza na urządzeniach dotykowych).

Opis alternatywny i semantyka

Atrybut alt opisuje treść, nie czynność. Unikaj pustych słów; zamiast "Zdjęcie 1" użyj "Mężczyzna na tle ceglanej ściany". Dobra semantyka to nie tylko aspekt etyczny i prawny, ale również korzyść w pozycjonowaniu. Jeśli obraz jest dekoracyjny, alt może być pusty, ale w galerii artystycznej zwykle jest sensowny opis.

Miniatury, proporcje i spójność

Miniatury powinny mieć stały stosunek boków (np. 3:2 lub 1:1), by siatka była równa. Zadbaj o atrybuty width i height, aby zredukować skoki układu (CLS) podczas ładowania. Dzięki temu przeglądarka zarezerwuje miejsce jeszcze przed pobraniem obrazu, co wspiera wydajność i komfort przewijania.

Styl CSS i układ: Grid, responsywność i dopracowanie

Reset, zmienne i fundament

Najpierw krótki reset i zmienne kolorystyczne. W arkuszu <assets/styles.css> zacznij od:

* { box-sizing: border-box; }
img { display: block; max-width: 100%; height: auto; }
:root { –gap: 12px; –bg: #0e0e0f; –fg: #f2f2f2; –muted: #9aa0a6; }

To zapewnia przewidywalność wymiarów i podstawową paletę. Później łatwo podmienisz kolory lub rozmiary przerw.

Siatka CSS Grid z auto-fit

Wprowadź elastyczną siatkę, która sama układa kafelki w zależności od szerokości kontenera:

.gallery {
   display: grid;
   grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
   gap: var(–gap);
   margin: 0 auto;
   padding: var(–gap);
   max-width: 1200px;
   background: var(–bg);
   color: var(–fg);
}

.gallery__item {
   position: relative;
   overflow: hidden;
   border-radius: 8px;
   outline: none;
}

Dzięki Grid i minmax otrzymujesz płynny układ dla telefonów, tabletów i desktopów — bez media queries albo z ich minimalnym udziałem. To solidna baza pod responsywność.

Dopasowanie obrazu: wypełnienie i bezpieczne przycięcie

Jeśli miniatury mają różne proporcje, kontroluj kadr przez kontener o wymuszonych proporcjach i obraz w trybie "okładki":

.gallery__item::before { content: ""; display: block; padding-bottom: 66.666%; }
.gallery__item img {
   position: absolute; inset: 0;
   width: 100%; height: 100%;
   object-fit: cover;
   transition: transform .3s ease;
}

Takie podejście trzyma siatkę w ryzach i poprawia wrażenia wizualne, nawet gdy źródłowe zdjęcia są niespójne.

Stany najechania i fokus klawiatury

Niech element daje subtelny sygnał interakcji:

.gallery__item:hover img, .gallery__item:focus img { transform: scale(1.03); }
.gallery__item:focus { outline: 2px solid #6ab0ff; outline-offset: 2px; }

Dzięki temu użytkownicy klawiatury natychmiast widzą, gdzie znajduje się fokus, co sprzyja standardom WCAG.

Tryb jasny/ciemny i kontrast

Dodaj automatyczny dobór motywu na podstawie preferencji systemowych:

@media (prefers-color-scheme: light) {
   :root { –bg: #ffffff; –fg: #1b1b1b; –muted: #555; }
}

Następnie sprawdź kontrast kluczowych elementów (np. podpisów) i w razie potrzeby zwiększ nasycenie barw lub rozmiary czcionek.

Interakcja bez bibliotek: lightbox, klawiatura i gesty

Modal oparty o czysty CSS (szybki start)

Jeśli chcesz natychmiastowego efektu, użyj "target hack": każdy element ma unikatowy identyfikator, a klik ustawionego linku otwiera warstwę pełnoekranową. To minimum bez JavaScript, choć z ograniczeniami nawigacji i dostępności. Lepszym wyborem będzie jednak wariant JS poniżej.

Wersja JavaScript: otwieranie, zamykanie, nawigacja

Stwórz prosty modal i logikę sterującą. W HTML dodaj na końcu body (lub w miejscu, w którym renderujesz komponent) kontener modalu:

<div class="lightbox" hidden>
   <button class="lightbox__close" aria-label="Zamknij">×</button>
   <button class="lightbox__prev" aria-label="Poprzednie">‹</button>
   <button class="lightbox__next" aria-label="Następne">›</button>
   <img class="lightbox__img" alt="">
   <div class="lightbox__caption"></div>
</div>

Style dla modalu (zachowaj skróconą formę):

.lightbox { position: fixed; inset: 0; display: grid; place-items: center; background: rgba(0,0,0,.9); z-index: 1000; }
.lightbox[hidden] { display: none; }
.lightbox__img { max-width: 90vw; max-height: 80vh; }
.lightbox__caption { color: #fff; margin-top: 8px; text-align: center; }

Skrypt w czystym JS (załaduj go na końcu dokumentu lub jako type="module"):

const items = […document.querySelectorAll(".gallery__item")];
const lb = document.querySelector(".lightbox");
const lbImg = lb.querySelector(".lightbox__img");
const lbCap = lb.querySelector(".lightbox__caption");
let index = -1;
function open(i) { index = i; const a = items[i]; lbImg.src = a.href; lbImg.alt = a.querySelector("img").alt; lbCap.textContent = a.dataset.title || ""; lb.hidden = false; lb.focus(); preload(i+1); preload(i-1); history.pushState({lb:true}, ""); }
function close() { lb.hidden = true; lbImg.src = ""; }
function next() { open((index + 1 + items.length) % items.length); }
function prev() { open((index – 1 + items.length) % items.length); }
items.forEach((a, i) => a.addEventListener("click", e => { e.preventDefault(); open(i); }));
lb.querySelector(".lightbox__close").addEventListener("click", close);
lb.querySelector(".lightbox__next").addEventListener("click", next);
lb.querySelector(".lightbox__prev").addEventListener("click", prev);
window.addEventListener("keydown", e => { if (lb.hidden) return; if (e.key === "Escape") close(); if (e.key === "ArrowRight") next(); if (e.key === "ArrowLeft") prev(); });
window.addEventListener("popstate", () => { if (!lb.hidden) close(); });
function preload(i) { if (i < 0 || i >= items.length) return; const img = new Image(); img.src = items[i].href; }

Ten zestaw zapewnia podstawy: otwieranie, zamykanie, obsługę strzałek, klawisza Escape, a nawet wstecz przeglądarki. Otrzymujesz w efekcie lekki lightbox bez bibliotek.

Pułapki fokusa i pułapka klawiatury

Dodaj pułapkę fokusa w modalu, by tab nie wychodził poza aktywne elementy. Zbierz focusable w modalu (przyciski) i przechwytuj zdarzenie keydown: gdy użytkownik jest na pierwszym i wciśnie Shift+Tab, skocz na ostatni; i odwrotnie. To poprawia dostępność w realnym użyciu.

Gesty dotyku i przewijanie

Na urządzeniach mobilnych możesz dodać prosty detektor przesunięcia (touchstart/touchend), by zmieniać zdjęcia ruchem palca. Ogranicz też przewijanie tła, gdy modal jest otwarty, np. przez dodanie klasy "no-scroll" na body i ustawienie overflow: hidden; podczas wyświetlania modalu.

Wydajność i SEO: optymalizacja obrazów, lazy-loading i cache

Format, kompresja i balans jakości

Generuj wersje WebP i AVIF dla nowoczesnych przeglądarek oraz zapasowy JPEG/PNG. Zadbaj o średnią wagę miniatur rzędu 20–50 KB i pełnych zdjęć 150–400 KB (w zależności od przeznaczenia). Mądrze dobrane parametry kompresji to kluczowa optymalizacja.

Responsive images: srcset i sizes

W miniaturach oraz pełnych zdjęciach używaj atrybutów <img srcset> i <sizes>. Dzięki temu przeglądarka pobierze najlepszy wariant dla faktycznej szerokości. Przykład miniatury:

<img src="img/portret-01-320.jpg"
   srcset="img/portret-01-320.jpg 320w, img/portret-01-480.jpg 480w, img/portret-01-640.jpg 640w"
   sizes="(max-width: 600px) 45vw, (max-width: 1200px) 30vw, 200px"
   alt="Portret w świetle dziennym" width="320" height="213" loading="lazy">

To znacząco poprawia wydajność. Ustalanie sizes nie musi być idealne – wystarczy dopasować je do typowego układu siatki.

Lazy loading i LCP/CLS

Native "loading=lazy" ogranicza transfer, ale nie stosuj go do pierwszych zdjęć widocznych w obszarze ekranu (Above the Fold), bo może to pogorszyć LCP. Pamiętaj o atrybutach width/height, by uniknąć przeskoków układu. Regularnie mierz metryki w Lighthouse lub WebPageTest. Świadoma optymalizacja to stały proces, a nie jednorazowe działanie.

Cache HTTP, CDN i wersjonowanie

Dla statycznych obrazów ustaw długie nagłówki cache-control z fingerprintem w nazwie, np. "portret-01-320.6f3a1.jpg". Pliki serwuj z CDN z włączonym HTTP/2 lub HTTP/3. Dla CSS/JS włącz minifikację i treeshaking. Razem daje to wymierny zysk dla SEO i zadowolenia użytkowników.

Preload i preconnect

Jeżeli modal często otwierasz po kliknięciu w miniaturę, rozważ <link rel="preload" as="image"> dla pierwszych fotografii lub dynamiczne preload w JS tuż po interakcji użytkownika. Dla CDN użyj <link rel="preconnect">, by skrócić czas ustanawiania połączenia.

Dostępność i integracja z CMS: WordPress, Jamstack i automatyzacja

Role, etykiety i kontrasty

Kontener modalu może mieć aria-modal="true", a przyciski aria-label z jasnymi komunikatami. Sprawdź czytelność podpisów i wielkość elementów dotykowych (co najmniej 44×44 px). Troska o dostępność i kontrast wpływa na realną użyteczność, nie tylko na raporty.

WordPress bez wtyczek

Jeśli używasz WordPressa, masz kilka ścieżek bez dodawania pluginów:

  • Blok HTML w edytorze: wklej strukturę <div class="gallery"> … i wczytaj własny arkusz stylów w funkcjach motywu (wp_enqueue_style) oraz skrypt (wp_enqueue_script, w trybie defer). To najprostsze.
  • Shortcode: dodaj w functions.php prosty rejestrator [simple_gallery], który wygeneruje markup z listy ID mediów. Dane wyciągniesz przez wp_get_attachment_image_src i wp_get_attachment_caption.
  • Pattern: przygotuj wzorzec bloku, by redaktorzy mogli dodawać gotową siatkę jednym kliknięciem, bez ryzyka psucia HTML.

Pamiętaj, by w galeriach WP uzupełniać alt i podpisy w bibliotece mediów, co wspiera SEO i spójność treści.

Jamstack: Eleventy/Hugo i generowanie miniaturek

W statycznych generatorach użyj pipeline’u do obrazów: np. Eleventy Image lub wtyczki Hugo do tworzenia wariantów 320/480/640/1600. Szablon wygeneruje atrybuty srcset i sizes automatycznie. Ten proces znacząco upraszcza utrzymanie wielojęzycznych i wieloformatowych galerii.

Narzędzia CLI: sharp/imagemin

W projektach bez CMS dodaj do package.json skrypty: "images:thumbs" z użyciem "sharp" do generowania miniatur i "images:opt" z "imagemin" oraz pluginami do WebP/AVIF. Zautomatyzuj nazewnictwo, aby utrzymać porządek i uniknąć ręcznej obróbki setek plików.

Testy jakości: lista kontrolna

  • Czy każde zdjęcie ma sensowny alt i poprawny podpis? (także pod kątem WCAG)
  • Czy metryki LCP/CLS są stabilne na 3G/4G? (ważne dla wydajność)
  • Czy modal zamyka się klawiszem Escape i nie gubi fokusa? (kluczowa dostępność)
  • Czy siatka skaluje się na małych ekranach i dużych monitorach? (pełna responsywność)
  • Czy obrazy mają odpowiednie warianty i poprawne srcset? (lepsza optymalizacja)

Rozszerzenia i utrzymanie

Rozważ filtrowanie po tagach, paginację i wirtualizację (render tylko widocznych elementów) przy bardzo dużych kolekcjach. Monitoruj błędy w konsoli i w narzędziach analitycznych. Portuj kod do komponentów (np. Web Components lub lekkie frameworki), ale bez utraty kontroli nad podstawowym HTML/CSS/JS.

Tworząc galerię bez wtyczek, zyskujesz przewidywalny skład kodu, który jest prosty do audytu i zgodny z najlepszymi praktykami: od dostępności, przez optymalizacja obrazów i responsywność, po mierzalne korzyści dla SEO. Jeśli potrzebujesz pełnego minimum copy-paste, wystarczy połączyć przedstawione fragmenty HTML, CSS i JS, a następnie dopasować ścieżki obrazów, podpisy i parametry siatki do własnych potrzeb.

< Powrót

Zapisz się do newslettera


Zadzwoń Napisz