Visualize, customize, and export your GitHub contribution calendar: custom palettes, shapes, and backgrounds. No token required. Available on the web and as an iOS & Android app, with home-screen widgets on Android.
Website · Google Play · App Store (soon) · Web docs · App docs
- What You Get
- Features
- Quick Start
- Project Structure
- Monorepo Development
- Documentation
- Support & Contributing
- Use of AI
Type a GitHub username and get a fully customizable contribution calendar you can pin, embed, or carry on your home screen:
-
Live SVG endpoint: this image is rendered by ContribKit right now:
-
Web app: render any profile, tweak palette and shape, and export as PNG, SVG, or Markdown.
-
Mobile app: native iOS & Android app, with Android home-screen widgets that keep your streak one glance away.
-
README embed: a one-line Markdown snippet that always shows your up-to-date calendar.
- 🎨 11 color palettes: GitHub, Catppuccin, Nord, Dracula, Gruvbox, Sunset, Tokyo Night, One Dark, Rosé Pine, Solarized, Monokai
- 🔷 5 cell shapes: rounded, square, circle, dot, hex
- 📤 3 export formats: PNG for the readme, SVG for the portfolio, Markdown for the bio
- 🔓 No token required: only public contribution data; no OAuth, no PAT
- 📱 Home-screen widgets: small (streak counter) and medium (grid, streak and total); Android only, refreshed daily
- 🗓️ Year selector: any year back to 2005
- 🌗 Dark/light theme: follows your system scheme, with manual override
- 🧩 Shared design tokens: palettes defined once in
shared/and read by both apps; shapes are shared with the web only (see ADR 0002)
- Open contribkit.app
- Type a GitHub username and hit render
- Customize, then copy or export from the export section
- Install from Google Play (App Store coming soon)
- Enter your username
- On Android, add the widget to your home screen
See the embedding guide for palettes, shapes, and background options.
Monorepo with three components sharing design tokens:
| Directory | Component | Stack | Docs |
|---|---|---|---|
web/ |
contribkit.app + SVG/JSON API | Astro · TypeScript · Cloudflare Workers | web/README.md |
app/ |
iOS & Android app, Android home-screen widgets | Flutter · Riverpod · RevenueCat | app/README.md |
shared/ |
Single source of truth for palettes, shapes, usernames | JSON consumed by both apps | shared/README.md |
Both apps follow the same DDD-ish layered architecture (domain → application → infrastructure / ui): the domain is pure, validated value objects guard every boundary, errors are a sealed set of typed Failures matched exhaustively at the boundary (returned as values on the web, thrown and caught in the app), and each layer documents its own rules in a colocated CLAUDE.md.
Both apps also share one vocabulary: CONTEXT.md is the domain glossary, and docs/adr/ records the decisions behind the architecture.
Tooling that applies to the whole repo:
- Package manager: pnpm workspaces (
pnpm-workspace.yaml) - Git hooks: lefthook (
lefthook.yml). Install once withbrew install lefthook && lefthook install - Commits: Conventional Commits, enforced by commitlint
- Releases: semantic-release per component (
web-vX.Y.Z/app-vX.Y.Ztags) - CI: one
ci.ymlon every push and pull request, with no path filter. Achangesjob decides which client was touched and every other job is gated on its output, so an app-only pull request skips the web jobs rather than never starting them. The documentation-consistency contract runs ungated, because it asserts things about both clients and the root
GitHub Environments are namespaced by component (<component>-<stage>) because they are repo-global and hold component-specific secrets:
| GitHub Environment | Component | Stage | Deployed by |
|---|---|---|---|
app-production |
Flutter app | production | release-app.yml (track = production) |
app-development |
Flutter app | development | release-app.yml (track ≠ production) |
web-production |
Astro web | production | ci.yml (deploy-production, push to main) |
web-development |
Astro web | development | ci.yml (deploy-development, per-PR preview) |
App development and web development map to different things: app development is the internal Play track + RevenueCat sandbox; web development is a per-PR preview Worker on *.workers.dev. The component-scoped configs do not repeat the prefix: wrangler uses [env.production] / [env.development]; Flutter has no build flavors: locally the stage is whichever dart-defines file you pass (dart-defines.prod.json vs dart-defines.json), and in CI it is the track input to release-app.yml, which picks the GitHub Environment whose REVENUECAT_KEY is written into dart-defines.json at build time.
Component-specific setup, commands, and deploy flows live in web/README.md and app/README.md.
| Guide | Description |
|---|---|
| Architecture | The layer map both clients share, a request end to end, build and release |
| Contributing | Setup, the checks, commit rules, and how a change gets released |
| Domain glossary | The canonical name for every domain concept, and the ones to avoid |
| Web | API reference, embedding guide, architecture, deploys |
| App | Flutter setup, widgets, in-app purchases, releases |
| Shared tokens | Palettes, shapes, and usernames consumed by both apps |
| Legal notice | Privacy · Terms |
The user-facing guides are published as the repository wiki:
| Page | Covers |
|---|---|
| Getting Started | Viewing, embedding, installing the app, running it locally |
| How It Works | End-to-end flow, from a username to a rendered calendar |
| API Reference | The SVG and JSON endpoints, parameters, caching, errors |
| Web Application | Routes, headers, environments, the Cloudflare deploy |
| Mobile App | Build configuration, home-screen widgets, tips |
| Architecture | Layers, value objects, entities, failures |
| Project Structure | Where every directory lives and what owns it |
| Fetching Contributions | How the public page is requested, and what can go wrong |
| HTML Parsing | The regexes, and why there is no DOM parser |
| Calendar Grid | Building the fixed 53×7 grid deterministically |
| SVG Rendering | Geometry, labels, shapes, and the emitted attributes |
| Deterministic Randomness | The seeded PRNG behind the placeholder grids |
| CI/CD | Both pipelines, environments, and Play delivery |
| Git Hooks | What lefthook runs, and when |
| Troubleshooting | Common failures and what they actually mean |
For why it is built this way (one decision per file), see the architecture decision records, indexed in ARCHITECTURE.md.
- Report bugs
- Request features
- Report a vulnerability privately, never as an issue
- Improve documentation
If you find this project useful, consider supporting its development:
This project uses AI assistance primarily for documentation and review purposes. AI tools (GitHub Copilot, Claude) were used to:
- Write and improve documentation (READMEs, layer
CLAUDE.mdfiles) - Generate boilerplate code and configuration files
- Assist with code reviews and refactoring suggestions
The core logic, architecture decisions, and implementation were developed by the maintainer. All AI-generated content has been reviewed and validated.
ContribKit is not affiliated with GitHub, Inc.
MIT © Made with 🤘🏼 by Ferran Buireu