ccc
Coffee Cups Contributions. A single-file, dependency-free coffee intake tracker with a GitHub-style heatmap. No build step, no framework, plain HTML, CSS, and JavaScript.
Live at coffee.gumx.cc. Source at git.gumx.cc/ccc.
Features
- Contributions heatmap: 53 x 7 grid (Sunday-aligned) covering the past 52 weeks, rendered in SVG. Month labels along the top, Mon/Wed/Fri labels on the left. Today's cell has a visible outline.
- 5-level greyscale scale: cup counts are bucketed proportionally to
MAX_CUPSusingMath.round. A legend is shown below the heatmap. - Hover tooltips: hovering any cell shows the date and cup count in a fixed-position tooltip.
- Two modes: local mode (full editing, localStorage) or static site mode (read-only, fetches from a remote URL).
- Stats: today's cups, current streak, total cups, and daily average.
- Light and dark mode: full
prefers-color-schemesupport via CSS custom properties. The heatmap re-renders if the OS theme changes mid-session.
Files
| File | Description |
|---|---|
ccc.html |
The entire application: one self-contained HTML file |
data.json |
Example data file for static site mode |
README.md |
This document |
LICENSE |
MIT licence |
Local Mode
Local mode is active when DATA_URL is set to "" (the default). All data is persisted to localStorage under the key ccc_v1.
Logging Coffee
A − / count / + adjuster lets you set how many cups to log (1–10). The + button disables when logCount would exceed the remaining cups for today. The log coffee button disables when today has already reached MAX_CUPS. Both controls re-evaluate after every action.
Clicking log coffee adds the selected count to today's total, hard-capped at MAX_CUPS.
Editing Past Days
Click any heatmap cell to open an inline popover for that day. The popover shows:
- The date
- − / number input / + to adjust the count (0–
MAX_CUPS) - set and cancel buttons
Keyboard shortcuts work inside the popover: Enter confirms, Escape cancels. Clicking anywhere outside the popover also cancels. Setting a day's count to 0 removes the entry entirely.
A hint below the log controls reads: "click any cell to edit that day's count".
Import and Export
- Export JSON: downloads your current data as
ccc-data.json. - Import JSON: opens a file picker; selecting a valid JSON file replaces the current data. Invalid files show an alert.
The data format is described below.
Static Site Mode
Static site mode is active when DATA_URL is set to a non-empty URL string. On load, the app fetches data.json (or whatever file is at that URL) and renders the heatmap from it.
In static site mode:
- All editing controls are hidden.
- Import and export are unavailable.
- A "static site mode: read-only" notice appears below the title.
- Data is never written to
localStorage.
To use static site mode, host ccc.html and data.json on any static hosting service (GitHub Pages, Netlify, etc.), then set DATA_URL to the full URL of data.json.
Configuration
The CONFIG block is at the top of the <script> tag in ccc.html:
const CONFIG = {
DATA_URL: "", // "" = local mode | "<url>" = static site mode
MAX_CUPS: 6, // maximum cups allowed per day
WEEK_START: 1, // first day of the week: 0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat
};
| Option | Type | Default | Description |
|---|---|---|---|
DATA_URL |
string | "" |
URL to fetch data.json from. Empty string activates local mode. |
MAX_CUPS |
integer | 6 |
Maximum cups allowed per day. Controls the colour scale and log/edit caps. |
WEEK_START |
integer | 1 |
First day of the week. 0 = Sunday, 1 = Monday (ISO default), through 6 = Saturday. Affects the heatmap column alignment and day labels. |
Data Format
Data is stored as a JSON object with a single entries key. Each entry is a date string (YYYY-MM-DD) mapped to a cup count (integer, 1–MAX_CUPS). Days with zero cups are omitted.
{
"entries": {
"2026-04-07": 3,
"2026-04-08": 2
}
}
- Dates must match the pattern
YYYY-MM-DD. - Values must be positive integers. Zero or negative values are ignored on import.
- Values exceeding
MAX_CUPSare clamped on import. - Deleting an entry (setting it to 0 via the cell editor) removes the key entirely.
Licence
MIT. see LICENSE.
