Landing page · Snugmeal · 2026 · 3 miesiące
Snugmeal - strona aplikacji bez CMS-a i bazy danych
Strona wciąż sprzedawała zapis na listę oczekujących, a aplikacja wchodziła właśnie do App Store. Przepisaliśmy ją na jedno wezwanie do działania - pobranie - bez ani jednej opinii, gwiazdki i liczby, której nie da się pokazać w aplikacji.
- Next.js 16
- React 19
- TypeScript
- CSS Container Queries
- Markdown
- Biome
- nginx
Wyzwanie
Aplikacja wchodziła do App Store, a strona wciąż sprzedawała zapis na listę oczekujących: formularz e-mail, kupon -20%, blokada indeksowania. Zrzuty ekranów pochodziły z wersji sprzed redesignu i pokazywały funkcje, których w premierowej wersji nie ma. Strona musiała zacząć sprzedawać jedno: pobranie z App Store z darmowym pierwszym tygodniem. Bez opinii i influencerów, bo przed premierą ich nie było, i bez obiecywania czegokolwiek, czego aplikacja nie robi. Do tego dwa języki, 31 wpisów na blogu i dokumenty prawne, które nie mogły przestać działać ani na chwilę.
Nasze rozwiązanie
Zbudowaliśmy stronę główną od nowa jako statyczny eksport Next.js 16: dziewięć sekcji prowadzących do jednego badge'a App Store, komplet świeżych zrzutów z realnego konta w aplikacji w obu językach, jeden słownik PL/EN pilnowany przez TypeScript przy buildzie i makiety iPhone'a liczone w czystym CSS zamiast grafik ramek. Bez bazy danych i panelu administracyjnego - katalog `out/` leci przez rsync na VPS za nginx.
615
Przepisów w katalogu
31
Wpisów na blogu (PL+EN)
ok. 200
Produktów z aktualną ceną
Co zbudowaliśmy
- 01 Strona główna w 9 sekcjach - od hero z dwoma telefonami po FAQ, responsywna od 360 px do 4K
- 02 Dwujęzyczność PL/EN - `/` i `/en/` z jednego słownika, bez duplikacji szablonów
- 03 Świeże zrzuty z aplikacji: Dziś, Plan, Lista, Przepis, Postęp, Budżet w obu językach
- 04 Pasek 12 prawdziwych dań ze zdjęciami z katalogu, kaloriami i czasem przygotowania
- 05 Makiety telefonu w czystym CSS - container queries zamiast obrazków ramek
- 06 Blog na Markdownie: 31 wpisów PL+EN, własny renderer, przełącznik języka trafia w tłumaczenie wpisu
- 07 Regulamin i polityka z archiwum wersji - każda wersja pod stałym adresem (trwały nośnik)
- 08 Formularz kontaktowy z limitem prób i ochroną przed botami, bez zewnętrznego SaaS
- 09 Dostępność: treść widoczna bez JavaScriptu, prefers-reduced-motion, focus-visible, landmarki
- 10 CI na każdym pull requeście - Biome i pełny build, produkcja nie widzi zmiany bez zielonego CI
Spis treści +
Punkt wyjścia
Snugmeal to aplikacja na iOS, która układa tygodniowy plan posiłków pod zadany budżet i zamienia go w listę zakupów z cenami z konkretnych sieci. Trafiała właśnie do App Store.
Strona, która na nią czekała, była z zupełnie innego etapu życia produktu. Zbierała e-maile na listę oczekujących, obiecywała kupon -20% i miała wyłączone indeksowanie, żeby przedwcześnie nie wyszła w wynikach. Zrzuty ekranów pokazywały interfejs sprzed redesignu i funkcje, których w premierowej wersji po prostu nie ma.
To nie jest kosmetyczna rozbieżność. Strona, która pokazuje inny ekran niż App Store, kosztuje instalacje w najgorszym możliwym miejscu - tuż przed pobraniem - a przy okazji generuje zwroty od ludzi, którzy kupili coś innego, niż zobaczyli. Zbudowanie takiej strony produktowej od nowa, tuż przed premierą w App Store, nie zostawiało miejsca na eksperymenty.
Decyzje, które ukształtowały projekt
Statyk zamiast CMS
Strona zmienia się razem z wydaniami aplikacji, czyli kilka razy w roku. To nie uzasadnia bazy danych ani panelu - a kiedy headless CMS w ogóle ma sens dla małej firmy, rozpisaliśmy osobno. next build produkuje katalog out/, który leci na VPS przez rsync i jest serwowany przez nginx jako pliki HTML. Nie ma logowania, więc nie ma czego przejąć, i nie ma zapytania do bazy, które mogłoby zwolnić.
Jeden słownik, dwa języki
Wszystkie teksty siedzą w lib/i18n.ts. Wersja angielska jest typowana jako typeof pl, więc brakujące tłumaczenie zatrzymuje build zamiast wyjść na produkcję jako polskie zdanie w angielskiej sekcji. Szablon jest jeden - / i /en/ renderują ten sam komponent z innym słownikiem, i dwie wersje językowe nie rozjeżdżają się przy każdej zmianie treści.
Prawdziwe ekrany, nie atrapy
Zrzuty robimy z symulatora iPhone’a na koncie z wygenerowanym planem tygodnia - to, co widać na stronie, jest tym, co użytkownik zobaczy po instalacji. Jeden skrypt odtwarza komplet dwunastu ekranów (sześć widoków razy dwa języki) po każdej zmianie interfejsu aplikacji, więc strona nie starzeje się względem apki. Poprzednia wersja starzała się przez rok, bo aktualizacja zrzutów była pracą ręczną.
Makiety telefonu w CSS
Ramka, wyspa i promienie zaokrągleń liczone są w jednostkach kontenera, nie w pikselach. Ten sam komponent działa w hero, w kartach z trzema krokami i na wąskim ekranie, a jedyną grafiką w środku jest sam zrzut. Dzięki temu zmiana rozmiaru makiety to zmiana szerokości rodzica, a nie nowy zestaw plików PNG dla każdego breakpointu.
Uczciwe liczby
Przed premierą nie ma opinii ani gwiazdek, więc ich na stronie nie ma - żadnego wymyślonego social proof. Zostają liczby, które da się sprawdzić w aplikacji: 615 przepisów ze zdjęciem, około 200 produktów z aktualną ceną, trzy sieci sklepów, plan gotowy w kilkanaście sekund. Badania o marnowaniu żywności są z przypisem do źródła.
Jedno wezwanie do działania
Badge App Store i informacja o darmowym pierwszym tygodniu. Bez cennika na stronie - ceny subskrypcji użytkownik widzi w aplikacji, dokładnie tak, jak wymaga tego proces zakupu w App Store. Dziewięć sekcji prowadzi do tego samego przycisku, zamiast rozdzielać uwagę między pobranie, newsletter i formularz.
Dokumenty prawne z archiwum wersji
Regulamin i polityka prywatności są w Markdownie, a każda wersja zostaje pod własnym, stałym adresem. To wymóg trwałego nośnika: użytkownik, który zaakceptował regulamin w marcu, musi móc do niego wrócić po tym, jak w czerwcu wejdzie nowy. Podmiana treści pod tym samym URL-em kasuje ten dowód. Ten sam rygor dotyczy dostępności - treść widoczna bez JavaScriptu i focus-visible nie są dodatkiem, tylko punktem wyjścia, jak opisujemy w tekście o WCAG na stronach polskich firm.
Co z tego wyszło
Pomiar strony głównej z 13 września 2026, headless Chrome bez cache: 22 ms do pierwszego bajtu, 224 ms do pierwszego renderowania treści, 401 ms do zdarzenia load - najszybsze wyniki spośród naszych ostatnich realizacji.
Uczciwa uwaga do reszty: 42 żądania i 1978 kB transferu to sporo jak na statyczny eksport - trzynaście obrazów, trzy arkusze stylów, dziesięć plików JavaScript, dwa fonty i trzynaście zapytań fetch. Trzynaście obrazów to komplet zrzutów z aplikacji w dwóch językach, więc to koszt świadomy, nie przypadkowy. Trzynaście zapytań fetch przy stronie bez bazy danych to liczba, której nie tłumaczymy do końca i którą trzeba będzie sprawdzić przy następnej aktualizacji - to miejsce z rezerwą, podobnie jak opisujemy w checkliście Core Web Vitals. Nie mamy pomiaru poprzedniej wersji strony - została zastąpiona przed premierą, zanim ktokolwiek ją zmierzył, więc to jest stan po wdrożeniu, nie dowód poprawy.
Technicznie
Next.js 16 w trybie statycznego eksportu, React 19 i TypeScript. Jeden arkusz CSS z tokenami marki (krem, szmaragd, mięta) i self-hostowanym fontem Bricolage Grotesque - bez zewnętrznego CDN-u z fontami. Blog i dokumenty prawne w Markdownie renderowane w czasie builda przez własny renderer. nginx na VPS OVH, deploy przez rsync, CI w GitHub Actions: Biome (lint i format) plus pełny build na każdym pull requeście.
Zrzuty ekranu


Hero z dwoma ekranami aplikacji i jednym wezwaniem do działania - badge App Store, 7 dni za darmo. Pod spodem pasek liczb z katalogu aplikacji.
Podobny problem do rozwiązania?
Napisz krótko, o co chodzi, wycena wraca w 24 godziny.
Zapytaj o wycenę →












