gp-dimm-city 1.2.0 → 1.3.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/CHANGELOG.md +65 -0
- package/components.yaml +5 -3
- package/package.json +1 -1
- package/plugin.js +14 -12
- package/styles/components/callouts.css +7 -1
- package/styles/components/cards.css +36 -15
- package/styles/components/data.css +9 -8
- package/styles/components/images.css +11 -9
- package/styles/components/section.css +64 -5
- package/styles/dc-core.css +19 -7
- package/styles/page-rules.css +9 -0
- package/styles/page-templates.css +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,71 @@ cut a version that has no `## [X.Y.Z]` heading below.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.3.0] - 2026-10-11
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The die chip now covers every wording of the instruction and no longer
|
|
15
|
+
disturbs the line it sits in.** Besides `ROLL THE DIE!`, the chip wraps
|
|
16
|
+
`ROLL A DIE!`, `ROLLS A DIE!` and `ROLLING THE DIE!` (uppercase, as written;
|
|
17
|
+
the text is never altered). It is redrawn as a stamped label (cream fill,
|
|
18
|
+
blood-red lettering, magenta rule and offset shadow) set as a plain inline
|
|
19
|
+
box, so a line holding a chip keeps the line height of the lines around it
|
|
20
|
+
and a chip never splits across two lines. Headings are left as plain heading
|
|
21
|
+
text. `--dc-roll-the-die-color` and `--dc-roll-the-die-border` work as before. (#18)
|
|
22
|
+
- **A run of cards or gear entries can pack two-up** (#15). Core's `.gp-grid-2` and
|
|
23
|
+
`.gp-grid-3` did nothing on a `@section` or `@column-panel`: the plugin's
|
|
24
|
+
float-containment default (`display: flow-root`) sat in a later cascade layer
|
|
25
|
+
and beat core's `display: grid` at any specificity, so every card took a full
|
|
26
|
+
row. The default now steps aside for the two grid classes, and the header bar
|
|
27
|
+
and preamble paragraph of a grid panel span the first row.
|
|
28
|
+
`@column-panel .gp-grid-2 .dc-allow-split` is the gear and item layout; the
|
|
29
|
+
design guide's Gear section shows it.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- A heading word wider than its box now breaks instead of running under the
|
|
34
|
+
next column or being clipped at the edge. This applies to `h1`-`h6` and the
|
|
35
|
+
`.dc-h3`-`.dc-h6` looks in every context (a two-column run is about 3.4in
|
|
36
|
+
per column), not only inside cards. Headings whose words fit are unchanged,
|
|
37
|
+
and an author's soft hyphen (`­`) still picks the break. (#13)
|
|
38
|
+
- In a loose bulleted list (blank lines between items) the `»` bullet no longer
|
|
39
|
+
prints alone on a line above the item's text. A loose item's text is wrapped
|
|
40
|
+
in a paragraph, so the bullet is now drawn on that first paragraph and sits
|
|
41
|
+
inline with the first line, in alerts and callouts too. Tight lists and
|
|
42
|
+
every other list render exactly as before. (#17)
|
|
43
|
+
- **`@npc-stat` blocks with `####` entries no longer split a creature across
|
|
44
|
+
pages or columns** (#16). The block may now break between entries (so
|
|
45
|
+
`@npc-stat .gp-columns-2` no longer jumps whole to the next page and leaves
|
|
46
|
+
a large empty area), the title bar stays with the first entry, and each
|
|
47
|
+
entry's name, stat lines and traits are kept together. Add `.dc-allow-split`
|
|
48
|
+
for an unusually long entry. A block with no `####` is unchanged.
|
|
49
|
+
- The chapter opener's `# Title` is never split across two sheets: it keeps
|
|
50
|
+
together and stays with the block after it, so a long opener moves the
|
|
51
|
+
whole title to the next page instead of leaving one line of it at the
|
|
52
|
+
foot of the page before. (#12)
|
|
53
|
+
- A callout label (`@callout gear label="SPECS"`, `[!NOTE]`, any variant) no
|
|
54
|
+
longer sits alone at the foot of a column or page with its body in the next
|
|
55
|
+
one: `.dc-alert-label` now carries `break-after: avoid`, which matters in the
|
|
56
|
+
multicol runs where a callout is allowed to split. A `@column-panel` header
|
|
57
|
+
bar can still be left at a page foot when its first block is not a
|
|
58
|
+
paragraph; that is a Chromium limit (it ignores `break-after: avoid` on a
|
|
59
|
+
`column-span: all` box), documented in `section.css`. (#14)
|
|
60
|
+
- **A split column panel that ends in an `@card` or `@outcome` shrinks to its
|
|
61
|
+
content** (#15). Both drew a decoration with an absolutely positioned box,
|
|
62
|
+
which stopped Chromium balancing the panel's last fragment: it ran the full
|
|
63
|
+
page height, so the next panel could not start beneath it. The `@card` body's
|
|
64
|
+
decorative tick is now the skill card's alone (it was never part of the
|
|
65
|
+
`@card` primitive's tokens), and the outcome ladder's inner hairline is an
|
|
66
|
+
inset shadow.
|
|
67
|
+
- **A column panel that ends flush with the page foot no longer spills its
|
|
68
|
+
bottom edge onto an extra page** (#15). The panel's bottom padding and rule
|
|
69
|
+
are painted as shadows below a box that ends with its content, so there is
|
|
70
|
+
no trailing box edge to push to the next sheet. Same picture, same spacing;
|
|
71
|
+
a neighbour with a larger top margin than the panel's bottom margin can now
|
|
72
|
+
sit up to 10px closer, because the padding that used to sit inside the panel
|
|
73
|
+
no longer adds to that gap.
|
|
74
|
+
|
|
10
75
|
## [1.2.0] - 2026-10-10
|
|
11
76
|
|
|
12
77
|
Requires Gutterpress 0.11.16 or newer (the desktop app or CLI): older
|
package/components.yaml
CHANGED
|
@@ -202,7 +202,7 @@ components:
|
|
|
202
202
|
css: styles/components/section.css
|
|
203
203
|
unstyled: [dc-gear]
|
|
204
204
|
breaks: "break-inside: avoid (via .dc-card)"
|
|
205
|
-
notes: "Renders as a plain .dc-card: nothing has ever styled .dc-gear (the only rules are inside the GEAR ENTRY tombstone comment in data.css). Reviving the treatment is a design decision — see that comment. Design guide only."
|
|
205
|
+
notes: "Renders as a plain .dc-card: nothing has ever styled .dc-gear (the only rules are inside the GEAR ENTRY tombstone comment in data.css). Reviving the treatment is a design decision — see that comment. Design guide only. A long run packs two-up in a core grid: `@column-panel .gp-grid-2 .dc-allow-split`."
|
|
206
206
|
|
|
207
207
|
- name: lede
|
|
208
208
|
status: live
|
|
@@ -403,12 +403,13 @@ components:
|
|
|
403
403
|
- name: roll-the-die
|
|
404
404
|
status: live
|
|
405
405
|
origin: plugin
|
|
406
|
-
recipe: "ROLL THE DIE! (automatic
|
|
406
|
+
recipe: "ROLL THE DIE! (automatic in markdown prose, uppercase only: ROLL|ROLLS|ROLLING + A|THE + DIE! — e.g. ROLL A DIE!, ROLLS A DIE!, ROLLING THE DIE!; code spans, raw HTML and headings are left alone)"
|
|
407
407
|
emits: span.dc-roll-the-die
|
|
408
408
|
css: styles/components/cards.css
|
|
409
409
|
tokens: [--dc-roll-the-die-color, --dc-roll-the-die-border]
|
|
410
|
-
usage: ["ROLL THE DIE!"]
|
|
410
|
+
usage: ["ROLL THE DIE!", "ROLL A DIE!", "ROLLS A DIE!", "ROLLING THE DIE!"]
|
|
411
411
|
breaks: none
|
|
412
|
+
notes: "A plain inline box (stamped label look): its padding and border paint around the text without making the line taller, so a line with a chip is the same height as one without. It never wraps: a chip that would straddle a line end moves to the next line whole."
|
|
412
413
|
|
|
413
414
|
- name: img-wrapper
|
|
414
415
|
status: live
|
|
@@ -496,6 +497,7 @@ components:
|
|
|
496
497
|
- --dc-npc-stat-label-color
|
|
497
498
|
- --dc-npc-stat-rule
|
|
498
499
|
- --dc-npc-stat-rule-width
|
|
500
|
+
breaks: "a block with #### entries breaks between entries, never inside one (keep-with-next chain; .dc-allow-split lets a long entry split); a block with no #### stays whole"
|
|
499
501
|
|
|
500
502
|
- name: chapter-start
|
|
501
503
|
status: live
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gp-dimm-city",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Dimm City design system for Gutterpress: the markdown-it macros (@skill, @specialty, @learning-path, @callout, …), print stylesheets, design tokens, fonts and page templates the Dimm City TTRPG books are set in.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "plugin.js",
|
package/plugin.js
CHANGED
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
* before core's layout transform; anywhere else it is
|
|
59
59
|
* core's section continuation)
|
|
60
60
|
* (chapter-opener composite is now markup-driven — see CSS notes below)
|
|
61
|
-
* `ROLL THE DIE!`
|
|
61
|
+
* `ROLL THE DIE!` (also ROLL A DIE!, ROLLS A DIE!, ROLLING THE DIE!)
|
|
62
|
+
* → span.dc-roll-the-die
|
|
62
63
|
* an image-only paragraph gets p.dc-img-wrapper
|
|
63
64
|
*
|
|
64
65
|
* GFM ALERT SYNTAX:
|
|
@@ -214,7 +215,8 @@ function buildStickerChain(items) {
|
|
|
214
215
|
return html;
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
+
// ROLL THE DIE! / ROLL A DIE! / ROLLS A DIE! / ROLLING THE DIE! — uppercase only.
|
|
219
|
+
const ROLL_THE_DIE_PATTERN = /ROLL(?:S|ING)? (?:A|THE) DIE!/g;
|
|
218
220
|
|
|
219
221
|
function isWordChar(char) {
|
|
220
222
|
return char !== '' && /[A-Za-z0-9_]/.test(char);
|
|
@@ -231,17 +233,16 @@ function splitRollDieText(token, state) {
|
|
|
231
233
|
const content = token.content || '';
|
|
232
234
|
const replacement = [];
|
|
233
235
|
let cursor = 0;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
while (searchFrom < content.length) {
|
|
237
|
-
const index = content.indexOf(ROLL_THE_DIE_TEXT, searchFrom);
|
|
238
|
-
if (index === -1) break;
|
|
236
|
+
const pattern = new RegExp(ROLL_THE_DIE_PATTERN.source, 'g');
|
|
237
|
+
let match;
|
|
239
238
|
|
|
239
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
240
|
+
const index = match.index;
|
|
240
241
|
const before = index > 0 ? content[index - 1] : '';
|
|
241
|
-
const end = index +
|
|
242
|
+
const end = index + match[0].length;
|
|
242
243
|
const after = end < content.length ? content[end] : '';
|
|
243
244
|
if (isWordChar(before) || isWordChar(after)) {
|
|
244
|
-
|
|
245
|
+
pattern.lastIndex = index + 1;
|
|
245
246
|
continue;
|
|
246
247
|
}
|
|
247
248
|
|
|
@@ -249,14 +250,13 @@ function splitRollDieText(token, state) {
|
|
|
249
250
|
replacement.push(copyTextToken(token, content.slice(cursor, index), state));
|
|
250
251
|
}
|
|
251
252
|
|
|
252
|
-
const rollToken = copyTextToken(token,
|
|
253
|
+
const rollToken = copyTextToken(token, match[0], state);
|
|
253
254
|
rollToken.type = 'dc_roll_the_die';
|
|
254
255
|
rollToken.tag = 'span';
|
|
255
256
|
rollToken.nesting = 0;
|
|
256
257
|
replacement.push(rollToken);
|
|
257
258
|
|
|
258
259
|
cursor = end;
|
|
259
|
-
searchFrom = end;
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
if (replacement.length === 0) return null;
|
|
@@ -288,8 +288,10 @@ function updateRawHtmlStack(html, stack) {
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
function dcRollDieTransform(state) {
|
|
291
|
-
state.tokens.forEach(token => {
|
|
291
|
+
state.tokens.forEach((token, i) => {
|
|
292
292
|
if (token.type !== 'inline' || !Array.isArray(token.children)) return;
|
|
293
|
+
// A heading is already display type: its wording stays plain heading text.
|
|
294
|
+
if (i > 0 && state.tokens[i - 1].type === 'heading_open') return;
|
|
293
295
|
|
|
294
296
|
const children = [];
|
|
295
297
|
const rawHtmlStack = [];
|
|
@@ -141,7 +141,8 @@
|
|
|
141
141
|
margin-bottom: 0.04in;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
.dc-alert li::before
|
|
144
|
+
.dc-alert li::before,
|
|
145
|
+
.dc-alert li > p:first-child::before {
|
|
145
146
|
color: var(--dc-alert-border);
|
|
146
147
|
}
|
|
147
148
|
|
|
@@ -156,6 +157,11 @@
|
|
|
156
157
|
letter-spacing: var(--dc-alert-label-spacing);
|
|
157
158
|
margin-bottom: var(--dc-alert-label-margin);
|
|
158
159
|
color: var(--dc-alert-label-color);
|
|
160
|
+
/* The label is a `span` (display: block), so core's heading rule does not
|
|
161
|
+
reach it. A callout may fragment in a multicol run (dc-native.css §8), and
|
|
162
|
+
without this the label can sit alone at a column or page foot with its
|
|
163
|
+
body in the next one. */
|
|
164
|
+
break-after: avoid;
|
|
159
165
|
}
|
|
160
166
|
|
|
161
167
|
/* ── Variant: Note (player-facing, default) ─────────────────────────────── */
|
|
@@ -387,7 +387,14 @@
|
|
|
387
387
|
variant (above), which shrinks each card vertically so 2 fit per page, or
|
|
388
388
|
cutting skill body text length at the markdown level. */
|
|
389
389
|
|
|
390
|
-
|
|
390
|
+
/* Scoped to the skill card on purpose. This mark is an absolutely positioned
|
|
391
|
+
box, and an absolutely positioned box inside a multicol run stops Chromium
|
|
392
|
+
balancing the run's last fragment: it stretches to the full page height
|
|
393
|
+
instead of shrinking to its content, so a `@column-panel .gp-columns-2`
|
|
394
|
+
that ends in a `@card` took a page of its own and spilled its bottom edge
|
|
395
|
+
onto one more. The `@card` primitive shares the `.dc-card-body` class and
|
|
396
|
+
never meant to carry this tick (its tokens are the skill card's). */
|
|
397
|
+
.dc-skill-card > .dc-card-body::before {
|
|
391
398
|
content: '';
|
|
392
399
|
position: absolute;
|
|
393
400
|
top: 10px;
|
|
@@ -475,21 +482,35 @@
|
|
|
475
482
|
font-weight: 600;
|
|
476
483
|
}
|
|
477
484
|
|
|
478
|
-
/* ROLL THE DIE! emphasis
|
|
479
|
-
--
|
|
480
|
-
|
|
485
|
+
/* ROLL THE DIE! emphasis chip — tokens: --dc-roll-the-die-color (text, default
|
|
486
|
+
--blood), --dc-roll-the-die-border (default --hud-magenta).
|
|
487
|
+
A stamped label for an instruction to act. It is a plain non-replaced
|
|
488
|
+
`display: inline` box on purpose: an inline box's vertical padding, border
|
|
489
|
+
and shadow paint around its text without adding to the line box, so a line
|
|
490
|
+
holding a chip is exactly as tall as one without, and nothing around it
|
|
491
|
+
moves. `line-height: 0` keeps the chip's own font from growing the line,
|
|
492
|
+
the font is smaller than the body, and `white-space: nowrap` moves a chip that
|
|
493
|
+
would straddle a line end to the next line whole (it still adds no height).
|
|
494
|
+
`box-decoration-break: clone` keeps both edges if one ever does fragment.
|
|
495
|
+
The fill is opaque paper, so contrast is fixed whatever it sits on: --blood on --paper-cream is 7.7:1
|
|
496
|
+
(it failed at 2.64:1 on skill-card paper-aged as --crimson text). */
|
|
481
497
|
.dc-roll-the-die {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
498
|
+
display: inline;
|
|
499
|
+
font-family: var(--font-display);
|
|
500
|
+
font-size: 0.8em;
|
|
501
|
+
font-weight: 700;
|
|
502
|
+
line-height: 0;
|
|
503
|
+
white-space: nowrap;
|
|
504
|
+
letter-spacing: 0.06em;
|
|
505
|
+
text-transform: uppercase;
|
|
506
|
+
color: var(--dc-roll-the-die-color);
|
|
507
|
+
background-color: var(--paper-cream);
|
|
508
|
+
padding: 0.14em 0.35em;
|
|
509
|
+
margin-inline: 0.05em;
|
|
510
|
+
border: 1.5pt solid var(--dc-roll-the-die-border);
|
|
511
|
+
border-bottom-width: 3pt;
|
|
512
|
+
box-shadow: 1pt 1pt 0 var(--dc-roll-the-die-border);
|
|
513
|
+
box-decoration-break: clone;
|
|
493
514
|
}
|
|
494
515
|
|
|
495
516
|
/* ── AP base + author-facing variants ─────────────────────────────────── */
|
|
@@ -215,14 +215,15 @@
|
|
|
215
215
|
background: var(--dc-outcomes-surface);
|
|
216
216
|
position: relative;
|
|
217
217
|
break-inside: avoid;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
218
|
+
/* Inset hairline, 4px in from the frame: a 4px ring of the surface over a
|
|
219
|
+
5px ring of the hairline colour leaves a 1px line between them. It is a
|
|
220
|
+
shadow, not an absolutely positioned `::after` box: an absolutely
|
|
221
|
+
positioned box inside a multicol run stops Chromium balancing the run's
|
|
222
|
+
last fragment (it stretches to the full page height), which a
|
|
223
|
+
`@column-panel .gp-columns-2` ending in an `@outcome` showed. */
|
|
224
|
+
box-shadow:
|
|
225
|
+
inset 0 0 0 4px var(--dc-outcomes-surface),
|
|
226
|
+
inset 0 0 0 5px var(--dc-outcomes-inner-border);
|
|
226
227
|
}
|
|
227
228
|
|
|
228
229
|
.dc-outcomes-label {
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
--dc-section-bg → panel background surface
|
|
171
171
|
--dc-section-accent → left strip / top border accent color */
|
|
172
172
|
|
|
173
|
-
/* Float-containment DEFAULT
|
|
173
|
+
/* Float-containment DEFAULT.
|
|
174
174
|
flow-root makes the card a block formatting context so it CONTAINS floated
|
|
175
175
|
children (.dc-sidebar, a book's floated art, fiction-excerpt art — all
|
|
176
176
|
float:right). Without it a section whose trailing content is a float
|
|
@@ -182,14 +182,16 @@
|
|
|
182
182
|
Safe w.r.t. margin collapsing: .section's non-zero vertical padding
|
|
183
183
|
already blocks parent/child margin collapse, so flow-root changes no
|
|
184
184
|
spacing for any variant that keeps that padding.
|
|
185
|
-
|
|
186
|
-
core
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
`@
|
|
190
|
-
`
|
|
191
|
-
|
|
192
|
-
|
|
185
|
+
It is a default, not a mandate, and `:where()` alone does not make it one:
|
|
186
|
+
core's `.gp-grid-*` lives in the earlier `gp.vocab` layer, and a rule in
|
|
187
|
+
any later layer beats it at ANY specificity, zero included. Measured:
|
|
188
|
+
`:where(.section) { display: flow-root }` still computed flow-root on
|
|
189
|
+
`@section .gp-grid-2` (a silent no-op, one card per row) while the same
|
|
190
|
+
class on a `@page` laid out as a grid. So the default steps aside for the
|
|
191
|
+
one core class that owns `display` (`.gp-columns-*` set `columns`, not
|
|
192
|
+
`display`, and coexist with flow-root). `:where()` keeps the specificity
|
|
193
|
+
at zero for the rest. */
|
|
194
|
+
:where(.section:not(.gp-grid-2, .gp-grid-3)) {
|
|
193
195
|
display: flow-root;
|
|
194
196
|
}
|
|
195
197
|
|
|
@@ -142,7 +142,17 @@
|
|
|
142
142
|
point of splitting them.
|
|
143
143
|
|
|
144
144
|
Top/bottom borders only: the base `.section` card's clip-path corner
|
|
145
|
-
breaks multicol, so this variant trades it for flat rules.
|
|
145
|
+
breaks multicol, so this variant trades it for flat rules.
|
|
146
|
+
|
|
147
|
+
The bottom edge (padding, then the 2px rule) is painted as two shadows
|
|
148
|
+
below a box that ends where its content ends, not laid out as padding and
|
|
149
|
+
border. A fragmented box that ends flush with the page foot has nowhere to
|
|
150
|
+
put trailing padding or border, and Chromium sends them to an extra page:
|
|
151
|
+
a panel whose last fragment filled the page spilled a 10px strip of its
|
|
152
|
+
bottom edge onto the next sheet. Shadows take no layout space, so there is
|
|
153
|
+
nothing to push. The picture is unchanged: the cream continues
|
|
154
|
+
--dc-section-pad-bottom below the content, then the rule, and the margin
|
|
155
|
+
below the panel grows by the same amount so nothing after it moves. */
|
|
146
156
|
.section.dc-column-panel {
|
|
147
157
|
--dc-section-surface: none;
|
|
148
158
|
--dc-section-shadow: none;
|
|
@@ -151,8 +161,11 @@
|
|
|
151
161
|
--dc-section-pad-bottom: var(--space-sm);
|
|
152
162
|
--dc-section-pad-left: var(--space-md);
|
|
153
163
|
background: var(--dc-section-bg);
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
padding-bottom: 0;
|
|
165
|
+
box-shadow:
|
|
166
|
+
0 var(--dc-section-pad-bottom) 0 0 var(--dc-section-bg),
|
|
167
|
+
0 calc(var(--dc-section-pad-bottom) + 2px) 0 0 var(--border-hairline);
|
|
168
|
+
margin: 12pt 0 calc(12pt + var(--dc-section-pad-bottom) + 2px);
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
/* ── Tabbed variant — first-child heading sits ABOVE the section ─────────
|
|
@@ -311,8 +324,15 @@
|
|
|
311
324
|
}
|
|
312
325
|
|
|
313
326
|
/* Column-break orphan protection: keep first paragraph with the spanning bar.
|
|
314
|
-
|
|
315
|
-
column-
|
|
327
|
+
MEASURED LIMIT (Chromium, no plugin CSS needed to show it): `break-after:
|
|
328
|
+
avoid` on a `column-span: all` box is honoured when the next box is also a
|
|
329
|
+
spanner (the intro paragraph below), but NOT between the spanner and the
|
|
330
|
+
column row after it. When the first block of that row does not fit, the
|
|
331
|
+
row moves to the next page and the bar stays behind. `break-before:
|
|
332
|
+
avoid` on the first block, `avoid-page` and `break-inside: avoid` do not
|
|
333
|
+
change it (the last one leaves the bar stranded more often). So the guards
|
|
334
|
+
here only help the spanning-paragraph case; they are kept because they cost
|
|
335
|
+
nothing and take effect when Chromium honours the avoid. */
|
|
316
336
|
.section.dc-column-panel > h2:first-child + *,
|
|
317
337
|
.section.dc-column-panel > h3:first-child + * {
|
|
318
338
|
break-before: avoid;
|
|
@@ -326,6 +346,15 @@
|
|
|
326
346
|
break-inside: avoid;
|
|
327
347
|
}
|
|
328
348
|
|
|
349
|
+
/* In a core GRID run (`@section .gp-grid-2`, `@column-panel .gp-grid-2`) the
|
|
350
|
+
header bar and a panel's preamble paragraph are grid items like any other
|
|
351
|
+
child, so each would take a cell. `grid-column: 1 / -1` gives them the whole
|
|
352
|
+
first row, as `column-span: all` does in a column run. */
|
|
353
|
+
.section:is(.gp-grid-2, .gp-grid-3) > :is(h2, h3):first-child,
|
|
354
|
+
.section.dc-column-panel:is(.gp-grid-2, .gp-grid-3) > :is(h2, h3):first-child + p {
|
|
355
|
+
grid-column: 1 / -1;
|
|
356
|
+
}
|
|
357
|
+
|
|
329
358
|
/* Multi-column body copy: kill rivers from narrow-column justification. */
|
|
330
359
|
.section.dc-column-panel p {
|
|
331
360
|
text-align: left;
|
|
@@ -674,6 +703,36 @@ h3 + .section {
|
|
|
674
703
|
--dc-npc-stat-primary → primary stat-line color, e.g. "2 HP — 1 Damage" (default --hud-blue)
|
|
675
704
|
--dc-npc-stat-secondary → secondary stat-line color (default --ink-smoke)
|
|
676
705
|
Override per chapter / per faction in the book's own sheet selector chains. */
|
|
706
|
+
/* Page breaks. A block with `####` entries is a RUN of creatures: it may break
|
|
707
|
+
between entries (token `auto`; this also stops `.gp-columns-2` on the section
|
|
708
|
+
from jumping the whole block to the next page) and never inside one. An entry
|
|
709
|
+
has no wrapper — it is an `h4` and the siblings that follow it up to the next
|
|
710
|
+
`h4` — so "never inside" is a keep-with-next chain: every child but the last
|
|
711
|
+
of its entry takes `break-after: avoid`, and each block of an entry is itself
|
|
712
|
+
unbreakable so a paragraph cannot split between columns. Chromium keeps a
|
|
713
|
+
chain together when it fits a page and lets it break only when it cannot. The
|
|
714
|
+
title bar (h3) and its intro chain on to the first entry, so the bar is never
|
|
715
|
+
alone at a page foot. A block with no `h4` is ONE creature (the bar names it)
|
|
716
|
+
and stays whole like any section. `.dc-allow-split` is the opt-in for an
|
|
717
|
+
unusually long entry: it keeps only each entry's name with its first two
|
|
718
|
+
blocks (the stat lines) and headings with what follows, and lets the rest
|
|
719
|
+
break (blocks too). */
|
|
720
|
+
.section.dc-npc-stat:has(> h4) {
|
|
721
|
+
--dc-section-break-inside: auto;
|
|
722
|
+
}
|
|
723
|
+
.section.dc-npc-stat:has(> h4) > :not(:last-child, :has(+ h4:not(:first-of-type))) {
|
|
724
|
+
break-after: avoid;
|
|
725
|
+
}
|
|
726
|
+
.section.dc-npc-stat:has(> h4) > :is(h4 ~ p, h4 ~ blockquote) {
|
|
727
|
+
break-inside: avoid;
|
|
728
|
+
}
|
|
729
|
+
.section.dc-npc-stat.dc-allow-split:has(> h4) > :is(h4 ~ p, h4 ~ blockquote) {
|
|
730
|
+
break-inside: auto;
|
|
731
|
+
}
|
|
732
|
+
.section.dc-npc-stat.dc-allow-split:has(> h4) > :not(:last-child, h1, h2, h3, h4, h5, h6, h4 + *, h4 + * + *, blockquote + p) {
|
|
733
|
+
break-after: auto;
|
|
734
|
+
}
|
|
735
|
+
|
|
677
736
|
.dc-npc-stat h4 {
|
|
678
737
|
border-top: var(--dc-npc-stat-rule-width) solid var(--dc-npc-stat-rule);
|
|
679
738
|
padding-top: 0.05in;
|
package/styles/dc-core.css
CHANGED
|
@@ -65,7 +65,17 @@ body {
|
|
|
65
65
|
sub-section label where the outline needs a section:
|
|
66
66
|
## Useful Items {.dc-h3}
|
|
67
67
|
Each look rule below sets every property any level's rule sets, so the
|
|
68
|
-
class fully replaces the tag's own look (a class outranks an element).
|
|
68
|
+
class fully replaces the tag's own look (a class outranks an element).
|
|
69
|
+
overflow-wrap: the display face is wide and set in caps, so one long word
|
|
70
|
+
(INTERDIMENSIONALISM at 16pt in a ~3.4in column) outruns its box: it ran
|
|
71
|
+
under the next column or was clipped at the edge, losing letters in print.
|
|
72
|
+
A word that cannot fit now breaks as a last resort, at every level, in every
|
|
73
|
+
context; words that fit are untouched. `break-word` (not `anywhere`) keeps
|
|
74
|
+
min-content widths intact for shrink-to-fit headings such as banners and
|
|
75
|
+
tabs. A soft hyphen (­) in the source still picks a better break.
|
|
76
|
+
`hyphens: auto` (inherited from body) hyphenates where the engine has a
|
|
77
|
+
dictionary, but uppercase display words are not reliably hyphenated, so
|
|
78
|
+
the guaranteed break does not depend on it. */
|
|
69
79
|
h1, h2, h3, h4, h5, h6,
|
|
70
80
|
.dc-h3, .dc-h4, .dc-h5, .dc-h6 {
|
|
71
81
|
font-family: var(--font-display);
|
|
@@ -76,12 +86,10 @@ h1, h2, h3, h4, h5, h6,
|
|
|
76
86
|
margin-top: 0;
|
|
77
87
|
margin-bottom: 0.5em;
|
|
78
88
|
color: var(--ink-dark);
|
|
89
|
+
overflow-wrap: break-word;
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
/* H1 — magenta neon authority: chapter openers, page-level titles.
|
|
82
|
-
overflow-wrap: the lixdu display face at 26pt overflows a narrow column
|
|
83
|
-
on one-word caps titles (CREATUREPUNK) and on long unbreakable strings
|
|
84
|
-
such as URLs or class names. */
|
|
92
|
+
/* H1 — magenta neon authority: chapter openers, page-level titles. */
|
|
85
93
|
h1 {
|
|
86
94
|
font-size: var(--fs-h1);
|
|
87
95
|
font-weight: var(--fw-bold);
|
|
@@ -90,7 +98,6 @@ h1 {
|
|
|
90
98
|
color: var(--hud-magenta);
|
|
91
99
|
border-bottom: 2px solid var(--hud-magenta);
|
|
92
100
|
padding-bottom: 0.15em;
|
|
93
|
-
overflow-wrap: break-word;
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
/* H2 — cold teal: major section breaks */
|
|
@@ -174,7 +181,12 @@ blockquote {
|
|
|
174
181
|
/* ── GLOBAL LIST STYLES (kept as-is per plan) ─────────────────────────────── */
|
|
175
182
|
ul { padding-left: 0.25in; }
|
|
176
183
|
ul li { list-style: none; margin-bottom: 0.06in; }
|
|
177
|
-
|
|
184
|
+
/* markdown-it wraps a loose item's text in <p>, and an inline ::before on the
|
|
185
|
+
<li> would sit alone on its own line above that block. A loose item draws
|
|
186
|
+
the bullet on its first paragraph instead, so it stays on the first line. */
|
|
187
|
+
ul li:has(> p:first-child)::before { content: none; }
|
|
188
|
+
ul li::before,
|
|
189
|
+
ul li > p:first-child::before {
|
|
178
190
|
content: "» ";
|
|
179
191
|
/* --orange (#d4500a) on wall = 2.46:1 (fail). --rust (#b23a12) = 3.48:1
|
|
180
192
|
(measured — also fail). --blood (#901a12) = 5.21:1 on wall, passes
|
package/styles/page-rules.css
CHANGED
|
@@ -114,6 +114,15 @@
|
|
|
114
114
|
Chapter start — footers suppressed; the chevron banner IS the visual
|
|
115
115
|
identity. dc-native.css refills the two bottom boxes with the brick
|
|
116
116
|
band so the margin band stays continuous.
|
|
117
|
+
|
|
118
|
+
The name applies to EVERY page `.dc-chapter-start` spans, so an opener that
|
|
119
|
+
runs past its first page prints its continuation pages without folios too;
|
|
120
|
+
end the opener with `@page` to get them back. MEASURED in Chromium's own
|
|
121
|
+
print (printToPDF) why this cannot be narrowed to the first page:
|
|
122
|
+
- `@page chapter-start:first` matches only the document's first page,
|
|
123
|
+
not the first page of a page group;
|
|
124
|
+
- naming just the `h1`, or a zero-height first child, forces a page
|
|
125
|
+
break at the name change (a title alone on a page, or a blank page).
|
|
117
126
|
--------------------------------------------------------------------------- */
|
|
118
127
|
|
|
119
128
|
@page chapter-start {
|
|
@@ -503,10 +503,15 @@
|
|
|
503
503
|
}
|
|
504
504
|
|
|
505
505
|
/* Chapter-start page: the h1 drops core's rule and gets headroom below the
|
|
506
|
-
page top.
|
|
506
|
+
page top. The title is never fragmented across sheets and stays with the
|
|
507
|
+
first block after it (a title wraps to several lines in the display face,
|
|
508
|
+
so an opener that runs past its first page used to leave one line of it
|
|
509
|
+
at the foot of a sheet and the rest on the next). */
|
|
507
510
|
.page.page-chapter-start.dc-chapter-start > h1 {
|
|
508
511
|
border: 0;
|
|
509
512
|
margin-top: 24pt;
|
|
513
|
+
break-inside: avoid;
|
|
514
|
+
break-after: avoid;
|
|
510
515
|
}
|
|
511
516
|
|
|
512
517
|
.page.page-chapter-start.dc-chapter-start p {
|