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
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ł.
Lightbox bez biblioteki
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.
Podobny problem do rozwiązania?
Napisz krótko, o co chodzi, wycena wraca w 24 godziny.
Zapytaj o wycenę →




