Fotografia · Aneta Ciepielowska - Netafoto · 2026 · 2 miesiące

Netafoto - strona fotografa bez CMS-a, na trzech plikach

Cała strona to trzy pliki i 44 linie JavaScriptu - bez frameworka, bez kroku budowania i bez panelu, do którego nikt się nie loguje.

  • HTML
  • CSS
  • JavaScript
  • WebP
  • sharp
  • GitHub Actions
Zobacz stronę live ↗
Netafoto - strona fotografa bez CMS-a, na trzech plikach

Wyzwanie

Poprzednia wersja strony miała panel administracyjny, blog i bazę danych - Aneta nie użyła ich ani razu. Zdjęcia z sesji wrzucała tam, gdzie zawsze, a strona po prostu stała. Do tego zdjęcia fotografki to najcięższa treść w internecie: kilkanaście pełnowymiarowych kadrów na podstronę, które na telefonie w terenie potrafią się nie doczekać wczytania.

Nasze rozwiązanie

Wyrzuciliśmy wszystko, czego nie używała. Została zwykła statyczna strona bez frameworka i bez kroku budowania: strona główna, O mnie, pełny cennik i sześć podstron z sesjami. Zdjęcia przechodzą przez sharpa do WebP, deploy leci FTP-em z GitHub Actions. Kontakt to telefon, mail i Facebook - bez formularza, którego nikt by nie obsługiwał.

3

Plików na całą stronę

44

Linii JavaScriptu

0,27 s

Pierwsze renderowanie treści

Co zbudowaliśmy

  • 01 Dziewięć podstron: główna, O mnie, oferta i sześć rodzajów sesji
  • 02 Sesje: noworodki, dzieci, kobiece, ślub, ciążowe, komunia - 14-21 kadrów każda
  • 03 Pełnoekranowy hero ze zdjęciem z portfolio
  • 04 Ścieżka 'Jak to działa': rezerwacja, sesja, odbiór zdjęć
  • 05 Cennik z konkretnymi kwotami zamiast 'wyceny indywidualnej'
  • 06 Lightbox, nawigacja i reveal w 44 liniach JavaScriptu
  • 07 Zdjęcia WebP obrobione skryptem sharpa
  • 08 Przekierowania ze starych adresów podstron i bloga
  • 09 Menu mobilne z aria-expanded
  • 10 Deploy FTP-em z GitHub Actions
Spis treści +

Punkt wyjścia

Aneta fotografuje od dziewiętnastu lat - noworodki, dzieci, sesje kobiece, ciążowe, komunie i śluby. Klientki trafiają do niej z polecenia i z Facebooka, więc strona nie musi sprzedawać od zera. Musi odpowiedzieć na trzy pytania: czy takie zdjęcia mi się podobają, ile to kosztuje i jak się umówić.

Poprzednia wersja odpowiadała na pierwsze, a pozostałe dwa chowała - problem, który rozkładamy szerzej w tekście o stronie fotografa: portfolio i galerii klienta.

Co jest na stronie

Zwykła strona firmowa, nie portfolio-galeria. Strona główna prowadzi przez to, co klientka chce wiedzieć: hero, trzy kroki od rezerwacji do odbioru zdjęć, krótkie „o mnie”, sześć rodzajów sesji, skrócony cennik i kontakt. Osobna podstrona O mnie, osobna pełna oferta.

Sześć podstron sesji to podstrony ofertowe, a nie galerie same w sobie: nagłówek z opisem, kiedy taka sesja ma sens, cena tuż pod nim, a dopiero potem zdjęcia - od 14 do 21 kadrów, razem ponad sto.

Decyzje, które ukształtowały projekt

Zero frameworka, zero builda

Strona zmienia się kilka razy w roku: nowa sesja do galerii, korekta ceny. Krok budowania przy takiej częstotliwości to koszt bez zwrotu - trzeba pamiętać komendę, utrzymać zależności, naprawić build, gdy po pół roku coś się rozjedzie. Statyczny HTML z jednym arkuszem stylów i jednym plikiem JS wgrywa się FTP-em i będzie działał za pięć lat bez dotykania.

Nie jest to reguła, którą stosujemy wszędzie. To wniosek z tego konkretnego przypadku: jedna osoba, rzadkie zmiany, zero potrzeby dynamiki.

Kasacja panelu CMS

Najtrudniejsza decyzja w tym projekcie polegała na usunięciu działającego kodu. Panel, blog, logowanie i integracja z bazą zostały w repozytorium jako pliki poprzedniej wersji, ale zniknęły ze strony. Utrzymywanie panelu, do którego nikt się nie loguje, to koszt bezpieczeństwa i aktualizacji ponoszony za funkcję o zerowym użyciu.

Blog został tam, gdzie Aneta faktycznie pisze - na Blogspocie. Stary adres bloga i stare adresy podstron przekierowują na nowe miejsca, żeby linki z Facebooka i z Google nie trafiały w pustkę.

Zdjęcia przycinane skryptem, nie na oko

Zdjęcia z sesji są z definicji ciężkie. Idą przez sharp: zmniejszenie i kompresja z jakością 82, a na stronie leżą jako WebP. Przy tej jakości różnicy nie widać nawet na dobrym ekranie, a plik schodzi kilkukrotnie. Skrypt jest w repo (npm run optimize-gallery), więc kolejna partia zdjęć przechodzi tę samą obróbkę, a nie zależy od tego, czy ktoś pamiętał eksportować z Lightrooma w odpowiednich ustawieniach.

Hero jest wyłączony z optymalizacji - to jedyne zdjęcie, gdzie pełna jakość ma sens, bo zajmuje cały ekran. Wszystkie kadry poza pierwszym ekranem mają loading="lazy".

Cennik na wierzchu

Sesja noworodkowa 800 zł, kobieca, ciążowa, dziecięca i rodzinna po 600 zł, komunia 600 zł w plenerze lub 1000 zł z reportażem, plener ślubny i ślub cywilny po 1200 zł, zadatek 200 zł. Konkretne kwoty zamiast „wyceny indywidualnej” odsiewają zapytania, na które i tak nie byłoby odpowiedzi, i skracają rozmowę z tymi, które zostają.

Pod cennikiem osiem kart z warunkami, które wcześniej trzeba było wyciągać w rozmowie: czas realizacji około trzech tygodni, 30 zł za dodatkowe ujęcie, dwa ujęcia gratis dla stałych klientek, akcesoria do sesji w cenie, sesja na prezent.

Kontakt bez formularza

Numer telefonu, mail i Facebook, powtórzone w stopce każdej podstrony. Aneta i tak umawia sesje telefonicznie i na Messengerze, więc formularz byłby czwartą skrzynką do sprawdzania - i pierwszą, o której by się zapomniało. To odwrotność tego, co opisujemy przy formularzu kontaktowym, który generuje leady: tam formularz ma sens, bo ktoś go faktycznie obsługuje, tu nie ma - bo nikt by go nie obsługiwał.

Kliknięcie w kadr otwiera go na pełnym ekranie, Escape zamyka. Razem z nawigacją i pojawianiem się sekcji mieści się to w 44 liniach script.js, operujących na kilku klasach CSS. Sama biblioteka do lightboxa waży więcej niż cały ten plik i dokłada zależność do aktualizowania - podobny rachunek jak przy mikrointerakcjach a konwersji: efekt ma się opłacać, nie tylko dobrze wyglądać.

Co z tego wyszło

Pomiar strony głównej z 13 września 2026, headless Chrome 1366x900, bez cache: 44 ms do pierwszego bajtu, 0,27 s do pierwszego renderowania treści, 0,51 s do zdarzenia load. Czternaście żądań: dokument, jeden arkusz stylów, jeden plik JavaScript i jedenaście obrazów - 673 kB transferu.

Uczciwe zastrzeżenie: to pomiar strony głównej, nie sześciu podstron sesji, gdzie leży ponad sto kadrów. Tamtych stron w tym pomiarze nie sprawdzaliśmy, więc nie wiemy, jak zachowują się przy wolniejszym łączu - to jest dokładnie miejsce, w którym dobrze sięgnąć po checklistę Core Web Vitals przy kolejnej partii zdjęć. Nie mamy też wyniku poprzedniej wersji z CMS-em do porównania, bo nikt jej w tym trybie nie zmierzył - więc nie piszemy o poprawie, tylko o stanie takim, jaki jest teraz.

Technicznie

Statyczny HTML, jeden styles.css, jeden script.js (nawigacja zmieniająca się nad hero, menu mobilne z aria-expanded, reveal na IntersectionObserver ze staggerem 70 ms, lightbox). Zdjęcia w WebP obok plików źródłowych. Deploy: push na main uruchamia GitHub Actions, które wysyła pliki FTP-em na hosting.

Zrzuty ekranu

Pełnoekranowy hero - zdjęcie z portfolio jako tło, treść i CTA na wierzchu.
Pełnoekranowy hero - zdjęcie z portfolio jako tło, treść i CTA na wierzchu. (telefon)

Pełnoekranowy hero - zdjęcie z portfolio jako tło, treść i CTA na wierzchu.

Podobny problem do rozwiązania?

Napisz krótko, o co chodzi, wycena wraca w 24 godziny.

Zapytaj o wycenę →