Skip to content

Commit c17f60c

Browse files
committed
[Technique Report] Init diamant, azul, and splendor technique report
1 parent a183ac6 commit c17f60c

8 files changed

Lines changed: 1286 additions & 52 deletions
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
/**
3+
* DotGrid — a waffle/unit chart for a single proportion, as build-time
4+
* HTML/CSS (no PNG, no JS).
5+
*
6+
* A grid of `total` cells with the first `value`% filled in ink and the rest
7+
* in a light gray. It reads a percentage at a glance and echoes the site's
8+
* geometric grid identity. Every color is a token, so it re-themes in dark
9+
* mode for free. Pair it with a big `stat` and a `note` for context.
10+
*
11+
* Usage (in .mdx):
12+
* <DotGrid value={57.5} stat="57.5%" note="first-mover win rate"
13+
* filledLabel="first mover" emptyLabel="rest" />
14+
*/
15+
interface Props {
16+
/** Filled share, as a percentage of `total` (0–100). */
17+
value: number;
18+
/** Number of cells in the grid. Default 100 (a 10×10 percent grid). */
19+
total?: number;
20+
/** Cells per row. Default 10. */
21+
columns?: number;
22+
/** Big headline shown beside the grid. Defaults to `${value}%`. */
23+
stat?: string;
24+
/** Mono caption under the headline. */
25+
note?: string;
26+
/** Optional legend text for the filled / empty swatches. */
27+
filledLabel?: string;
28+
emptyLabel?: string;
29+
}
30+
31+
const {
32+
value,
33+
total = 100,
34+
columns = 10,
35+
stat,
36+
note,
37+
filledLabel,
38+
emptyLabel,
39+
} = Astro.props;
40+
41+
const filled = Math.max(0, Math.min(total, Math.round((value / 100) * total)));
42+
const cells = Array.from({ length: total }, (_, i) => i < filled);
43+
const headline = stat ?? `${value}%`;
44+
const hasLegend = Boolean(filledLabel || emptyLabel);
45+
---
46+
47+
<div class="dot-grid" role="img" aria-label={`${headline} — ${note ?? "filled share"}`}>
48+
<div class="dot-grid__grid" style={`--cols: ${columns}`}>
49+
{
50+
cells.map((on) => (
51+
<span class:list={["dot-grid__cell", on && "dot-grid__cell--on"]} />
52+
))
53+
}
54+
</div>
55+
56+
<div class="dot-grid__aside">
57+
<div class="dot-grid__stat">{headline}</div>
58+
{note && <div class="dot-grid__note">{note}</div>}
59+
{
60+
hasLegend && (
61+
<div class="dot-grid__legend">
62+
{filledLabel && (
63+
<span class="dot-grid__legend-item">
64+
<span class="dot-grid__swatch dot-grid__swatch--on" />
65+
{filledLabel}
66+
</span>
67+
)}
68+
{emptyLabel && (
69+
<span class="dot-grid__legend-item">
70+
<span class="dot-grid__swatch" />
71+
{emptyLabel}
72+
</span>
73+
)}
74+
</div>
75+
)
76+
}
77+
</div>
78+
</div>
79+
80+
<style>
81+
.dot-grid {
82+
display: flex;
83+
flex-wrap: wrap;
84+
align-items: center;
85+
gap: var(--space-8);
86+
width: 100%;
87+
padding: var(--space-6);
88+
border: var(--guideline-width) solid var(--guideline-color);
89+
background: var(--c-gray-50);
90+
}
91+
92+
.dot-grid__grid {
93+
display: grid;
94+
grid-template-columns: repeat(var(--cols), 1fr);
95+
gap: 3px;
96+
flex: 1 1 14rem;
97+
max-width: 18rem;
98+
}
99+
100+
.dot-grid__cell {
101+
aspect-ratio: 1;
102+
background: var(--c-gray-200);
103+
border-radius: 1px;
104+
}
105+
106+
.dot-grid__cell--on {
107+
background: var(--c-ink);
108+
}
109+
110+
.dot-grid__aside {
111+
flex: 1 1 8rem;
112+
}
113+
114+
.dot-grid__stat {
115+
font-family: var(--font-display-serif);
116+
font-weight: 400;
117+
font-size: var(--text-4xl);
118+
line-height: 1.1;
119+
color: var(--c-ink);
120+
}
121+
122+
.dot-grid__note {
123+
margin-top: var(--space-1);
124+
font-family: var(--font-mono);
125+
font-size: var(--text-sm);
126+
color: var(--c-gray-600);
127+
}
128+
129+
.dot-grid__legend {
130+
display: flex;
131+
flex-wrap: wrap;
132+
gap: var(--space-3);
133+
margin-top: var(--space-4);
134+
font-family: var(--font-mono);
135+
font-size: var(--text-xs);
136+
color: var(--c-gray-500);
137+
}
138+
139+
.dot-grid__legend-item {
140+
display: inline-flex;
141+
align-items: center;
142+
gap: var(--space-2);
143+
}
144+
145+
.dot-grid__swatch {
146+
display: inline-block;
147+
width: 10px;
148+
height: 10px;
149+
background: var(--c-gray-200);
150+
border-radius: 1px;
151+
}
152+
153+
.dot-grid__swatch--on {
154+
background: var(--c-ink);
155+
}
156+
</style>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
/**
3+
* StatRow — a band of "big number" callouts for a report's headline figures.
4+
*
5+
* Pure HTML/CSS (no PNG, no JS). The big value uses the display serif (the
6+
* same face as the page headings) and the label uses the mono stack, so the
7+
* band reads as part of the report's type system and re-themes automatically
8+
* via tokens.css. Use it for the 3–4 numbers a reader should remember.
9+
*
10+
* Usage (in .mdx):
11+
* <StatRow items={[
12+
* { value: "27", label: "turns to win", sub: "per player" },
13+
* { value: "½", label: "of your cards score 0 points" },
14+
* ]} />
15+
*/
16+
interface Item {
17+
/** The headline number, kept as a string so "½", "+12pp", "≈27" all work. */
18+
value: string;
19+
/** Short mono caption under the number. */
20+
label: string;
21+
/** Optional smaller note under the label. */
22+
sub?: string;
23+
}
24+
25+
interface Props {
26+
items: Item[];
27+
}
28+
29+
const { items } = Astro.props;
30+
---
31+
32+
<div class="stat-row">
33+
{
34+
items.map((item) => (
35+
<div class="stat-row__cell">
36+
<div class="stat-row__value">{item.value}</div>
37+
<div class="stat-row__label">{item.label}</div>
38+
{item.sub && <div class="stat-row__sub">{item.sub}</div>}
39+
</div>
40+
))
41+
}
42+
</div>
43+
44+
<style>
45+
.stat-row {
46+
display: grid;
47+
grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
48+
width: 100%;
49+
border: var(--guideline-width) solid var(--guideline-color);
50+
background: var(--c-gray-50);
51+
}
52+
53+
.stat-row__cell {
54+
display: flex;
55+
flex-direction: column;
56+
align-items: center;
57+
text-align: center;
58+
gap: var(--space-2);
59+
padding: var(--space-6) var(--space-4);
60+
border-left: var(--guideline-width) solid var(--guideline-color);
61+
}
62+
63+
/* The first cell in each row would otherwise carry a stray left rule; the
64+
auto-fit grid makes "first in row" non-static, so we drop the very first
65+
and let wrapping rows keep their leading divider (a clean enough seam). */
66+
.stat-row__cell:first-child {
67+
border-left: none;
68+
}
69+
70+
.stat-row__value {
71+
font-family: var(--font-display-serif);
72+
font-weight: 400;
73+
font-size: var(--text-4xl);
74+
line-height: 1.1;
75+
color: var(--c-ink);
76+
}
77+
78+
.stat-row__label {
79+
font-family: var(--font-mono);
80+
font-size: var(--text-sm);
81+
line-height: 1.4;
82+
color: var(--c-gray-600);
83+
}
84+
85+
.stat-row__sub {
86+
font-family: var(--font-mono);
87+
font-size: var(--text-xs);
88+
color: var(--c-gray-400);
89+
}
90+
91+
@media (max-width: 480px) {
92+
.stat-row {
93+
grid-template-columns: repeat(2, 1fr);
94+
}
95+
96+
.stat-row__value {
97+
font-size: var(--text-3xl);
98+
}
99+
}
100+
</style>

0 commit comments

Comments
 (0)