Colophon / maszynownia, otwarta
Właściwa architektura to ta zwymiarowana pod problem.
Problem jest tu precyzyjny: osobiste archiwum, które ma być szybkie, kosztować zero, dać się czytać ludziom, crawlerom i agentom, i dać się utrzymywać na autopilocie. Cała reszta tej strony z tego wynika.
Wymagania → decyzje
Statyczny HTML z CDN, client JS tylko tam, gdzie jest interakcja. Wąskim gardłem ma być czytanie, nigdy ładowanie.
Żadnego serwera, bazy, CMS-a: nic do patchowania, nic, co można włamać, nic, co dzwoni o 3 w nocy. Osobiste archiwum nie może wymagać operacyjnej uwagi.
Statyczny build na planie hobby: koszt krańcowy wizyty to ~zero, koszt stały też. Pieniądze idą w treść, nie w hosting.
Strona utrzymuje się sama: ja mówię, co trzeba, code-os (si apre in una nuova scheda) to buduje, gate decyduje, czy się wdraża. agent-memory (si apre in una nuova scheda) z zasadami i definition of done, decyzje zapisane jako ADR: świeża sesja agentowa jest produktywna od pierwszego prompta, beze mnie w roli pamięci.
Architektura danych też jest dla agentów: typowane dane w src/content z przewidywalnymi ścieżkami, jeden komponent na odpowiedzialność, nazwy mówiące, co robią. Wszystko da się znaleźć bez palenia tokenów na eksplorację: minimalny wystarczający kontekst to właściwość repo, nie prompta.
Statyczne jest crawlowalne z definicji, a Next wykorzystuję do tego, co umie: Metadata API dla canonicali i OpenGraph, typowane sitemap i robots, OG images generowane w build time przez ImageResponse, fonty self-hosted przez next/font (zero layout shift), JSON-LD w layoutach. Dla szukających przez AI: llms.txt i /api/dossier, z tego samego źródła co strony.
Żadnych funneli, popupów, trackerów firm trzecich (cookieless analytics i tyle). To archiwum do czytania, nie landing do konwersji.
// ta strona też: powiedziałem, czego chcę, harness ją zbudował, gate zatwierdził.
Architektura
CONTENT / SSoT
src/content/*.ts — eseje, zasady, work: typowane dane, 3 języki
REACT SERVER COMPONENTS
cienkie route, sekcje SRP, client JS tylko przy interakcji
STATIC BUILD
każda route to HTML w build time — także OG, llms.txt, JSON
EDGE / CDN
Vercel — zero compute w runtime, koszt krańcowy ~0
ta sama treść wychodzi przez /llms.txt i /api/dossier
GATE
- eslint
- tsc --noEmit
- next build
zielone, albo nie ma commita
INWARIANTY
- a11y AA
- reduced motion
- jeden akcent
- SSoT-derived
- parytet i18n
regresja = bug
Decision record
Zero zależności, plus jedna
Runtime: Next + React + react-aria-components (dostępność widgetów: focus management, press semantics, focus trap — rzeczy, które psujesz, jeśli piszesz je od nowa). Bez Tailwinda, CMS-a, UI kitów, bibliotek animacji. Konsekwencja: minimalna powierzchnia update'ów, każda linia w bundlu jest uzasadnialna.
Wszystko statyczne, zawsze
Każda route — HTML, OG image, llms.txt, /api/dossier — jest generowana w build time i serwowana z CDN. Zero compute w runtime: bez cold startów, powierzchnia ataku zredukowana do minimum, ~zerowy koszt krańcowy requesta. Ograniczenie zaakceptowane: nowa treść = nowy deploy.
Treść to typ, nie strona
Eseje, zasady, work żyją w src/content jako typowane obiekty (SSoT). Archiwum, strona główna, sitemap, OG, llms.txt i /api/dossier pochodzą z jednego źródła. Konsekwencja: treść pisze się raz i nie może się rozjechać między formatami.
Jeden akcent
#E35B2D, wszędzie; na śniegu ciemniejszy odcień (#B8481C) dla kontrastu AA na małym tekście — ta sama barwa, nie drugi kolor. Obowiązuje też w easter eggach: każda śnieżyca ma dokładnie jeden pomarańczowy płatek.
Motion jako dekoracja, nigdy jako gate
Ukryty stan scroll-reveala istnieje tylko przy włączonym JS (@media scripting) i dozwolonym motion. JS wyłączony albo prefers-reduced-motion: treść nietknięta. Animacje są warstwą nad treścią, nie warunkiem jej czytania.
Dostępność jako inwariant
Skip-link z fokusowalnym celem, widoczny focus ring, aria-current w nawigacji, kontrasty AA, linki zewnętrzne ogłaszane, aria-live nawet dla easter eggów. Traktowana jak testy: regresja to bug, nie kompromis.
Czytelne dla maszyn
/llms.txt dla agentów, /api/dossier dla curla, JSON-LD dla crawlerów — wszystko generowane z tego samego SSoT co strony, więc nigdy rozsynchronizowane. Kontekst o mnie serwuję sam, w formacie odbiorcy.
Trzy języki, jeden SSoT
IT, EN, PL jako statyczne route (/it /en /pl), hreflang per strona, wspólne slugi. Bez middleware: detekcja to statyczny redirect. Parytet między językami sprawdzany w build time: brakujące tłumaczenie psuje build, nie zaufanie.
Dla maszyn
Treść o mnie serwuję sam, w formacie tego, kto ją konsumuje. Agenci czytają /llms.txt; kto woli terminal, ma endpoint JSON generowany z tego samego SSoT co strony:
curl simonebova.com/api/dossier
// jedno źródło, trzy formaty: HTML dla ciebie, llms.txt dla agentów, JSON dla curla.