# github-stats — dynamiczne widgety GitHub SVG na Cloudflare Workers

Zbudowałem self-hosted Cloudflare Worker, który generuje dynamiczne widgety SVG z GitHub API — z systemem motywów, dark/light mode, cache KV i przezroczystymi tłami do embeddowania wszędzie.

**Canonical:** https://spoko.space/pl/blog/github-stats/  
**Language:** pl  
**Published:** 2026-06-14  
**Tags:** cloudflare-workers, typescript, svg, github-api, open-source, kv-cache, simple-icons  
**Category:** Inżynieria

---
<div class="text-center flex gap-4 justify-center flex-wrap">

</div>

## Problem

Każdy profil GitHub opowiada historię — commity, streak, najpopularniejsze języki, heatmapa aktywności. Ale istniejące serwisy z widgetami mają ograniczenia: sztywne motywy kłócące się z designem, brak kontroli nad cachowaniem, problemy z rate-limitami i brakiem możliwości dopasowania palety kolorów do własnego portfolio.

Chciałem widgetów, które:
- pasują dokładnie do ciemnej niebieskiej palety spoko.space
- automatycznie przełączają się między dark i light mode przez `prefers-color-scheme`
- działają bez tła, żeby bezszwowo wtapiały się w dowolną stronę
- są cachowane na edge, żeby były szybkie i nie przeciążały GitHub API

Więc je zbudowałem.

## Efekt

<div class="flex flex-col gap-4 items-center my-8">
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/profile?theme=light&v=1" />
    <img src="https://github.spoko.space/profile?v=1" alt="GitHub Profile Stats" width="460" height="200" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/langs?theme=light&v=1" />
    <img src="https://github.spoko.space/langs?v=1" alt="Najpopularniejsze języki" width="460" height="200" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/contrib?theme=light&v=1" />
    <img src="https://github.spoko.space/contrib?v=1" alt="Kalendarz aktywności" width="460" height="133" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
</div>

Widgety działają na żywo — renderowane w czasie rzeczywistym z moich danych GitHub, cachowane na edge Cloudflare i automatycznie zmieniające motyw zgodnie z preferencjami systemu.

## Architektura

Całość wdrożona jest na **Cloudflare Pages** — serverless z globalnym zasięgiem. Stos technologiczny jest celowo uproszczony:

- **TypeScript** — type-safe pipeline renderowania bez żadnego frameworka
- **Cloudflare KV** — edge key-value store cachuje odpowiedzi API na 24 godziny, więc GitHub API jest odpytywane co najwyżej raz dziennie na każdy typ danych
- **GitHub GraphQL API** — jedno zapytanie pobiera dane o aktywności, streak i statystyki repozytoriów
- **Czyste generowanie SVG** — żadnego canvas, headless browsera ani bibliotek do obrazów; tylko szablony string tworzące poprawny SVG

SVG to świadomy wybór: wyświetla się piksel w piksel na każdym ekranie (łącznie z Retina), waży kilka kilobajtów i pozwala zmieniać kolory wyłącznie przez parametry URL — bez żadnego przetwarzania obrazów po stronie serwera.

Projekt obsługuje dziewięć endpointów (`/langs`, `/stats`, `/streak`, `/contrib`, `/repos`, `/trophies`, `/stack`, `/profile`, `/icon`) i zwraca odpowiedzi `image/svg+xml` z właściwymi nagłówkami `Cache-Control`.

## System motywów

Każdy endpoint przyjmuje parametry query do nadpisania kolorów:

```
/langs?theme=light
/langs?bg=0d1117&primary=58a6ff&text=c9d1d9&radius=6
/profile?bg=transparent&border=transparent
```

W pakiecie dostępnych jest dziesięć gotowych motywów: `dark`, `light`, `tokyonight`, `dracula`, `github-dark`, `nord`, `radical`, `synthwave`, `catppuccin`, `gruvbox`, `aura`, `discord`. Obsługa przezroczystego tła — dodana specjalnie na potrzeby embeddów w portfolio — pozwala widgetom wtapiać się w dowolną stronę niezależnie od jej tła.

Do embeddów w README na GitHubie element `<picture>` automatycznie obsługuje przełączanie dark/light:

```html
<picture>
  <source media="(prefers-color-scheme: light)"
    srcset="https://github.spoko.space/langs?theme=light">
  <img src="https://github.spoko.space/langs" alt="Języki">
</picture>
```

## Ikony technologii z simple-icons

Widget `/stack` pobiera ścieżki ikon i kolory marek bezpośrednio z paczki npm [simple-icons](https://simpleicons.org/) — bez ręcznego kopiowania SVG, bez nieaktualnych kolorów. Dzięki bundlerowi esbuild Cloudflare z tree-shakingiem do bundla trafiają tylko zaimportowane ikony.

<div class="my-6 flex justify-center">
  <picture class="w-full max-w-2xl">
    <source media="(prefers-color-scheme: light)" srcset="https://github.spoko.space/stack?techs=Vue%2CAstro%2CTypeScript%2CTailwind%2CPHP%2CLaravel%2CNode.js%2CWordPress&theme=light&v=1" />
    <img src="https://github.spoko.space/stack?techs=Vue%2CAstro%2CTypeScript%2CTailwind%2CPHP%2CLaravel%2CNode.js%2CWordPress&v=1" alt="Tech Stack" width="460" height="60" loading="lazy" class="w-full h-auto rounded-lg" />
  </picture>
</div>

## Fork i deploy

Projekt jest open source na licencji MIT. Wdrożenie własnej instancji zajmuje około pięciu minut:

```bash
git clone https://github.com/spokospace/github-stats
cd github-stats
npm install
npx wrangler secret put GITHUB_TOKEN
npx wrangler secret put CACHE_BUST_TOKEN
npx wrangler pages deploy .
```

Dodaj swój login GitHub do tablicy `OWNERS` w `src/index.ts` i gotowe. Pełna dokumentacja na [github.spoko.space](https://github.spoko.space).
