@casomoltd/tooling 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -21
- package/bin/check-hooks.sh +84 -0
- package/bin/check-personal-data.mjs +270 -0
- package/bin/render-figures.mjs +159 -0
- package/bin/render-report.mjs +47 -46
- package/bin/spec-check.mjs +229 -0
- package/bin/utils.mjs +19 -0
- package/bin/verify-pack.mjs +1 -0
- package/package.json +13 -6
- package/styles/casomo-spec.css +230 -0
- package/styles/casomo-tokens.css +43 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/* Casomo document chrome — plain by design.
|
|
2
|
+
*
|
|
3
|
+
* A spec is read to extract decisions from, not to be impressed by, so this
|
|
4
|
+
* sheet is deliberately quiet: white ground, one link colour, hairline rules
|
|
5
|
+
* instead of cards and shadows, and colour used only where it carries
|
|
6
|
+
* meaning. It styles the skeleton's own class names and nothing else, so a
|
|
7
|
+
* page needs no markup of its own.
|
|
8
|
+
*
|
|
9
|
+
* Colour is a signal, not decoration. The four callout kinds and the status
|
|
10
|
+
* tags keep a hue because they mean something; everything else is ink on
|
|
11
|
+
* white. If a rule here paints something for emphasis alone, it is a bug.
|
|
12
|
+
*
|
|
13
|
+
* Tokens come from casomo-tokens.css, which is the brand sheet and is left
|
|
14
|
+
* alone. The block below re-points the handful this sheet reads, so a page
|
|
15
|
+
* that wants the brand look can drop it and get one.
|
|
16
|
+
*/
|
|
17
|
+
:root {
|
|
18
|
+
--bg: #ffffff;
|
|
19
|
+
--card: #ffffff;
|
|
20
|
+
--panel: #f8f9fa;
|
|
21
|
+
--ink: #202122;
|
|
22
|
+
--muted: #54595d;
|
|
23
|
+
--dim: #72777d;
|
|
24
|
+
--line: #a2a9b1;
|
|
25
|
+
--hair: #eaecf0;
|
|
26
|
+
--accent: #3366cc;
|
|
27
|
+
--radius: 2px;
|
|
28
|
+
--shadow: none;
|
|
29
|
+
--measure: 62rem;
|
|
30
|
+
/* Everything read as prose shares one measure; content that needs the
|
|
31
|
+
width -- tables, code, figures -- takes the whole column. One value, so
|
|
32
|
+
a callout and the paragraph above it end at the same edge. */
|
|
33
|
+
--reading: 44rem; /* clears the floated contents card in the masthead */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
html { -webkit-text-size-adjust: 100%; }
|
|
37
|
+
body {
|
|
38
|
+
margin: 0; background: var(--bg); color: var(--ink);
|
|
39
|
+
font: 15px/1.65 var(--sans); }
|
|
40
|
+
.wrap { max-width: var(--measure); margin: 0 auto; padding: 0 16px; }
|
|
41
|
+
|
|
42
|
+
/* ---- masthead: eyebrow, title, contents and the Overview card,
|
|
43
|
+
and no loose prose (S11) ---- */
|
|
44
|
+
header.hero { border-bottom: 1px solid var(--line); padding: 28px 0 18px; }
|
|
45
|
+
header.hero .eyebrow {
|
|
46
|
+
font: 500 12px/1.5 var(--mono); color: var(--muted);
|
|
47
|
+
letter-spacing: .02em; margin-bottom: 10px; }
|
|
48
|
+
header.hero h1 {
|
|
49
|
+
font-size: 30px; line-height: 1.2; font-weight: 600; margin: 0 0 8px; }
|
|
50
|
+
header.hero a { color: var(--accent); }
|
|
51
|
+
.pillrow { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 6px 14px; }
|
|
52
|
+
.pill { font-size: 12.5px; color: var(--muted); }
|
|
53
|
+
.pill + .pill::before { content: "· "; color: var(--dim); margin-left: -8px; }
|
|
54
|
+
|
|
55
|
+
/* ---- contents: a box the brief flows around, the way a reference page
|
|
56
|
+
does it. Floated rather than placed in a column, so a short brief
|
|
57
|
+
does not leave a tall empty gutter beside it. ---- */
|
|
58
|
+
nav.toc {
|
|
59
|
+
float: right; width: 15rem; margin: 4px 0 14px 28px;
|
|
60
|
+
border: 1px solid var(--line); background: var(--panel); padding: 10px 14px; }
|
|
61
|
+
nav.toc .wrap { max-width: none; margin: 0; padding: 0; display: block; }
|
|
62
|
+
nav.toc::before {
|
|
63
|
+
content: "Contents"; display: block; font-weight: 600; font-size: 13px;
|
|
64
|
+
margin-bottom: 6px; padding-bottom: 5px; border-bottom: 1px solid var(--line); }
|
|
65
|
+
nav.toc a {
|
|
66
|
+
display: block; color: var(--accent); text-decoration: none;
|
|
67
|
+
font-size: 13px; line-height: 1.5; padding: 1px 0; }
|
|
68
|
+
nav.toc a:hover { text-decoration: underline; }
|
|
69
|
+
header.hero::after { content: ""; display: block; clear: both; }
|
|
70
|
+
|
|
71
|
+
/* ---- numbering, generated ----
|
|
72
|
+
Sections number themselves from document order, so a section that moves
|
|
73
|
+
renumbers itself and everything after it. Nothing in the markup carries a
|
|
74
|
+
number, which means a heading and its contents entry cannot disagree and a
|
|
75
|
+
renumber cannot leave a subsection behind. Prose cites a section by NAME,
|
|
76
|
+
never by number -- a named link is right wherever the section ends up. */
|
|
77
|
+
body { counter-reset: sec; }
|
|
78
|
+
section[id]:not(.unnumbered) { counter-increment: sec; counter-reset: sub; }
|
|
79
|
+
section[id]:not(.unnumbered) > h2::before { content: counter(sec) " · "; }
|
|
80
|
+
section[id]:not(.unnumbered) h3 { counter-increment: sub; }
|
|
81
|
+
section[id]:not(.unnumbered) h3::before {
|
|
82
|
+
content: counter(sec) "." counter(sub) " · "; }
|
|
83
|
+
nav.toc { counter-reset: toc; }
|
|
84
|
+
nav.toc a:not(.unnumbered) { counter-increment: toc; }
|
|
85
|
+
nav.toc a:not(.unnumbered)::before { content: counter(toc) " · "; }
|
|
86
|
+
|
|
87
|
+
/* ---- headings: a rule under each, no numbers bolted on ---- */
|
|
88
|
+
section { margin: 30px 0; }
|
|
89
|
+
h2 {
|
|
90
|
+
font-size: 22px; font-weight: 600; margin: 32px 0 10px;
|
|
91
|
+
padding-bottom: 5px; border-bottom: 1px solid var(--line); }
|
|
92
|
+
h2::before { content: none; }
|
|
93
|
+
h3 { font-size: 17px; font-weight: 600; margin: 24px 0 8px; }
|
|
94
|
+
h4 { font-size: 15px; font-weight: 600; margin: 18px 0 6px; }
|
|
95
|
+
h5 { font-size: 14px; font-weight: 600; margin: 14px 0 4px; }
|
|
96
|
+
p { margin: 0 0 12px; max-width: var(--reading); }
|
|
97
|
+
/* Justified with hyphenation, so a justified line does not open rivers.
|
|
98
|
+
Code, tables and figures keep their own alignment. */
|
|
99
|
+
p, li, .callout, .legend { text-align: justify; hyphens: auto; }
|
|
100
|
+
/* A box's padding and border sit inside the measure, so its outer edge
|
|
101
|
+
lines up with the paragraphs beside it. */
|
|
102
|
+
.callout, .card { box-sizing: border-box; }
|
|
103
|
+
ul, ol { max-width: var(--reading); }
|
|
104
|
+
a { color: var(--accent); }
|
|
105
|
+
a:visited { color: #795cb2; }
|
|
106
|
+
/* An unbreakable path in a justified line stretches every gap before it,
|
|
107
|
+
so inline code may wrap; a split path reads better than a river. */
|
|
108
|
+
code:not(pre code) {
|
|
109
|
+
overflow-wrap: anywhere;
|
|
110
|
+
font: 13px var(--mono); background: var(--panel);
|
|
111
|
+
border: 1px solid var(--hair); border-radius: 2px; padding: 0 3px; }
|
|
112
|
+
|
|
113
|
+
/* ---- boxes: a thin rule, never a shadow ---- */
|
|
114
|
+
.card {
|
|
115
|
+
border: 1px solid var(--line); background: var(--card);
|
|
116
|
+
padding: 14px 16px; margin: 14px 0; max-width: var(--reading); }
|
|
117
|
+
.callout {
|
|
118
|
+
border: 1px solid var(--hair); border-left: 3px solid var(--dim);
|
|
119
|
+
background: var(--panel); padding: 10px 14px; margin: 14px 0;
|
|
120
|
+
max-width: var(--reading); }
|
|
121
|
+
.callout .lbl {
|
|
122
|
+
display: block; font-weight: 600; font-size: 13px; margin-bottom: 4px; }
|
|
123
|
+
.c-info { border-left-color: var(--info); }
|
|
124
|
+
.c-decide { border-left-color: var(--gen); }
|
|
125
|
+
.c-warn { border-left-color: var(--amber); }
|
|
126
|
+
.c-key { border-left-color: var(--ok); }
|
|
127
|
+
|
|
128
|
+
/* ---- tables ---- */
|
|
129
|
+
table {
|
|
130
|
+
border-collapse: collapse; width: 100%; margin: 14px 0; font-size: 14px; }
|
|
131
|
+
th, td {
|
|
132
|
+
border: 1px solid var(--line); padding: 7px 9px;
|
|
133
|
+
text-align: left; vertical-align: top; }
|
|
134
|
+
th { background: var(--hair); font-weight: 600; }
|
|
135
|
+
td.ok { color: var(--ok); }
|
|
136
|
+
|
|
137
|
+
/* ---- status words: colour without a badge ---- */
|
|
138
|
+
.tag {
|
|
139
|
+
font: 600 12px var(--mono); letter-spacing: .01em; white-space: nowrap; }
|
|
140
|
+
.tag.ok { color: var(--ok); }
|
|
141
|
+
.tag.red { color: var(--red); }
|
|
142
|
+
.tag.amber { color: var(--amber); }
|
|
143
|
+
.tag.gen { color: var(--gen); }
|
|
144
|
+
.tag.new { color: var(--info); }
|
|
145
|
+
|
|
146
|
+
/* ---- layout helpers ---- */
|
|
147
|
+
.grid, .grid2, .grid3 { display: grid; gap: 12px; margin: 14px 0; }
|
|
148
|
+
.grid2 { grid-template-columns: repeat(2, 1fr); }
|
|
149
|
+
.grid, .grid3 { grid-template-columns: repeat(3, 1fr); }
|
|
150
|
+
@media (max-width: 820px) {
|
|
151
|
+
.grid, .grid2, .grid3 { grid-template-columns: 1fr; }
|
|
152
|
+
.wrap { padding: 0 14px; }
|
|
153
|
+
nav.toc { float: none; width: auto; margin: 14px 0; }
|
|
154
|
+
}
|
|
155
|
+
.stat {
|
|
156
|
+
border: 1px solid var(--line); background: var(--panel);
|
|
157
|
+
padding: 10px 12px; font: 600 17px/1.3 var(--sans); }
|
|
158
|
+
.statlbl {
|
|
159
|
+
display: block; font: 400 12px/1.4 var(--sans);
|
|
160
|
+
color: var(--muted); margin-bottom: 3px; }
|
|
161
|
+
|
|
162
|
+
/* ---- code ---- */
|
|
163
|
+
pre {
|
|
164
|
+
background: var(--code-bg); border: 1px solid var(--line);
|
|
165
|
+
border-radius: 2px; padding: 12px 14px; overflow-x: auto; margin: 12px 0; }
|
|
166
|
+
pre code { font: 13px/1.55 var(--mono); }
|
|
167
|
+
|
|
168
|
+
/* ---- figures ----
|
|
169
|
+
`.figure-card` holds a PRE-RENDERED svg and is deliberately not called
|
|
170
|
+
`.mermaid`: a mermaid runtime treats such an element as a diagram source
|
|
171
|
+
and would try to parse the rendered svg as text. */
|
|
172
|
+
.figure-card, .mermaid {
|
|
173
|
+
border: 1px solid var(--line); background: var(--card);
|
|
174
|
+
padding: 16px; margin: 14px 0; text-align: center; overflow-x: auto; }
|
|
175
|
+
.legend {
|
|
176
|
+
font-size: 13px; color: var(--muted); margin: -4px 0 16px;
|
|
177
|
+
max-width: var(--reading); }
|
|
178
|
+
|
|
179
|
+
/* ---- lists and odds ---- */
|
|
180
|
+
ul.tight, ol.tight { margin: 8px 0; padding-left: 20px; }
|
|
181
|
+
ul.tight li, ol.tight li { margin-bottom: 5px; }
|
|
182
|
+
.kbd {
|
|
183
|
+
font: 12px var(--mono); border: 1px solid var(--line);
|
|
184
|
+
border-bottom-width: 2px; border-radius: 3px; padding: 1px 5px;
|
|
185
|
+
background: var(--panel); }
|
|
186
|
+
.q { font-weight: 600; }
|
|
187
|
+
.layer {
|
|
188
|
+
border: 1px solid var(--line); padding: 12px 14px; margin: 10px 0; }
|
|
189
|
+
.layer h4 { margin-top: 0; }
|
|
190
|
+
.layer .n {
|
|
191
|
+
display: inline-block; min-width: 1.5em; font-weight: 600;
|
|
192
|
+
color: var(--muted); }
|
|
193
|
+
.big-arrow { color: var(--dim); text-align: center; margin: 6px 0; }
|
|
194
|
+
footer {
|
|
195
|
+
border-top: 1px solid var(--line); margin-top: 36px;
|
|
196
|
+
padding-top: 14px; font-size: 13px; color: var(--muted); }
|
|
197
|
+
|
|
198
|
+
/* ---- print ----
|
|
199
|
+
A spec is read on screen and marked up on a tablet, and only the second
|
|
200
|
+
surface has a fixed page. So the measure gives way to the page box, and
|
|
201
|
+
nothing carrying an argument may break across a leaf: a figure parted
|
|
202
|
+
from its legend, or a row from its heading, is read wrong rather than
|
|
203
|
+
read as broken. */
|
|
204
|
+
@page { size: A4; margin: 14mm 12mm; }
|
|
205
|
+
@media print {
|
|
206
|
+
:root { --measure: 100%; --reading: 100%; }
|
|
207
|
+
body { font-size: 10.5pt; }
|
|
208
|
+
.wrap { padding: 0; }
|
|
209
|
+
nav.toc { float: none; width: auto; margin: 0 0 18px; }
|
|
210
|
+
/* Every selector that sets a link colour on screen has to be named, or it
|
|
211
|
+
keeps it: a bare `a` is (0,0,1) and loses to all three. */
|
|
212
|
+
a, a:visited, nav.toc a, header.hero a {
|
|
213
|
+
color: inherit; text-decoration: none; }
|
|
214
|
+
h2, h3, h4, h5 { break-after: avoid; }
|
|
215
|
+
.callout, .stat, pre, tr { break-inside: avoid; }
|
|
216
|
+
/* The legend is a sibling of the figure's WRAPPER, not of the card inside
|
|
217
|
+
it, so the wrapper is what must hold together and what the legend must
|
|
218
|
+
follow. */
|
|
219
|
+
[id^="fig-"] { break-inside: avoid; break-after: avoid; }
|
|
220
|
+
.legend { break-before: avoid; }
|
|
221
|
+
/* Mermaid emits width and height as presentation attributes, which any
|
|
222
|
+
author rule already beats -- no !important needed. Only the cap is
|
|
223
|
+
ours: A4 less the @page margins leaves 269mm, and a figure is held
|
|
224
|
+
under that so its legend and heading can share the leaf. */
|
|
225
|
+
.figure-card svg { max-height: 215mm; width: auto; }
|
|
226
|
+
/* On paper an overflow box clips instead of scrolling, so a long code
|
|
227
|
+
line would be cropped at the page edge with nothing to show it. */
|
|
228
|
+
pre { overflow: visible; white-space: pre-wrap; overflow-wrap: break-word; }
|
|
229
|
+
footer { break-before: avoid; }
|
|
230
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* Casomo brand tokens.
|
|
2
|
+
*
|
|
3
|
+
* Link this next to any HTML that should carry the company's look. It
|
|
4
|
+
* declares custom properties and paints nothing, so it is safe to drop into
|
|
5
|
+
* a page that has its own stylesheet: nothing here can bleed into the
|
|
6
|
+
* page's own rules.
|
|
7
|
+
*
|
|
8
|
+
* The colours are transcribed by hand from the company site's design
|
|
9
|
+
* tokens. They have to be literal values: a rendered page is opened from
|
|
10
|
+
* disk over file:// with no server, so it cannot fetch at view time, and
|
|
11
|
+
* this package is a devDependency of the site rather than a dependency, so
|
|
12
|
+
* it cannot import at build time either. Nine colours are copied; the drift
|
|
13
|
+
* risk that creates, and what would retire the copy, are recorded at
|
|
14
|
+
* https://github.com/casomoltd/tooling/issues/6
|
|
15
|
+
*
|
|
16
|
+
* These are DEFAULTS. Every value is a custom property, so a consumer
|
|
17
|
+
* rebrands by adding a later :root block overriding the ones they care
|
|
18
|
+
* about — no fork of this file needed.
|
|
19
|
+
*
|
|
20
|
+
* The status colours are NOT brand. They are the diagram key's vocabulary
|
|
21
|
+
* and mean the same thing in every figure that uses this sheet.
|
|
22
|
+
*/
|
|
23
|
+
:root {
|
|
24
|
+
--bg: #DADADA; /* surface-mid — the ground the cards sit on */
|
|
25
|
+
--card: #FFFFFF; /* white — every panel, figure and code frame */
|
|
26
|
+
--panel: #EFEFEF; /* surface-light — table heads, inline code */
|
|
27
|
+
--ink: #2C2C2C; /* text-dark */
|
|
28
|
+
--muted: #707070; /* text-muted */
|
|
29
|
+
--dim: #8A8A8A; /* text-mid */
|
|
30
|
+
--line: #C0C0C0; /* border */
|
|
31
|
+
--accent: #F6643A; /* accent — the only brand colour */
|
|
32
|
+
--black: #000000; /* the head and foot bands */
|
|
33
|
+
--on-black: #EFEFEF; /* text-white, for use on those bands only */
|
|
34
|
+
--radius: 12px; /* rounded-xl */
|
|
35
|
+
--shadow: 0 1px 3px rgba(0,0,0,.1), 0 1px 2px -1px rgba(0,0,0,.1); /* shadow-sm */
|
|
36
|
+
--ok: #007f3b; --ok-bg: #e6f4ec; --amber: #a8720a; --amber-bg: #fbf3e2;
|
|
37
|
+
--red: #b3140c; --red-bg: #fbe9e8; --gen: #6b3fa0; --gen-bg: #f0e9f8;
|
|
38
|
+
--info: #1451a0; --info-bg: #eaf2fb;
|
|
39
|
+
--code-bg: #0d1117;
|
|
40
|
+
--sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
41
|
+
--mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
42
|
+
--prompt: "Share Tech Mono", "IBM Plex Mono", ui-monospace, monospace;
|
|
43
|
+
}
|