Skip to main content

max / ripgrow

3.7 KB · 125 lines History Blame Raw
1 // ripgrow session sheet — technical-drawing feel. Dense enough to read
2 // from the floor mid-lift, with the load number given the most weight.
3 //
4 // Layout: masthead across the top with title + profile + date; a warmup
5 // call-out band; a wide bordered table where each row is a slot with big
6 // numeric SETS / REPS / LOAD columns.
7 //
8 // Invoked as `typst compile --input data-path=data.json sheet.typ out.pdf`.
9 // Data schema: see pdf.rs render_json.
10
11 #let data = json(sys.inputs.at("data-path"))
12
13 #set page(paper: "us-letter", margin: (top: 0.55in, bottom: 0.55in, x: 0.55in))
14 #set text(font: "Helvetica", size: 11pt)
15
16 #let rule-heavy = 1.5pt
17 #let rule-light = 0.4pt
18 #let ink = rgb("#0f0f0f")
19 #let dim = rgb("#585858")
20 #let band = luma(238)
21
22 // -- masthead ---------------------------------------------------------------
23 #grid(
24 columns: (1fr, auto),
25 align: (left + bottom, right + bottom),
26 [
27 #text(size: 26pt, weight: "black", tracking: 3pt, fill: ink)[RIPGROW]
28 #v(-0.35em)
29 #text(size: 8pt, tracking: 2pt, fill: dim)[SESSION SHEET / TRAINING PROGRAM]
30 ],
31 [
32 #text(size: 12pt, weight: "bold", tracking: 1pt, fill: ink)[#upper(data.profile)]
33 #v(-0.35em)
34 #text(size: 10pt, tracking: 1pt, fill: dim)[#data.date]
35 ]
36 )
37
38 #v(0.25em)
39 #line(length: 100%, stroke: rule-heavy + ink)
40 #v(0.6em)
41
42 // -- warmup band ------------------------------------------------------------
43 #block(
44 fill: band,
45 inset: (x: 10pt, y: 8pt),
46 width: 100%,
47 )[
48 #grid(
49 columns: (auto, 1fr),
50 align: (left + horizon, left + horizon),
51 column-gutter: 14pt,
52 [
53 #text(size: 8pt, weight: "bold", tracking: 2pt, fill: dim)[WARMUP]
54 ],
55 [
56 #text(size: 12pt, weight: "medium", fill: ink)[
57 #if data.warmup.len() > 0 [
58 #data.warmup.join(" • ")
59 ] else [
60
61 ]
62 ]
63 ],
64 )
65 ]
66
67 #v(0.6em)
68
69 // -- main table -------------------------------------------------------------
70 //
71 // Columns: #, exercise, sets, reps, load, unit, vs. Rules are heavy at the
72 // top and bottom, light between rows. LOAD gets the largest type since that
73 // is what the lifter checks between working sets.
74
75 #let header-cell(body) = text(
76 size: 8pt,
77 weight: "bold",
78 tracking: 2pt,
79 fill: dim,
80 )[#body]
81
82 #let idx-cell(body) = text(size: 16pt, weight: "bold", fill: ink)[#body]
83 #let name-cell(body) = text(size: 15pt, weight: "medium", fill: ink)[#body]
84 #let num-cell(body) = text(size: 16pt, weight: "medium", fill: ink)[#body]
85 #let load-cell(body) = text(size: 18pt, weight: "bold", fill: ink)[#body]
86 #let unit-cell(body) = text(size: 10pt, weight: "medium", tracking: 1pt, fill: dim)[#body]
87 #let vs-cell(body) = text(size: 9pt, tracking: 1pt, fill: dim)[#upper(body)]
88
89 #table(
90 columns: (28pt, 1fr, 44pt, 44pt, 84pt, 44pt, 52pt),
91 align: (center + horizon, left + horizon, right + horizon, right + horizon, right + horizon, left + horizon, right + horizon),
92 stroke: (x, y) => (
93 top: if y == 0 { rule-heavy + ink }
94 else if y == 1 { rule-heavy + ink }
95 else { rule-light + dim },
96 bottom: if y == data.slots.len() { rule-heavy + ink } else { none },
97 left: none,
98 right: none,
99 ),
100 inset: (x: 6pt, y: 10pt),
101
102 header-cell("#"),
103 header-cell("EXERCISE"),
104 header-cell("SETS"),
105 header-cell("REPS"),
106 header-cell("LOAD"),
107 header-cell("UNIT"),
108 header-cell("VS LAST"),
109
110 ..data.slots.enumerate().map(((i, slot)) => (
111 idx-cell(str(i + 1)),
112 name-cell(slot.name),
113 num-cell(slot.sets),
114 num-cell(slot.reps),
115 load-cell(slot.load),
116 unit-cell(slot.unit),
117 vs-cell(slot.glyph),
118 )).flatten()
119 )
120
121 #v(0.5em)
122 #text(size: 7pt, tracking: 2pt, fill: dim)[
123 RIPGROW · #data.profile · #data.date · #str(data.slots.len()) SLOT#if data.slots.len() != 1 [S]
124 ]
125