← Wszystkie projekty / System projektowy i development

2026 / 10 min czytania

Pomagam.pl

System projektowy odtworzony z działającej strony, zaprojektowany w Figmie, a potem wdrożony jako ~70 zweryfikowanych komponentów React przez pokierowanie agentami AI.

Pomagam.pl to jedna z największych platform zbiórek internetowych w Polsce, działająca od 2014 roku. Ludzie zbierają na niej pieniądze na leczenie, zwierzęta, inicjatywy lokalne i wszystko inne, co jest legalne, a platforma pobiera 0% prowizji od wpłat i wypłat. Produkt rozrastał się funkcja po funkcji, a każda nowa funkcja trafiała prosto na działającą stronę.

Nie było pliku Figma, biblioteki projektowej ani archiwum projektu. Sama strona była jedynym zapisem tego, jak cokolwiek miało wyglądać. Zostałem zaproszony, żeby zamienić to w system projektowy, na którym zespół mógłby budować, a potem zamienić ten system projektowy w produkcyjną bibliotekę React.

Zastrzeżenie: wszystkie opinie wyrażone w tym tekście są moje własne i nie muszą odzwierciedlać stanowiska firmy. Niektóre elementy projektowe zostały zmienione względem oryginału.

Moja rola

Przy Pomagam.pl pracowałem jako projektant UI/UX i systemu projektowego, a potem jako design engineer. Zespół był mały: CTO, product owner, jeden full-stack developer i ja.

Praca miała cztery części. Zrobiłem audyt działającego produktu, żeby znaleźć wzorce, których już używał. Zaprojektowałem system w Figmie, razem z fundamentami, komponentami, dokumentacją oraz regułami responsywności i dostępności. Zbudowałem bibliotekę komponentów React i jej Storybook, kierując agentami Claude Code przez pipeline, który sam zaprojektowałem i prowadziłem. I zaprojektowałem proces QA, który sprawdza to, co agenci produkują. Większości kodu nie napisałem sam. Zaprojektowałem system, ustaliłem reguły, prowadziłem pipeline i wyłapywałem błędy, kiedy coś szło nie tak.


Start bez źródła prawdy

Bez plików projektowych każda decyzja musiała zaczynać się od działającej strony. Zanim jakikolwiek komponent powstał w Figmie, musiałem rozpracować, czym produkt już był: które wzorce się powtarzały, które były jednorazowymi rozwiązaniami dodanymi pod konkretną funkcję i co system projektowy musi obejmować, żeby nowe funkcje przestały wymyślać na nowo te stare.

Dwa ekrany wykonują prawdziwą pracę produktu i system musiał dobrze obsłużyć oba. Strona kampanii to miejsce, w którym darczyńca podejmuje decyzję. W jej prawej kolumnie kolejno widać cel i pasek postępu, linijkę społecznego dowodu słuszności („Wpłaciły 42 osoby w ciągu 2 dni”), jeden wypełniony przycisk główny („Wpłać teraz”), cichszy przycisk „Udostępnij” z licznikiem udostępnień, a potem organizatora i ostatnie wpłaty. Na mobile przycisk wpłaty ląduje tuż pod paskiem postępu, a organizator kurczy się do jednego wiersza. Formularz tworzenia kampanii to miejsce, w którym organizator się angażuje: jedna karta na krok, licznik znaków przy tytule, duże, walidowane pole kwoty i czerwona etykieta „Wskazówka” nad polami zdjęcia i historii, z poradą napisaną prostym językiem i listą tego, o czym warto napisać.

Brak źródła prawdy bardziej boli mały zespół niż duży. Kiedy funkcje wdraża jeden full-stack developer, każdy ekran projektowany od zera w kodzie zabiera czas produktowi, a każda drobna różnica wizualna staje się trwała w momencie wejścia na produkcję.


Argumenty za systemem projektowym

Przed rozpoczęciem budowy przedstawiłem zespołowi plan, razem z tym, co system projektowy da Pomagam.pl w praktyce:

Responsywność domyślnie. Komponenty mają wbudowane reguły dla breakpointów, więc nowa strona działa na mobile bez dodatkowego etapu projektowania.

Krótszy czas od idei do działającej strony. Nowe funkcje składa się z istniejących, przetestowanych elementów, zamiast projektować i kodować je od zera.

Testy A/B bez przeprojektowywania. Warianty zbudowane z tych samych komponentów mogą wejść na produkcję równolegle, więc zespół może przetestować pomysł bez zobowiązywania się do niego.

Jedno źródło prawdy. Projekt i kod wskazują na te same komponenty, więc pytanie „który przycisk jest właściwy?” ma jedną odpowiedź.

Dostępność sprawdzona raz, dziedziczona wszędzie. Kontrast, stany focus i obsługa klawiatury są ustalane na poziomie komponentu, a nie strona po stronie.

Mniej przerabiania dla małego zespołu. Mniej jednorazowych ekranów znaczy, że full-stack developer spędza czas na produkcie, a nie na budowaniu tej samej karty po raz piąty.

CTO i PO się zgodzili, więc zacząłem pracę w Figmie.


Projektowanie systemu w Figmie

Odtworzyłem produkt w Figmie jako system, zaczynając od fundamentów: typografii, odstępów, ikon i czterech siatek layoutu (4 kolumny na smartfonie, 8 na tablecie, 12 na desktopie i dużym desktopie). Na nich stoją komponenty, z wariantami, dokumentacją tego, jak i kiedy ich używać, oraz regułami responsywności dla każdego breakpointu. Celem była biblioteka, której zespół mógłby zaufać i na której mógłby budować nowe funkcje.

Każda strona komponentu ma ten sam szablon, żeby nikt nie musiał za każdym razem uczyć się nowego układu: plakietka responsywności, jednozdaniowy opis przeznaczenia, uwaga o użyciu, baner statusu budowy, który mówi, które części są już zakodowane w Storybooku, a potem rozmiary, stany, hierarchia i zachowanie na mobile. Strona przycisku Button pokazuje poziom szczegółowości. Definiuje sześć wariantów (filled, success, outlined, outlined brand, text, link), cztery rozmiary o wysokości od 40 do 64 px i dokładnie cztery stany: domyślny, hover, loading i disabled. Hierarchia jest stała (filled, potem outlined, potem text), wariant outlined brand jest w systemie przyciskiem destrukcyjnym, a przyciski zawsze dzieli 12 px. Kliknięcie nie jest osobnym stanem, bo sygnalizuje je już fala rozchodząca się z miejsca kliknięcia. Na małych ekranach przyciski zmieniają orientację i rozmiar, a MD staje się najmniejszym dostępnym.

Żeby sprawdzić, czy system się trzyma, testowałem go na szablonach stron zbudowanych wyłącznie z nowych komponentów. Szablony szybko pokazują, gdzie brakuje komponentu, gdzie wariant nie pokrywa realnego przypadku użycia i gdzie reguła responsywności łamie się na prawdziwym układzie strony. Po każdej rundzie przeglądałem wyniki z zespołem i potwierdzałem kierunek, zanim szedłem dalej. Na koniec zespół miał plik Figma, który służył jako źródło prawdy dla nowej pracy.


Od Figmy do Storybooka, przez pokierowanie AI

System z Figmy musiał potem stać się produkcyjną biblioteką React, z której Claude miał później sam korzystać, żeby składać strony kampanii. Każdy subtelny błąd w komponencie mnoży się na każdej stronie zbudowanej na nim, a później nikt nie wyłapie tego wzrokiem.

AI umie napisać komponent React. Trudniejszym problemem było zbudowanie procesu, w którym mogła wyprodukować ich ~70, a ja mógłbym zaufać wynikowi.

Pięć wyspecjalizowanych agentów zamiast jednego generalisty. Skonfigurowałem subagentów Claude Code zbudowanych pod konkretne zadania: jeden zamienia plik Figma w czytelny dla maszyny spis, jeden wyciąga design tokeny (kolory, odstępy, typografię) z własnych zmiennych Figmy, a nie z ręcznie wpisanych wartości, jeden buduje komponenty w małych partiach, jeden przeprowadza QA wizualne i dostępnościowe względem żywego projektu w Figmie, a jeden, senior-reviewer, krytycznie czyta diff i zgłasza znaleziska.

Celowo wąski dostęp do narzędzi. Każdy mój agent dostaje jawną listę narzędzi. Agent QA może robić screenshoty i mierzyć, ale nie może edytować kodu, więc nie może „naprawić” problemu, po cichu zmieniając definicję tego, co znaczy zaliczony test.

Jeden model na jedno zadanie. Tanie modele zajmowały się mechanicznym wyliczaniem, mocniejsze budowały, a najmocniejsze były zarezerwowane dla review. Taki podział był decyzją kosztową w takim samym stopniu, jak jakościową. Puszczenie wszystkiego na największym modelu byłoby rozrzutne, a puszczenie wszystkiego na najtańszym wypuściłoby śmieci.

Pętla, która eskaluje tylko, gdy musi. Połączyłem agentów w pętlę: budowa, QA, review, poprawka i ponowna weryfikacja, która działa bez nadzoru i wraca do mnie tylko, gdy trafi na coś, co musi rozstrzygnąć człowiek. W szczytowym momencie miałem ponad dwanaście takich pipeline'ów działających równolegle na różnych częściach biblioteki.

Zabezpieczenia, które czynią wynik AI sprawdzalnym

Jedno źródło prawdy, też w kodzie. Każdy komponent jest powiązany z konkretnym node'em w Figmie przez zwalidowaną mapę JSON. Agent, który otrzymuje URL do Figmy, najpierw sprawdza tę mapę, więc żaden komponent nie powstaje dwa razy pod dwiema nazwami, a żadna wartość nie odchodzi od projektu bez tego, żeby było to widoczne.

Zero „wywnioskowane” i zero „dla spójności”. Stało się to twardą zasadą projektu, kiedy zauważyłem, że każdy błąd dokładności prowadził do tej samej przyczyny: coś nie było oczywiste, więc model napisał wiarygodnie wyglądającą odpowiedź, zamiast sprawdzić. Linia w stylu „brak dokładnej specyfikacji, używam X dla spójności” teraz liczy się jako czerwona flaga do zbadania.

Automatyczne wykrywanie odchyleń. Linter wywala build, jeśli komponent sięga po surowy kolor hex albo przypadkową wartość w pikselach zamiast prawdziwego design tokenu. Ten rodzaj zgnilizny przechodzi review i miesiącami po trochu degraduje system projektowy.

Obowiązek zatrzymania się i zapytania. Kiedy Figma nie ma odpowiednika dla czegoś, agent budujący musi się zatrzymać i zapytać człowieka, zamiast wymyślać komponent, którego nikt nie zatwierdził.


Wyłapywanie błędów

Agenci AI zawodzą w konkretny, rozpoznawalny sposób. Dużą część mojej pracy przy tym projekcie stanowiło poznanie tych wzorców i zaprojektowanie procesu, który je uwzględnia.

Zgłaszał sukces przy pracy, której nie wykonał. Agent QA powiedział mi „5/5 komponentów PASS”, z pewnymi siebie szczegółami. Cztery z pięciu plików raportów, które sam napisał, mówiły „not yet run”. Złapałem to, sprawdzając pliki, a nie wierząc podsumowaniu. Sprawdzanie plików, które agent twierdzi, że napisał, stało się stałym krokiem, dodałem go do trwałej pamięci projektu, żeby nikt nie musiał uczyć się tego na nowo.

Czytał kod, zamiast go testować. Agenci wciąż „weryfikowali” komponenty, ponownie czytając kod źródłowy, zamiast renderować je w przeglądarce. Przepisałem proces QA tak, żeby wymagał prawdziwych dowodów: obliczonych stylów, zmierzonych wymiarów i realnej interakcji przez hover i klawiaturę. Passy bez tych dowodów odrzucałem. Więcej niż raz odsyłałem tę samą kontrolę trzy albo cztery razy, zanim wróciła uczciwa.

Mierzył złą rzecz i zgłaszał pass. Jeden przebieg QA „potwierdził” responsywne zachowanie, zmieniając rozmiar elementu wrappera, a nie viewportu przeglądarki, co w ogóle nie może wywołać breakpointów CSS. Ta kontrola nigdy nie mogła znaleźć błędu, który rzekomo wykluczyła. Doszedłem, że przyczyną było brakujące uprawnienie do narzędzia we własnej konfiguracji agenta, i naprawiłem to tam.

Chował realne ograniczenie za pewnym siebie językiem. Agent opisał modal jako mający działającą pułapkę fokusa, podczas gdy biblioteka, na której ten modal stał, nie miała żadnego zarządzania fokusem. Poprawiłem to i uczciwie opisałem brak.

Ta dyscyplina złapała prawdziwe błędy, zanim trafiły na produkcję:

  • input walutowy, w którym prop value nie był naprawdę kontrolowany, więc rodzic nie mógł odrzucić wpisanego znaku
  • pole tekstowe, które gubiło to, co użytkownik wpisał, w momencie przejścia przeglądarki przez breakpoint, bo wersje mobile i desktop stały się dwoma osobnymi, niekontrolowanymi inputami
  • modal bez pułapki fokusa dla klawiatury
  • tekst w kolorze marki, który nie przechodził kontrastu WCAG przy 2,97:1
  • przycisk w stanie ładowania, który na czas wirowania usuwał swoją etykietę ze strony, zostawiając czytnikom ekranu nieaktywną kontrolkę bez nazwy
  • polska gramatyka liczby mnogiej („1 dzień”, „2 dni”, „5 dni”), poprawna tylko dla trzech przykładowych liczb, które przypadkiem użyto w projekcie

Żaden z tych błędów nie jest egzotyczny. To właśnie przecieka, kiedy „wygląda dobrze” zastępuje weryfikację.


Decyzje, które zostawiłem sobie

Kiedy odejść od pliku projektowego. Figma definiowała stan pressed dla przycisków. Zrezygnowaliśmy z niego na rzecz własnego efektu ripple z biblioteki komponentów i zapisaliśmy to jako opatrzoną datą decyzję produktową, żeby żaden kolejny przebieg nie „naprawił” tego z powrotem. Dla wariantu link, osadzonego w tekście, ripple jest wyłączony, bo nie pasuje do podkreślonego tekstu.

Kiedy dostępność wygrywa z literą specyfikacji. Przycisk w stanie ładowania zamienia etykietę na spinner. Pierwsza wersja usuwała etykietę ze strony, więc oczekujący przycisk stawał się nieaktywną kontrolką bez dostępnej nazwy. Teraz etykieta zostaje na stronie, ukryta wizualnie, a przycisk ustawia aria-busy. Powód jest zapisany w kodzie obok poprawki.

Kiedy biblioteka pod spodem nie wystarcza. Accordion jest zbudowany na Material Tailwind, który nie ustawia aria-expanded ani aria-controls. Nasz wrapper dodaje oba i usuwa zamknięte ciało z drzewa dostępności oraz z kolejności tabulacji, zamiast tylko przycinać je wizualnie.

Kiedy projekt i kod mają mówić jednym językiem. Propsy komponentów odwzorowują właściwości z Figmy. variant w Accordionie to właściwość Type z Figmy, a defaultOpen odpowiada State=closed. Projektanci i deweloperzy mówią o tym samym, a recenzent może sprawdzić prop względem pliku.

Kiedy wariant mobilny jest jednym komponentem, a kiedy dwoma. Część mobilnych wariantów w Figmie to ten sam komponent w mniejszym rozmiarze. Inne różnią się strukturą na tyle, że uzasadniają osobny eksport. Przykładem jest blok organizatora na stronie kampanii: pełna karta z awatarem i przyciskiem „Zadaj pytanie” na desktopie, jeden wiersz na mobile, a biblioteka eksportuje oba jako CardOrganizer i CardOrganizerCompact. Ten wybór ma realne konsekwencje dla API, więc podejmowałem go przypadek po przypadku, zamiast pozwolić, żeby zdecydowała o tym reguła.

Kiedy sam system projektowy jest błędny. Połowa skali typografii w Figmie zmienia rozmiar w zależności od breakpointu, ale system klas utility nie miał sposobu, żeby to wyrazić. Zamiast łatać ~35 komponentów jeden po drugim, przeniosłem poprawkę na poziom tokenów i zrobiłem same zmienne CSS świadome breakpointów. Każda istniejąca klasa stała się responsywna bez zmiany w ani jednym komponencie i bez ryzyka, że coś przegapimy.

Kiedy przestać. Komponent tooltip został zbudowany, przetestowany i zrecenzowany, a potem zespół zdecydował, że nie będzie go używać. Jest nieeksportowany i schowany, a nie usunięty, z zapisanym powodem, więc praca nie przepada i nikt nie zbuduje go przypadkiem na nowo.

Co marketing z tym robi

Zespół marketingu teraz generuje nowe strony z komponentów i szablonów, które zaprojektowałem, a wynik wychodzi bardzo bliski prawdziwej stronie. Strony, które kiedyś czekały na full-stack developera, mogą zaczynać od elementów, które są już zaprojektowane, opisane i przetestowane.


Efekt

  • ~70 komponentów produkcyjnych, każdy powiązany z konkretnym node'em w Figmie i zweryfikowany względem niego
  • Fundamenty i dokumentacja, z siatką layoutu dla każdego rozmiaru ekranu i polską stroną dokumentacji dla każdego komponentu, a propsy nazwane po właściwościach z Figmy
  • Design tokeny generowane z własnych zmiennych Figmy, w tym responsywny system typografii, w którym jedna klasa utility daje cztery rozmiary w zależności od breakpointu
  • ~700 automatycznych testów, plus kontrole visual-diff, dostępności i odchyleń tokenów wpięte w workflow
  • Żywy katalog komponentów w Storybooku, wdrażany przez CI za bramką dostępu
  • Pipeline do ponownego wykorzystania: ta sama konfiguracja agentów i reguły weryfikacji teraz przenoszą się na budowanie stron

Stack. React 18.3, TypeScript 5 (strict), Tailwind CSS 3.4, Material Tailwind, Storybook 8, npm workspaces, Vitest i Testing Library, Playwright, Figma API do tokenów i specyfikacji komponentów, GitHub Actions i GitHub Pages, oraz Claude Code z własnymi subagentami, zawężonymi uprawnieniami do narzędzi, routingiem modeli per etap i trwałą pamięcią projektu.

Co się przenosi dalej. Biblioteka komponentów jest specyficzna dla Pomagam.pl. Trzy nawyki przenoszą się na inne projekty: czytanie działającego produktu na tyle uważnie, żeby znaleźć system, który już w nim siedzi, projektowanie komponentów tak, żeby dostępność i responsywność były częścią specyfikacji od początku, oraz kierowanie agentami AI z zawężonymi narzędziami, kontrolami opartymi na dowodach i jasną regułą, kiedy zatrzymać maszynę i podjąć decyzję samemu.