docs: add project documentation set

Seven canonical docs under docs/ per the project structure convention:

- project-overview-pdr.md   users, non-goals, requirements
- codebase-summary.md       directory layout, module responsibilities
- system-architecture.md    runtime phases, GraphQL flow, SVG primitives
- code-standards.md         YAGNI/KISS/DRY, Go conventions, commit rules
- design-guidelines.md      frame dimensions, theme roles, per-card specs
- deployment-guide.md       Action/binary/Docker paths, release process
- project-roadmap.md        done phases (0-5), planned phases (6-9)

All files under the 800-line cap. Each leans on tables; grammar
sacrificed for concision per project rules.
This commit is contained in:
tiennm99 committed 2026-04-18 22:10:09 +07:00
1 parent 0168fd1f56
commit 8b3a8e1932
7 files changed
+740

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
# Design Guidelines
Visual conventions for ghstats SVG cards. All cards share a single frame shape so they stack cleanly in a README.
## Card frame
| Property | Value |
| --- | --- |
| Width × Height | **500 × 220** |
| Corner radius | 8 px |
| Stroke | `theme.Stroke` at `theme.StrokeOpacity` |
| Fill | `theme.Background` |
| Font family | `'Segoe UI', Ubuntu, Sans-Serif` |
| Title | 18 px, weight 600, `theme.Title`, anchored at `(25, 35)` |
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 | Value |
| --- | --- |
| First row baseline (y) | 70 |
| Row spacing | 24 px |
| Icon scale | `14/16 = 0.875` from 16×16 Octicon viewBox |
| Icon color | `theme.Muted` |
| Right-aligned value anchor (stats) | `x = 475`, `text-anchor="end"` |
| Value font weight | 600 |
| Value color | `theme.Accent` |
Cap rows at what fits: 7 for profile, 7 for stats (commits row splits into lifetime + last-year).
## Donut cards (language breakdowns)
| Metric | Value |
| --- | --- |
| Donut centre | `(380, 120)` |
| Outer radius | 70 |
| Inner radius | 38 |
| Top-N entries shown | 6 (overflow collapses into "Other") |
| Slice stroke | `theme.Background`, 1.5 px (gap between slices) |
| Legend origin | `(30, 70)` |
| Legend row height | 22 px |
| Swatch size | 12 × 12 |
Language colors come from linguist via GraphQL (`repo.languages.edges[].node.color`). Missing colors fall back to `theme.Accent`.
## Bar-chart cards (productive time)
| Metric | Value |
| --- | --- |
| Chart area | `x ∈ [50, 475]`, `y ∈ [60, 170]` (110 tall) |
| Bars | 24 bars, 2 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 ∈ [35, 465]`, `y ∈ [60, 180]` (120 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](https://primer.style/octicons/) 16×16 set.
- Stored as raw `<path d="…"/>` strings in `internal/card/icons.go`.
- Rendered inside `<g transform="translate(x,y) scale(0.875)" fill="muted">…</g>`.
- 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 500 px width, that's a card design problem — fix the layout, not the data.