BJJ Journal
BJJ Journal is a structured training log I built for my own practice. I train a few times a week and wanted something that would answer specific questions: what have I been drilling, how much time have I logged, who do I roll with most? The spreadsheets and note apps I tried either knew too little about jiu-jitsu or required me to manage a back end I did not want to maintain. So I built the tool I wanted.
The core bet was that a single-user training log does not need a server. The journal lives in localStorage, the app ships as a static site, and there is nothing to deploy, maintain, or pay for beyond a GitHub Pages URL.

The home page aggregates everything live from localStorage — sessions, mat time, moves drilled, rolls, and partners.
Logging a session
Each session records a start and end time, optional notes rendered as Markdown or Org-mode, the drills you worked, and every roll. Drills get a category tag — attack, defense, or transition — and rolls link to a named partner. The form is minimal: pick times, add rows, save.

Session detail breaks the training hour into its parts: which techniques, which partners, and any notes from the mat.
The category badges carry through everywhere. On the Moves page every technique is tagged in the color that tells me at a glance whether I am over-indexing on attacks or neglecting defense.
Moves and partner tracking

The Moves page ranks every technique by repetition count and filters by category or date range — useful for seeing how your focus has shifted over a training block.
The Moves page is the view I check most. It surfaces which techniques I have drilled the most, when I last worked each one, and links back to every session that included it. Fuzzy search handles the inevitable naming drift that comes from logging moves over months.

The Rolls page keeps a running tally by partner — total rolls, last session — plus every individual roll with notes.
localStorage as a feature
I chose localStorage deliberately, not as a compromise. The threat model for a personal training log is simple: I own the data, I train at one gym, and there is no case where I need real-time sync or shared access. A cloud backend would add complexity, infrastructure cost, and a privacy surface without buying anything I actually care about.
The tradeoff is portability. I solved that with JSON export and import baked into the Log page. One click produces a dated backup file; importing is idempotent so restoring is always safe. The same JSON schema powers the example site’s six months of seeded demo data, which means the format has been stable by construction from the start.

Navigating the example site: training summary, move rankings with category filters, a move detail page, and partner roll history.
Stack
| Layer | Choice |
|---|---|
| UI | React 18 + TypeScript |
| Build | Vite |
| Design system | PicoCSS |
| Routing | React Router |
| Storage | localStorage with JSON export / import |
| Deployment | GitHub Pages via GitHub Actions |
One detail I like: the header is a 10px black belt with a red rank bar and four white stripes. It is purely cosmetic, but it sets the context of the app in one glance before a word is read.
Example site
The live example ships seeded with six months of deterministic mock data — real move names, realistic session cadences, nine named partners, and roll notes. I built it so the site is immediately useful to evaluate rather than an empty screen. Edits land in your browser’s localStorage under a separate key, so you can explore freely without touching a real journal.