Visualize, customize, and export your GitHub contribution calendar: custom palettes, shapes, and backgrounds. No token required. Available on the web and as an Android app with home-screen widgets; iOS is not released yet.
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: an Android app on Google Play, with home-screen widgets that keep your streak one glance away. The same Flutter code builds for iOS, but nothing ships it yet.
-
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
- 📤 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, rewritten by the app after every fetch and refreshed daily in the background
- ✉️ Contact the maintainer from the web's
/contactpage or from the app: the message leaves as an email and is stored nowhere - 🔒 Telemetry behind consent: on the web, Google Analytics and Better Stack load only after cookie consent; in the app, Diagnostic Reports go to Sentry unless you turn them off, and Usage Events go to PostHog only once you turn them on
- 🗓️ 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/ |
Android app with home-screen widgets; iOS not released | Flutter · Riverpod · RevenueCat · Sentry · PostHog | 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). The rules a change is reviewed against are written once, in CODING_STANDARDS.md, and each layer has a colocated AGENTS.md for whoever works in it.
Both apps also share one vocabulary: GLOSSARY.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), a root devDependency wired up bypnpm installthrough the rootpreparescript - 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 tomainand every pull request into it, 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 secrets fill the dart-defines.json the release writes at build time: five keys, listed in app/README.md. Only Android is built and released; CI/CD has the whole pipeline, its secrets and what to do when a release fails halfway.
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 |
| Coding standards | The rules a change is reviewed against, and which of them the tooling enforces |
| 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, the Tip Jar, 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 | Every web flow end to end: the landing page, a search, the SVG and JSON routes, contact, caching, and their failures |
| API Reference | The SVG and JSON endpoints, parameters, caching, errors |
| Web Application | Routes, headers, environments, the Cloudflare deploy |
| Mobile App | How the app starts, fetches, caches and refreshes its home-screen widget; build configuration, releases, 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 Sunday-aligned grid deterministically |
| SVG Rendering | Geometry, labels, shapes, and the emitted attributes |
| Deterministic Randomness | The seeded PRNG behind the placeholder grids |
| CI/CD | Every workflow and job, the secrets they read, Play delivery, and recovering a release that failed halfway |
| 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
AGENTS.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