Files
ghstats/docs/design-guidelines.md
T
tiennm99 94e13d2c65 docs: resync with current state across all project docs
- design-guidelines: every dimension was stale — card frame 340x200
  (was 500x220), corner radius 6, title 15px at (20,30), row y0/dy,
  donut centre (250,110) r=55/30, topN=5, legend y0=55 dy=20, bar chart
  area [35,325]x[45,155], area chart [28,312]x[45,150], icon scale 0.75.
- code-standards: FetchContributionsAllTime signature now ctx-first,
  viewbox 500x220 → 340x200.
- codebase-summary: test coverage lists main_test.go +
  TestDonutSingleSlice/Empty; filename convention says plain kebab-case
  (no numeric prefix).
- project-overview-pdr: forks/private defaults now on, not off.
- project-roadmap: add Phase 7 (Marketplace polish — resize, numeric-
  prefix drop, v1 floating tag, rename-rollback). Renumber planned
  phases 8-11. Fix "hard width 500 px" limitation.
- deployment-guide: document update-major-tag job; note
  Marketplace listing name is `ghstats-cards`.
2026-04-19 00:09:00 +07:00

4.6 KiB
Raw Blame History

Design Guidelines

Visual conventions for ghstats SVG cards. All cards share a single frame shape so they stack cleanly in a README — two cards sit side-by-side inside GitHub's ~816 px content column.

Card frame

Property Value
Width × Height 340 × 200 (matches github-profile-summary-cards)
Corner radius 6 px
Stroke theme.Stroke at theme.StrokeOpacity
Fill theme.Background
Font family 'Segoe UI', Ubuntu, Sans-Serif
Title 15 px, weight 600, theme.Title, anchored at (20, 30)

Generated by header(width, height, bg, stroke, strokeOpacity, titleColor, title) in internal/card/svg.go.

Theme role mapping

Theme field Used for
Title Card title text
Text Primary content (values, names)
Background Card fill + stroke around donut slices to separate colors
Stroke + StrokeOpacity Card outline
Muted Axis lines, axis labels, icons, legend metadata
Accent Bars, area fills, stat values, fallback slice color

Cards MUST NOT hardcode colors outside these fields. If a new visual needs a shade, pick the closest existing field — don't extend the schema without a strong reason.

Row-based cards (profile, stats)

Single-column rows of icon + label or icon + label + value.

Metric Profile Stats
First row baseline (y) 60 55
Row spacing 20 px 20 px
Row x padding 20 20
Icon scale 12/16 = 0.75 from 16×16 Octicon viewBox same
Icon color theme.Muted same
Value font 12 px, theme.Text 12 px, weight 600, theme.Accent, right-anchored at x = 320

Cap rows at what fits: up to 7 rows per card. Stats splits commits into lifetime + last-year rows.

Donut cards (language breakdowns)

Metric Value
Donut centre (250, 110)
Outer radius 55
Inner radius 30
Top-N entries shown 5 (overflow collapses into "Other")
Slice stroke theme.Background, 1.5 px (gap between slices)
Legend origin (20, 55)
Legend row height 20 px
Swatch size 10 × 10
Legend font 11 px

Language colors come from linguist via GraphQL (repo.languages.edges[].node.color). Missing colors fall back to theme.Accent.

When there's exactly one slice (one language at 100%), the renderer emits two concentric <circle> elements instead of a pie arc, because SVG's A command from point P back to the same P draws nothing. Regression guarded by TestDonutSingleSlice.

Bar-chart cards (productive time)

Metric Value
Chart area x ∈ [35, 325], y ∈ [45, 155] (110 tall)
Bars 24 bars, 1 px gap
Bar fill theme.Accent
Y-axis ticks niceTicks(max, 5) — 1/2/5 × 10^k ladder
X-axis labels Hours 0, 6, 12, 18, 23
Axis caption "hour of day" bottom-center
Title format Commits by Hour (<window>, UTC±N.NN)
Hover <title>HH:00 — N commits</title> inside each bar

Area-chart cards (contributions)

Metric Value
Chart area x ∈ [28, 312], y ∈ [45, 150] (105 tall)
Curve Catmull-Rom → cubic Bezier (tension 0.5)
Fill theme.Accent at 25% opacity
Stroke theme.Accent, 2 px
Y-axis Both sides, mirrored, same tick values
X-axis labels mm/yy, stride-thinned to ~6 labels regardless of bucket count
Axis caption "mm/yy" bottom-center
First/last labels Always printed (pinned endpoints)

Missing months in the [first, last] range are inserted as zero-count rows to keep the curve time-continuous.

Icons

  • Sourced from Primer Octicons 16×16 set.
  • Stored as raw <path d="…"/> strings in internal/card/icons.go.
  • Rendered inside <g transform="translate(x,y) scale(0.75)" fill="muted">…</g> (scaled to fit the 12 px box).
  • Used: iconRepos, iconCompany, iconLocation, iconClock, iconLink, iconPeople, iconStar, iconCommit, iconPR, iconIssue, iconReview.

Add new icons by copying the <path> from Octicons and appending to icons.go. Keep them to the same 16×16 viewBox so the existing scale math applies.

Accessibility

  • Contrast is the theme author's responsibility — we don't validate at runtime.
  • Tooltips (<title>) on productive-time bars let screen readers announce counts.
  • No motion, no <animate> elements — profile READMEs render statically.

Text overflow

  • Long strings (bio, repo names) are not truncated; they're XML-escaped and printed as-is.
  • If a card looks crowded at 340 px width, that's a card design problem — fix the layout, not the data.