Skip to content

Overview

Terminal window
ghchronicle -config config.yaml -card card.svg -card-only
FlagDoes
-card <path>Runs one sweep and writes the SVG there
-card-onlyWrites the SVG and nothing else, so no database is needed
-card-layout <name>One of the thirteen layouts
-card-theme <dark|light|auto|both>Both writes the light card and a _dark twin from one sweep
-card-motion <once|loop|off>How an animated layout moves; the others ignore it
-card-fields <list>Comma-separated, in drawing order
-card-width <pixels>The layout’s own width when left out; on activity-heatmap it buys weeks
-card-speed <0 to 1>How fast an animated layout plays; 0.5, the default, is the pace it always had
-card-layoutsPrints the layouts with their fields and widths, then exits

Without -card-only the card is written as well as everything the sinks would normally get, which is the arrangement for a host that is already collecting and wants a picture too.

A run given -card collects every family, whatever the cadences say, because every number on the card comes from the points of that one sweep and a family skipped as not due would be a zero on the picture.

With -card-only it also leaves the state file exactly as it found it. Nothing that run collected reached a store, so nothing it learned may tell the next collection that a family is already done. It still reads the file, which is what lets it skip the one-off walk of the star history: what the state remembers makes the sweep cheaper, never the card smaller.

So a card, a second card and a collection can all share one state_file, and each of them is the whole account.

Stars, forks, followers and repository count; contributions over the last year; views and unique visitors over GitHub’s fourteen-day traffic window; a sparkline of the contribution calendar; and the top repositories by stars with their language.

Stars and forks are summed from the repositories the sweep collected, because GitHub’s account endpoint reports neither total. That means excluding forks or archived repositories in targets is visible on the card too, which is the honest reading rather than a hidden discrepancy.

-card-fields takes a comma-separated list, in drawing order, from:

stars, forks, followers, repos, contributions, views, visitors, clones, commits, pull_requests, reviews, issues, languages, top_repos, sparkline.

A layout skips a field it cannot draw. An unknown name is an error that lists the valid ones, rather than a silent skip: a typo would otherwise remove a number and nobody would notice until the card was already committed.

Terminal window
ghchronicle -config config.yaml -card card.svg -card-only \
-card-layout github-stats -card-theme dark \
-card-fields repos,stars,forks,followers,commits,pull_requests,languages

Empty means the layout’s default set.

One palette each, and the choice for a README. -card-theme both (or card-theme: both in the Action) writes the light card at the path given and the dark one beside it with _dark before the extension, from the same sweep, so the two can never disagree. Put both in a <picture>, the way GitHub documents showing a different picture per theme:

<picture><source media="(prefers-color-scheme: dark)" srcset="card_dark.svg"><img src="card.svg" alt="My GitHub card"></picture>

This project’s own README does exactly that with its cards.

Self-contained. No external stylesheet, no webfont, no <image> pointing at a URL, no script. GitHub’s camo proxy serves README images from its own domain and blocks all of that, so anything external would simply not render.

Deterministic. The same input produces a byte-identical file, so a scheduled job that commits the card does not produce a diff on every run.

Written atomically. Rendered to a temporary file and renamed, so a reader watching the path never sees half a document.

Animation, where a layout has it, is CSS inside the SVG, and there is no script, ever. Every animation ends on the complete static card, so a renderer that ignores animation shows the finished state, and prefers-reduced-motion switches it off in every mode.

-card-motionWhat the card does
oncePlays when it loads and settles. The default
loopThe same, and whatever the layout has that never ends goes on for ever
offNo animation at all, and a smaller file

An animation here reveals content: a number counts up to what it is, a bar grows to its share, a line draws itself. Playing that again would take back something the reader has already been shown, and a card that keeps hiding its own figures is worse than a still one.

So loop does not mean “and again”. The reveal plays once and settles in both modes, and the only thing that may go on for ever is motion that puts nothing on the card and takes nothing off it. Two layouts have such a thing:

  • terminal, whose cursor blinks at its prompt from the moment the window is drawn. The numbers still type themselves in once. Played once instead, the cursor waits for the last of them, blinks a couple of times and settles lit.
  • ticker, whose band of pills keeps scrolling. Nothing disappears; the same pills come round again.

On every other layout loop draws exactly the card once draws, to the byte. It is accepted rather than refused, so a workflow can set it once and change card-layout freely.

once is still the considerate default for a profile, and more so than before. A card that loops moves for everyone who opens the README, and neither of the two that can is paced: the cursor blinks and the band scrolls with no pause between passes, where the looping cards this replaced held still for seven seconds between plays. A README gives a reader no way to stop it. Their only escape is prefers-reduced-motion, which switches the animation off for them but is a setting for their whole machine, not a control over your card. That is the same reason the layouts page never shows a card looping until the reader presses the toggle (WCAG 2.2.2, Pause, Stop, Hide).

The terminal layout played once: a terminal window whose lines of output each have a number typing itself in, with a lit block cursor at the prompt below them that blinks once the last number lands, and the page can also play it in a loop, where the typing still happens once and only the cursor goes on, blinking from the startThe terminal layout played once: a terminal window whose lines of output each have a number typing itself in, with a lit block cursor at the prompt below them that blinks once the last number lands, and the page can also play it in a loop, where the typing still happens once and only the cursor goes on, blinking from the start
Command for terminal

Binary

Terminal window
ghchronicle -card-layout terminal -card-theme both -card-only -card card.svg -config config.yaml

GitHub Action

- uses: jmrplens/ghchronicle@v1
with:
token: ${{ secrets.GHCHRONICLE_TOKEN }}
mode: card
card: generated/card.svg
card-layout: terminal
card-theme: both

-card-speed is a decimal from 0 to 1, and 0.5 is the default. It is one number for the whole card: every animated layout scales by it, the two continuous motions with the reveals, so a card set slower has a band that takes longer to come round and a cursor that blinks more slowly at the same time.

-card-speedWhat the card does
0The slowest animation, twice as long as the default
0.5Exactly the card this renderer always drew, to the byte. The default
1The fastest, half as long as the default

There is one knob and not one per layout for the reason there is one width and not one per layout: the cycles here were paced against each other, and scaling them together is what keeps the pacing the motion was designed with. A speed outside the range is refused before the sweep runs, naming both ends.

prefers-reduced-motion is untouched by any of this: a reader who has asked their machine for less motion gets no animation to slow down or speed up.

<picture><source media="(prefers-color-scheme: dark)" srcset="generated/card_dark.svg"><img src="generated/card.svg" alt="My GitHub statistics"></picture>

The workflow that keeps it current, for a profile README or any other, is in A card in your profile README.

The renderer lives in internal/render, and Go refuses that import from outside the module, so there is no way to draw a card from your own program by calling into this one. A program that wants an SVG runs the binary with -card and reads the file, the same way it would run any other command: see calling it from a program.