gp-dimm-city 1.3.0 → 1.4.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 +70 -0
- package/components.yaml +14 -44
- package/package.json +2 -2
- package/plugin.js +20 -3
- package/snippets/card-grid.md +4 -0
- package/snippets/credits-page.md +2 -2
- package/styles/components/cards.css +14 -74
- package/styles/components/chrome.css +0 -11
- package/styles/components/data.css +1 -8
- package/styles/components/images.css +6 -19
- package/styles/components/section.css +56 -128
- package/styles/dc-component-defaults.css +7 -0
- package/styles/dc-native.css +24 -47
- package/styles/page-templates.css +32 -71
- package/snippets/column-panel.md +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,76 @@ cut a version that has no `## [X.Y.Z]` heading below.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.4.0] - 2026-10-11
|
|
11
|
+
|
|
12
|
+
One way to set content in columns: Gutterpress core's `@columns N` …
|
|
13
|
+
`@column-break` … `@end-columns`. Everything else this package did with
|
|
14
|
+
columns is removed or now takes that marker. **This release needs
|
|
15
|
+
Gutterpress 0.11.17-alpha.6 or later**: that core draws the column hairline
|
|
16
|
+
(`--gp-column-rule`), reads a bare `@columns` as two, lets `@section`s sit
|
|
17
|
+
inside an `@columns` run, and no longer splits a keep-together section that
|
|
18
|
+
follows other content. Two rules here that only undid that last core
|
|
19
|
+
behaviour (on a single-creature `@npc-stat` and on a section after a
|
|
20
|
+
`.dc-intro` lede) are removed with it.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **`@columns` works inside a `@skill` card.** A `@columns 2` … `@end-columns`
|
|
25
|
+
run (and any `@column-break` in it) used to end the card at the marker, so
|
|
26
|
+
the columns and everything after them landed outside the card. They now stay
|
|
27
|
+
in the card's body, in place.
|
|
28
|
+
- **`@card-grid`, `@npc-stat`, `@citizen-walkthrough` and the credits
|
|
29
|
+
colophon take `@columns 2` inside them.** `@card-grid` no longer sets its own
|
|
30
|
+
columns: write `@columns 2` around its cards (the grid keeps its chrome reset
|
|
31
|
+
and card density). An `@npc-stat` keeps creatures whole and breaks between
|
|
32
|
+
entries whether they sit directly in the block or in an `@columns` run in it
|
|
33
|
+
(a one-creature block follows a section at a page foot without splitting, and
|
|
34
|
+
the bar is never left alone above an empty first row);
|
|
35
|
+
a walkthrough keeps the two-line heading height in its column runs. The
|
|
36
|
+
credits snippet writes the colophon names in `@columns 2` instead of a
|
|
37
|
+
`dc-colophon-grid` div.
|
|
38
|
+
- **The column gap and hairline are two tokens, set once.** `--gp-column-gap`
|
|
39
|
+
(the gutter) and `--gp-column-rule` (a 1pt hairline) are set at `:root`
|
|
40
|
+
instead of by rules on `.gp-columns-2` / `.gp-columns-3`. Three columns now
|
|
41
|
+
use the same gap as two (0.15in, was 0.18in). The hairline needs the next
|
|
42
|
+
Gutterpress; until then no column rule is drawn. `.dc-card-grid` and the
|
|
43
|
+
credits colophon set `--gp-column-rule: none`, as they never drew one.
|
|
44
|
+
- The rules that let a section or a callout fragment inside a column run now
|
|
45
|
+
match any run (`@columns N`, or a count class on a panel), not only the
|
|
46
|
+
two- and three-column count classes; the guard on a page that was itself a
|
|
47
|
+
column run (`@page .gp-columns-2`) is gone with that form.
|
|
48
|
+
|
|
49
|
+
### Removed
|
|
50
|
+
|
|
51
|
+
- **`.dc-two-col` (`@skill {.dc-two-col}`) is gone.** A card that wants columns
|
|
52
|
+
wraps just that part of its body in `@columns 2`; the flavor line and sub-headers
|
|
53
|
+
stay full width. Books that wrote the class should replace it.
|
|
54
|
+
- **`.dc-cards-two-col` is gone.** Written on a wrapper (typically
|
|
55
|
+
`@specialty .augmerc .dc-cards-two-col`), it set every skill card inside
|
|
56
|
+
two-column, which broke the layout of cards that should not be columned:
|
|
57
|
+
short blocks split into one-line fragments, a lone line in the right
|
|
58
|
+
column, list blocks divided unevenly. A wrapper no longer decides its
|
|
59
|
+
cards' columns. A card that needs two columns says so itself with
|
|
60
|
+
`@columns 2` inside it, and a section of running text uses core's
|
|
61
|
+
`@columns 2`. Books that wrote the class should delete it from their
|
|
62
|
+
`@specialty` lines; until they do it does nothing.
|
|
63
|
+
- **`.column-break`** (`---{.column-break}`, an `<hr>`): write core's
|
|
64
|
+
`@column-break`.
|
|
65
|
+
- **`.dc-colophon-grid`**, **`.dc-at-a-glance-cards`** (a three-across grid;
|
|
66
|
+
keep the `.dc-at-a-glance-card` cards and set them in `@columns 3`), and the
|
|
67
|
+
`column-fill: balance` rule on `.gp-columns-2` / `.gp-columns-3` (it restated
|
|
68
|
+
the CSS default).
|
|
69
|
+
- The `.gp-grid-2` / `.gp-grid-3` support added in 1.3.0: the float-containment
|
|
70
|
+
default is a plain `:where(.section) { display: flow-root }` again, and the
|
|
71
|
+
grid-row spans for a panel's header bar are gone. A long run of gear entries
|
|
72
|
+
packs two-up in `@columns 2` inside a `@section .dc-allow-split`.
|
|
73
|
+
- **`@column-panel` / `.dc-column-panel` are gone.** Columns have one spelling,
|
|
74
|
+
core's `@columns N`. Write a plain `@section` with the body in `@columns 2`;
|
|
75
|
+
the heading and intro paragraph stay above the run, full width. Before:
|
|
76
|
+
`@column-panel .gp-columns-2` … `@end-column-panel`; after: `@section` / `##
|
|
77
|
+
Heading` / `@columns 2` … `@end-columns` / `@end-section`. The migrated
|
|
78
|
+
panel looks like a normal `@section` card.
|
|
79
|
+
|
|
10
80
|
## [1.3.0] - 2026-10-11
|
|
11
81
|
|
|
12
82
|
### Changed
|
package/components.yaml
CHANGED
|
@@ -65,7 +65,7 @@ components:
|
|
|
65
65
|
- --dc-alert-label-spacing
|
|
66
66
|
- --dc-alert-label-margin
|
|
67
67
|
- --dc-alert-margin-top
|
|
68
|
-
breaks: "break-inside: avoid, relaxed to auto inside
|
|
68
|
+
breaks: "break-inside: avoid, relaxed to auto inside a column run (@columns; dc-native.css)"
|
|
69
69
|
notes: "`warning` is an unprefixed modifier on .dc-note. The blockquote form is single-block; use @callout for multi-paragraph bodies."
|
|
70
70
|
|
|
71
71
|
- name: callout
|
|
@@ -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. A long run packs two-up in
|
|
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 `@columns 2` inside a `@section .dc-allow-split`."
|
|
206
206
|
|
|
207
207
|
- name: lede
|
|
208
208
|
status: live
|
|
@@ -353,7 +353,7 @@ components:
|
|
|
353
353
|
- name: skill
|
|
354
354
|
status: live
|
|
355
355
|
origin: plugin
|
|
356
|
-
recipe: "@skill [id=…] [{.dc-allow-split}]
|
|
356
|
+
recipe: "@skill [id=…] [{.dc-allow-split}] / #### Name | Tier / > flavor / 1. **N AP** *Ability:* text / ##### Sub-header … @end-skill"
|
|
357
357
|
emits:
|
|
358
358
|
- div.dc-skill-card[name][data-break-inside=avoid] > div.dc-card-tab > h4.dc-tab-title + span.dc-tab-tier
|
|
359
359
|
- div.dc-card-body > div.dc-card-inner > p.dc-flavor + div.dc-sub-header
|
|
@@ -368,7 +368,7 @@ components:
|
|
|
368
368
|
- --dc-card-gap
|
|
369
369
|
- --dc-card-tab-shadow
|
|
370
370
|
breaks: "data-break-inside=avoid by default; {.dc-allow-split} opts into splitting (see allow-split); tab break-after: avoid; last two abilities glued"
|
|
371
|
-
notes: "Declared marker. Every #### heading in one @skill is its own card, with the @skill line's classes and attributes. @end-skill closes only the skill, never the path around it. A ##### Outcomes sub-header is swallowed (the outcomes block carries its own label)."
|
|
371
|
+
notes: "Declared marker. Every #### heading in one @skill is its own card, with the @skill line's classes and attributes. @end-skill closes only the skill, never the path around it. A ##### Outcomes sub-header is swallowed (the outcomes block carries its own label). Part of a card's body can sit in columns: wrap it in `@columns 2` … `@end-columns`; the run (and any `@column-break` in it) stays inside the card's .dc-card-inner, in place."
|
|
372
372
|
|
|
373
373
|
- name: skill-highlight
|
|
374
374
|
status: live
|
|
@@ -429,14 +429,6 @@ components:
|
|
|
429
429
|
breaks: none
|
|
430
430
|
|
|
431
431
|
# ── Author classes on core markers, attrs and raw HTML ────────────────
|
|
432
|
-
- name: column-panel
|
|
433
|
-
status: live
|
|
434
|
-
origin: plugin
|
|
435
|
-
recipe: "@column-panel [.gp-columns-2] … @end-column-panel (or @section .gp-columns-2 .dc-column-panel)"
|
|
436
|
-
emits: div.section.dc-column-panel
|
|
437
|
-
css: styles/components/section.css
|
|
438
|
-
breaks: "first heading glued to its first paragraph"
|
|
439
|
-
|
|
440
432
|
- name: tabbed
|
|
441
433
|
status: live
|
|
442
434
|
origin: plugin
|
|
@@ -449,15 +441,16 @@ components:
|
|
|
449
441
|
- name: card-grid
|
|
450
442
|
status: live
|
|
451
443
|
origin: plugin
|
|
452
|
-
recipe: "@card-grid … @end-card-grid (or @section .dc-card-grid)"
|
|
453
|
-
emits: div.section.dc-card-grid
|
|
444
|
+
recipe: "@card-grid / @columns 2 / @specialty … / @end-columns / @end-card-grid (or @section .dc-card-grid)"
|
|
445
|
+
emits: div.section.dc-card-grid > div.gp-columns.gp-columns-2
|
|
454
446
|
css: [styles/components/section.css, styles/page-templates.css]
|
|
455
447
|
breaks: "break-inside: auto on the grid; each specialty card avoid"
|
|
448
|
+
notes: "Sets no columns of its own: the cards go in `@columns 2` inside it."
|
|
456
449
|
|
|
457
450
|
- name: citizen-walkthrough
|
|
458
451
|
status: live
|
|
459
452
|
origin: plugin
|
|
460
|
-
recipe: "@citizen-walkthrough [
|
|
453
|
+
recipe: "@citizen-walkthrough [@columns 2 … @end-columns] … @end-citizen-walkthrough (or @section .dc-citizen-walkthrough)"
|
|
461
454
|
emits: div.section.dc-citizen-walkthrough
|
|
462
455
|
css: [styles/components/section.css, styles/dc-native.css]
|
|
463
456
|
notes: "Themed through the section chassis tokens --dc-section-accent / --dc-section-bg (see section), which the design guide documents."
|
|
@@ -488,7 +481,7 @@ components:
|
|
|
488
481
|
- name: npc-stat
|
|
489
482
|
status: live
|
|
490
483
|
origin: plugin
|
|
491
|
-
recipe: "@npc-stat … @end-npc-stat (or @section .dc-npc-stat)"
|
|
484
|
+
recipe: "@npc-stat [### bar] [@columns 2 … @end-columns] … @end-npc-stat (or @section .dc-npc-stat)"
|
|
492
485
|
emits: div.section.dc-npc-stat
|
|
493
486
|
css: [styles/components/section.css]
|
|
494
487
|
tokens:
|
|
@@ -497,7 +490,7 @@ components:
|
|
|
497
490
|
- --dc-npc-stat-label-color
|
|
498
491
|
- --dc-npc-stat-rule
|
|
499
492
|
- --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"
|
|
493
|
+
breaks: "a block with #### entries breaks between entries, never inside one (keep-with-next chain; .dc-allow-split lets a long entry split), whether the entries are direct children or sit in an @columns run inside it; a block with no #### stays whole"
|
|
501
494
|
|
|
502
495
|
- name: chapter-start
|
|
503
496
|
status: live
|
|
@@ -513,14 +506,7 @@ components:
|
|
|
513
506
|
recipe: "@page .page-credits .dc-credits"
|
|
514
507
|
emits: div.page.dc-credits
|
|
515
508
|
css: [styles/page-templates.css, styles/dc-native.css]
|
|
516
|
-
notes: "Styled in page-templates.css and dc-native.css — carried in from the books in 1.0.0."
|
|
517
|
-
|
|
518
|
-
- name: colophon-grid
|
|
519
|
-
status: live
|
|
520
|
-
origin: author
|
|
521
|
-
recipe: "raw <div class=\"dc-colophon-grid\"> on the credits page"
|
|
522
|
-
emits: div.dc-colophon-grid
|
|
523
|
-
css: styles/page-templates.css
|
|
509
|
+
notes: "Styled in page-templates.css and dc-native.css — carried in from the books in 1.0.0. The colophon names go in `@columns 2` inside `@section .credits-colophon`."
|
|
524
510
|
|
|
525
511
|
- name: chevron
|
|
526
512
|
status: live
|
|
@@ -569,11 +555,11 @@ components:
|
|
|
569
555
|
css: styles/components/images.css
|
|
570
556
|
breaks: "break-inside: avoid"
|
|
571
557
|
|
|
572
|
-
- name: at-a-glance-
|
|
558
|
+
- name: at-a-glance-card
|
|
573
559
|
status: live
|
|
574
560
|
origin: author
|
|
575
|
-
recipe: "raw <div class=\"dc-at-a-glance-
|
|
576
|
-
emits: div.dc-at-a-glance-
|
|
561
|
+
recipe: "@columns 3 / raw <div class=\"dc-at-a-glance-card\"> × 3 / @end-columns"
|
|
562
|
+
emits: div.dc-at-a-glance-card
|
|
577
563
|
css: styles/components/data.css
|
|
578
564
|
|
|
579
565
|
- name: stat-block
|
|
@@ -596,22 +582,6 @@ components:
|
|
|
596
582
|
breaks: "break-inside: auto — opts a section or skill card out of the default avoid. A split specialty card also keeps its first two blocks, and any lead-in (ability row, bold-only line, paragraph before a list or table), with what follows (break-after: avoid)"
|
|
597
583
|
notes: "The plugin reads this class (to drop data-break-inside=avoid) but never adds it."
|
|
598
584
|
|
|
599
|
-
- name: two-col
|
|
600
|
-
status: live
|
|
601
|
-
origin: author
|
|
602
|
-
recipe: "@skill {.dc-two-col}"
|
|
603
|
-
emits: div.dc-skill-card.dc-two-col
|
|
604
|
-
css: styles/components/cards.css
|
|
605
|
-
breaks: "list items and paragraphs break-inside: avoid within the columns"
|
|
606
|
-
|
|
607
|
-
- name: cards-two-col
|
|
608
|
-
status: live
|
|
609
|
-
origin: author
|
|
610
|
-
recipe: "@specialty .augmerc .dc-cards-two-col (or on @chapter / @page / @section)"
|
|
611
|
-
emits: .dc-cards-two-col (on the ancestor it is written on; styles the .dc-skill-card inside it)
|
|
612
|
-
css: styles/components/cards.css
|
|
613
|
-
breaks: "list items break-inside: avoid; a paragraph may break between columns. Flavor line, ability rows, tables, sub-heads and rules span both columns. A card with .dc-allow-split stays single-column; a card with its own .dc-two-col keeps the per-card form"
|
|
614
|
-
|
|
615
585
|
- name: snug
|
|
616
586
|
status: live
|
|
617
587
|
origin: author
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gp-dimm-city",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"pack:check": "npm pack --dry-run"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"gutterpress": "0.11.
|
|
72
|
+
"gutterpress": "0.11.17-alpha.6",
|
|
73
73
|
"markdown-it": "^14.1.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
package/plugin.js
CHANGED
|
@@ -886,6 +886,8 @@ function dcSkillCards(state) {
|
|
|
886
886
|
const emit = (html) => out.push(makeToken('html_block', html));
|
|
887
887
|
let inCard = false;
|
|
888
888
|
const belongsToCard = new Set(); // open/close tokens of the components that stay in the card
|
|
889
|
+
let columnsDepth = 0; // how many `@columns` runs sit open inside the current card
|
|
890
|
+
const columnsCloses = new Set(); // their close tokens
|
|
889
891
|
let title = { name: '', tier: '' }; // the current card's title, for @continue
|
|
890
892
|
|
|
891
893
|
const cardOpen = (cont) => {
|
|
@@ -902,7 +904,9 @@ function dcSkillCards(state) {
|
|
|
902
904
|
for (let i = 0; i < content.length; i++) {
|
|
903
905
|
const tok = content[i];
|
|
904
906
|
|
|
905
|
-
|
|
907
|
+
// An h4 inside a `@columns` run in a card is a plain heading: a card
|
|
908
|
+
// cannot start in the middle of another card's columns.
|
|
909
|
+
if (tok.type === 'heading_open' && tok.tag === 'h4' && columnsDepth === 0) {
|
|
906
910
|
endCard();
|
|
907
911
|
const parsed = parseSkillTitle(content[i + 1].content || '');
|
|
908
912
|
title = { name: parsed.name, tier: parsed.tier };
|
|
@@ -952,6 +956,20 @@ function dcSkillCards(state) {
|
|
|
952
956
|
do depth += content[end++].nesting; while (depth > 0);
|
|
953
957
|
belongsToCard.add(tok).add(content[end - 1]);
|
|
954
958
|
}
|
|
959
|
+
// `@columns` … `@end-columns` and the `@column-break`s between are content of
|
|
960
|
+
// the card too when they sit inside one: the columns run stays in the
|
|
961
|
+
// card's body, in place. (A run that opens before the card, around
|
|
962
|
+
// several cards, is not the card's: it ends the card as any layout token does.)
|
|
963
|
+
if (inCard && tok.type === 'layout_component_open' && tok.meta.kind === 'columns') {
|
|
964
|
+
let end = i;
|
|
965
|
+
let depth = 0;
|
|
966
|
+
do depth += content[end++].nesting; while (depth > 0);
|
|
967
|
+
belongsToCard.add(tok).add(content[end - 1]);
|
|
968
|
+
columnsCloses.add(content[end - 1]);
|
|
969
|
+
columnsDepth++;
|
|
970
|
+
}
|
|
971
|
+
if (columnsCloses.has(tok)) columnsDepth--;
|
|
972
|
+
if (tok.type === 'layout_column_break' && columnsDepth > 0) belongsToCard.add(tok);
|
|
955
973
|
if (tok.type.startsWith('layout_') && !belongsToCard.has(tok)) endCard();
|
|
956
974
|
|
|
957
975
|
if (!inCard) {
|
|
@@ -1359,8 +1377,7 @@ export const markers = {
|
|
|
1359
1377
|
// styles/components/section.css. No variants: none of these is styled per
|
|
1360
1378
|
// specialty (that lives on `.dc-specialty.<name>`, from `@specialty`), and
|
|
1361
1379
|
// modifiers such as `.dc-plain`, `.dc-snug`, `.dc-allow-split` and
|
|
1362
|
-
// `.gp-columns-2` stay author classes
|
|
1363
|
-
'column-panel': { section: true, class: 'dc-column-panel' },
|
|
1380
|
+
// `.gp-columns-2` stay author classes.
|
|
1364
1381
|
tabbed: { section: true, class: 'dc-tabbed' },
|
|
1365
1382
|
'card-grid': { section: true, class: 'dc-card-grid' },
|
|
1366
1383
|
'citizen-walkthrough': { section: true, class: 'dc-citizen-walkthrough' },
|
package/snippets/card-grid.md
CHANGED
package/snippets/credits-page.md
CHANGED
|
@@ -288,80 +288,20 @@
|
|
|
288
288
|
DELETED (2026-09-02): zero uses of either in any book's rendered HTML,
|
|
289
289
|
zero emissions from the Dimm City plugin. */
|
|
290
290
|
|
|
291
|
-
/*
|
|
292
|
-
|
|
293
|
-
`@
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
/* Inside 2-col body, a table spans both columns so its chrome doesn't break
|
|
304
|
-
across the gap. */
|
|
305
|
-
.dc-skill-card.dc-two-col .dc-card-inner table {
|
|
306
|
-
column-span: all;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/* AP/numbered list items must stay whole — splitting "Bullet to Blood"
|
|
310
|
-
across columns reads as sloppy. break-inside: avoid keeps each <li>
|
|
311
|
-
together in a single column even when the column boundary lands in
|
|
312
|
-
the middle of one. Paragraphs immediately following list items also
|
|
313
|
-
stay glued so their text doesn't strand from its anchor item. */
|
|
314
|
-
.dc-skill-card.dc-two-col .dc-card-inner ul > li,
|
|
315
|
-
.dc-skill-card.dc-two-col .dc-card-inner p {
|
|
291
|
+
/* `.dc-two-col` (`@skill {.dc-two-col}`: a card's whole body in two columns)
|
|
292
|
+
and `.dc-cards-two-col` (every card under an ancestor) were here and are
|
|
293
|
+
DELETED. Columns are core's `@columns 2` … `@end-columns`, and a card that
|
|
294
|
+
wants them wraps just that part of its body: the skill rule keeps the run
|
|
295
|
+
inside `.dc-card-inner`, in place (plugin.js, `dcSkillCards`).
|
|
296
|
+
|
|
297
|
+
The one thing a card still says about its run: an ability row, a paragraph
|
|
298
|
+
or a list item stays whole. Split across the gap, an ability reads as
|
|
299
|
+
sloppy (a table, which `.dc-two-col` used to span across both columns,
|
|
300
|
+
goes outside the run instead). */
|
|
301
|
+
.dc-card-inner .gp-columns :is(.dc-ability, p, li) {
|
|
316
302
|
break-inside: avoid;
|
|
317
303
|
}
|
|
318
304
|
|
|
319
|
-
/* ── SKILL-CARD TWO-COLUMN FORM, BY ANCESTOR ───────────────────────────────── */
|
|
320
|
-
/* `.dc-cards-two-col` on ANY ancestor of skill cards (a `@specialty` marker,
|
|
321
|
-
a `@chapter`/`@page`, a `@section`) makes every skill card inside it
|
|
322
|
-
two-column: `@specialty .augmerc .dc-cards-two-col`. It is the book-wide
|
|
323
|
-
counterpart of the per-card `.dc-two-col` above, and the two differ on
|
|
324
|
-
purpose. Here only running text (paragraphs and lists) flows in the two
|
|
325
|
-
columns, a paragraph MAY break between them so the columns balance, and a
|
|
326
|
-
list item stays whole. Everything else spans the card: the flavor line,
|
|
327
|
-
ability rows (AP pill + text: in a column they ran taller than the whole
|
|
328
|
-
card and could not balance), tables, outcome ladders, alerts, sub-heads
|
|
329
|
-
and rules. Per-card `.dc-two-col` instead keeps every paragraph whole and
|
|
330
|
-
spans only tables.
|
|
331
|
-
A card with its own `.dc-two-col` keeps the per-card form entirely: every
|
|
332
|
-
rule below excludes it, so its own, stricter form wins over the inherited
|
|
333
|
-
one. A card marked `.dc-allow-split` stays single-column: a balanced
|
|
334
|
-
multicol that fragments across pages leaves the first fragment's right
|
|
335
|
-
column empty.
|
|
336
|
-
Column authority for both forms is this file (COLUMNS_ALLOWLIST in
|
|
337
|
-
test/integrity.test.js): this is component-internal layout of the card
|
|
338
|
-
body, not a page or section template. */
|
|
339
|
-
.dc-cards-two-col .dc-skill-card:not(.dc-two-col, .dc-allow-split) > .dc-card-body > .dc-card-inner {
|
|
340
|
-
column-count: 2;
|
|
341
|
-
column-gap: 16px;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/* The second selector: a text block left alone between two spanning pieces
|
|
345
|
-
(or at either end) cannot balance and would leave a column empty. */
|
|
346
|
-
.dc-cards-two-col .dc-skill-card:not(.dc-two-col, .dc-allow-split) .dc-card-inner > :not(ul, ol, p:not(.dc-flavor)),
|
|
347
|
-
.dc-cards-two-col .dc-skill-card:not(.dc-two-col, .dc-allow-split) .dc-card-inner > :is(:first-child, :not(ul, ol, p:not(.dc-flavor)) + *):is(:last-child, :has(+ :not(ul, ol, p))) {
|
|
348
|
-
column-span: all;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.dc-cards-two-col .dc-skill-card:not(.dc-two-col, .dc-allow-split) .dc-card-inner li {
|
|
352
|
-
break-inside: avoid;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/* An ability row under text that runs in the two columns sat flush on its
|
|
356
|
-
last line: the column row drops that text's bottom margin at the
|
|
357
|
-
spanning row. The row is moved down, not given a margin: a 0.08in margin
|
|
358
|
-
cost two pages in the Field Guide and split a card across a page turn, and
|
|
359
|
-
0.04in cost one page (measured). */
|
|
360
|
-
.dc-cards-two-col .dc-skill-card:not(.dc-two-col, .dc-allow-split) .dc-card-inner > :is(p:not(.dc-flavor), ul, ol) + :is(p:not(.dc-flavor), ul, ol) + .dc-ability {
|
|
361
|
-
position: relative;
|
|
362
|
-
top: 0.06in;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
305
|
/* A split card (`.dc-allow-split`) never opens with just its tab and a line
|
|
366
306
|
or two at a page foot: its first two blocks (the flavor line and a
|
|
367
307
|
possibly one-line ability) stay with the first two lines of what follows,
|
|
@@ -383,14 +323,14 @@
|
|
|
383
323
|
|
|
384
324
|
/* Horizontal skill placement — not currently attempted. `.dc-specialty
|
|
385
325
|
.skill-grid { columns: 2 }` and `display: grid` are both worth testing
|
|
386
|
-
before more work goes into the alternatives:
|
|
387
|
-
|
|
326
|
+
before more work goes into the alternatives: an `@columns 2` run in the
|
|
327
|
+
card's body, which shrinks each card vertically so 2 fit per page, or
|
|
388
328
|
cutting skill body text length at the markdown level. */
|
|
389
329
|
|
|
390
330
|
/* Scoped to the skill card on purpose. This mark is an absolutely positioned
|
|
391
331
|
box, and an absolutely positioned box inside a multicol run stops Chromium
|
|
392
332
|
balancing the run's last fragment: it stretches to the full page height
|
|
393
|
-
instead of shrinking to its content, so a
|
|
333
|
+
instead of shrinking to its content, so a column run (`@columns 2`)
|
|
394
334
|
that ends in a `@card` took a page of its own and spilled its bottom edge
|
|
395
335
|
onto one more. The `@card` primitive shares the `.dc-card-body` class and
|
|
396
336
|
never meant to carry this tick (its tokens are the skill card's). */
|
|
@@ -289,17 +289,6 @@ h1.dc-chevron {
|
|
|
289
289
|
break-inside: avoid;
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
/* Core glues every section to whatever precedes it (`:where(.section) >
|
|
293
|
-
:where(:first-child) { break-before: avoid }`), which chains heading →
|
|
294
|
-
lede → section into one unbreakable run. When that section cannot fit,
|
|
295
|
-
Chromium has to break inside the chain: it split the lede, or with the
|
|
296
|
-
lede kept whole, stranded the heading at the page foot. A lede stands on
|
|
297
|
-
its own, so the section after it may start a new page; the section's own
|
|
298
|
-
break-inside: avoid still moves it whole. */
|
|
299
|
-
.dc-intro + .section > :first-child {
|
|
300
|
-
break-before: auto;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
292
|
/* ── Spec tweak section header ────────────────────────────────────────── */
|
|
304
293
|
.dc-spec-tweak {
|
|
305
294
|
font-size: 22px;
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
shadow, not an absolutely positioned `::after` box: an absolutely
|
|
221
221
|
positioned box inside a multicol run stops Chromium balancing the run's
|
|
222
222
|
last fragment (it stretches to the full page height), which a
|
|
223
|
-
|
|
223
|
+
column run (`@columns 2`) ending in an `@outcome` showed. */
|
|
224
224
|
box-shadow:
|
|
225
225
|
inset 0 0 0 4px var(--dc-outcomes-surface),
|
|
226
226
|
inset 0 0 0 5px var(--dc-outcomes-inner-border);
|
|
@@ -762,13 +762,6 @@ p.dc-img-wrapper {
|
|
|
762
762
|
width: 100%;
|
|
763
763
|
}
|
|
764
764
|
|
|
765
|
-
.dc-at-a-glance-cards {
|
|
766
|
-
display: grid;
|
|
767
|
-
grid-template-columns: repeat(3, 1fr);
|
|
768
|
-
gap: 18px;
|
|
769
|
-
margin: 0 0 18px;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
765
|
.dc-at-a-glance-card {
|
|
773
766
|
background: var(--paper-light);
|
|
774
767
|
border: 1px solid var(--paper-aged);
|
|
@@ -146,15 +146,10 @@
|
|
|
146
146
|
absolute-positioned copy that used to live here unioned with that rule
|
|
147
147
|
and produced an incoherent box model — no rule belongs here. */
|
|
148
148
|
|
|
149
|
-
/*
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`@block .gp-columns-2` and `@page … .gp-columns-2` included, neither of
|
|
154
|
-
which is a `.section`. */
|
|
155
|
-
.gp-columns-2 {
|
|
156
|
-
column-rule: 1pt solid var(--border-hairline);
|
|
157
|
-
}
|
|
149
|
+
/* The hairline between columns is `--gp-column-rule`, set once at `:root` in
|
|
150
|
+
dc-component-defaults.css. There is no column selector here: a rule on
|
|
151
|
+
core's column classes would outrank core's own (this package's layer wins),
|
|
152
|
+
and the token is the hook core reads. */
|
|
158
153
|
|
|
159
154
|
|
|
160
155
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -182,16 +177,8 @@
|
|
|
182
177
|
Safe w.r.t. margin collapsing: .section's non-zero vertical padding
|
|
183
178
|
already blocks parent/child margin collapse, so flow-root changes no
|
|
184
179
|
spacing for any variant that keeps that padding.
|
|
185
|
-
It is a default, not a mandate
|
|
186
|
-
|
|
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)) {
|
|
180
|
+
It is a default, not a mandate; `:where()` keeps the specificity at zero. */
|
|
181
|
+
:where(.section) {
|
|
195
182
|
display: flow-root;
|
|
196
183
|
}
|
|
197
184
|
|
|
@@ -128,46 +128,6 @@
|
|
|
128
128
|
--dc-section-shadow: none;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
/* ── COLUMN PANEL — the section variant for a column run ──────────────────
|
|
132
|
-
`.dc-column-panel` is the CHROME half of what `.two-column` /
|
|
133
|
-
`.three-column` used to mean. Those names were the book's own generic
|
|
134
|
-
spelling of core's `.gp-columns-2` / `.gp-columns-3` (gutterpress
|
|
135
|
-
CLAUDE.md §6: a theme must not ship competing generic column names), but
|
|
136
|
-
they were doing two unrelated jobs at once — the column COUNT and this
|
|
137
|
-
card treatment. Core owns the count now; the count class says how many
|
|
138
|
-
columns, this class says what the box around them looks like.
|
|
139
|
-
|
|
140
|
-
Authors write both: `@section .gp-columns-2 .dc-column-panel`. A column
|
|
141
|
-
run that wants no card is `@section .gp-columns-2` alone — which is the
|
|
142
|
-
point of splitting them.
|
|
143
|
-
|
|
144
|
-
Top/bottom borders only: the base `.section` card's clip-path corner
|
|
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. */
|
|
156
|
-
.section.dc-column-panel {
|
|
157
|
-
--dc-section-surface: none;
|
|
158
|
-
--dc-section-shadow: none;
|
|
159
|
-
--dc-section-pad-top: var(--space-sm);
|
|
160
|
-
--dc-section-pad-right: var(--space-md);
|
|
161
|
-
--dc-section-pad-bottom: var(--space-sm);
|
|
162
|
-
--dc-section-pad-left: var(--space-md);
|
|
163
|
-
background: var(--dc-section-bg);
|
|
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);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
131
|
/* ── Tabbed variant — first-child heading sits ABOVE the section ─────────
|
|
172
132
|
`@section .dc-tabbed` opts the section into a `.dc-card-tab → .dc-card-body`
|
|
173
133
|
silhouette. Flex container is required so the first-child heading's
|
|
@@ -305,14 +265,6 @@
|
|
|
305
265
|
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
|
|
306
266
|
}
|
|
307
267
|
|
|
308
|
-
/* Column-panel header bar: column-span keeps it from being trapped in column 1.
|
|
309
|
-
Its margins follow the variant's pad tokens. The bar is the panel's top, so
|
|
310
|
-
the variant has no border-top. */
|
|
311
|
-
.section.dc-column-panel > h2:first-child,
|
|
312
|
-
.section.dc-column-panel > h3:first-child {
|
|
313
|
-
column-span: all;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
268
|
/* Heading bar followed immediately by a table — give the table head room
|
|
317
269
|
so the bar and `<th>` background don't bleed into a single coloured block.
|
|
318
270
|
The two selectors are spelled out; :is(h2, h3) would be equivalent and
|
|
@@ -323,43 +275,6 @@
|
|
|
323
275
|
margin-top: var(--space-sm);
|
|
324
276
|
}
|
|
325
277
|
|
|
326
|
-
/* Column-break orphan protection: keep first paragraph with the spanning bar.
|
|
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. */
|
|
336
|
-
.section.dc-column-panel > h2:first-child + *,
|
|
337
|
-
.section.dc-column-panel > h3:first-child + * {
|
|
338
|
-
break-before: avoid;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/* Span the intro paragraph across both columns so it reads as a shared
|
|
342
|
-
preamble rather than being trapped in column 1. */
|
|
343
|
-
.section.dc-column-panel > h2:first-child + p,
|
|
344
|
-
.section.dc-column-panel > h3:first-child + p {
|
|
345
|
-
column-span: all;
|
|
346
|
-
break-inside: avoid;
|
|
347
|
-
}
|
|
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
|
-
|
|
358
|
-
/* Multi-column body copy: kill rivers from narrow-column justification. */
|
|
359
|
-
.section.dc-column-panel p {
|
|
360
|
-
text-align: left;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
278
|
/* Section sub-heading H3 — H3s inside a section that are NOT the first child
|
|
364
279
|
(i.e. NOT the section's header bar) get a left-accent border matching the
|
|
365
280
|
section accent. This visually distinguishes subsection headings from the
|
|
@@ -657,17 +572,17 @@ h3 + .section {
|
|
|
657
572
|
id scope in the book's own sheet (unlayered) — do NOT add bespoke rules here unless an
|
|
658
573
|
observed problem requires it.
|
|
659
574
|
|
|
660
|
-
OBSERVED PROBLEM (2026-05-25): When the walkthrough's first card
|
|
661
|
-
|
|
575
|
+
OBSERVED PROBLEM (2026-05-25): When the walkthrough's first card sits in
|
|
576
|
+
an `@columns 2` run for an internal Handle/Designation split, the longer h4
|
|
662
577
|
text ("What's Yr Handle?") wraps to 2 lines while shorter text
|
|
663
578
|
("Designation") fits on 1 line at the narrow column width. The body
|
|
664
579
|
text below each h4 then starts at different baselines across columns
|
|
665
580
|
— visible asymmetry the user-eye reads as a production error. Fix:
|
|
666
|
-
reserve uniform heading height inside
|
|
667
|
-
both columns' body content starts at the same y. Keyed on the
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
.dc-citizen-walkthrough.gp-columns
|
|
581
|
+
reserve uniform heading height inside the walkthrough's column runs so
|
|
582
|
+
both columns' body content starts at the same y. Keyed on the run
|
|
583
|
+
(`.gp-columns`, what `@columns` emits), not on the card chrome: the
|
|
584
|
+
asymmetry is caused by the narrow column measure. */
|
|
585
|
+
.dc-citizen-walkthrough .gp-columns h4 {
|
|
671
586
|
min-height: calc(2 * 1.35em);
|
|
672
587
|
}
|
|
673
588
|
|
|
@@ -704,35 +619,57 @@ h3 + .section {
|
|
|
704
619
|
--dc-npc-stat-secondary → secondary stat-line color (default --ink-smoke)
|
|
705
620
|
Override per chapter / per faction in the book's own sheet selector chains. */
|
|
706
621
|
/* Page breaks. A block with `####` entries is a RUN of creatures: it may break
|
|
707
|
-
between entries (token `auto`; this also stops
|
|
708
|
-
from jumping the whole block to the next page) and never inside one. An
|
|
709
|
-
has no wrapper — it is an `h4` and the siblings that follow it up to
|
|
710
|
-
`h4` — so "never inside" is a keep-with-next chain: every child but
|
|
711
|
-
of its entry takes `break-after: avoid`, and each block of an entry
|
|
712
|
-
unbreakable so a paragraph cannot split between columns. Chromium
|
|
713
|
-
chain together when it fits a page and lets it break only when it
|
|
714
|
-
title bar (h3) and its intro chain on to the first entry, so
|
|
715
|
-
alone at a page foot. A block with no `h4` is ONE creature
|
|
716
|
-
and stays whole like any section. `.dc-allow-split` is
|
|
717
|
-
unusually long entry: it keeps only each entry's name with
|
|
718
|
-
blocks (the stat lines) and headings with what follows, and
|
|
719
|
-
break (blocks too).
|
|
720
|
-
|
|
622
|
+
between entries (token `auto`; this also stops a column run in the section
|
|
623
|
+
from jumping the whole block to the next page) and never inside one. An
|
|
624
|
+
entry has no wrapper — it is an `h4` and the siblings that follow it up to
|
|
625
|
+
the next `h4` — so "never inside" is a keep-with-next chain: every child but
|
|
626
|
+
the last of its entry takes `break-after: avoid`, and each block of an entry
|
|
627
|
+
is itself unbreakable so a paragraph cannot split between columns. Chromium
|
|
628
|
+
keeps a chain together when it fits a page and lets it break only when it
|
|
629
|
+
cannot. The title bar (h3) and its intro chain on to the first entry, so
|
|
630
|
+
the bar is never alone at a page foot. A block with no `h4` is ONE creature
|
|
631
|
+
(the bar names it) and stays whole like any section. `.dc-allow-split` is
|
|
632
|
+
the opt-in for an unusually long entry: it keeps only each entry's name with
|
|
633
|
+
its first two blocks (the stat lines) and headings with what follows, and
|
|
634
|
+
lets the rest break (blocks too).
|
|
635
|
+
|
|
636
|
+
The entries are either direct children of the section (one column) or the
|
|
637
|
+
children of the `@columns` run inside it (`.gp-columns`); the rules below take
|
|
638
|
+
both. */
|
|
639
|
+
.section.dc-npc-stat:is(:has(> h4), :has(> .gp-columns > h4)) {
|
|
721
640
|
--dc-section-break-inside: auto;
|
|
722
641
|
}
|
|
723
642
|
.section.dc-npc-stat:has(> h4) > :not(:last-child, :has(+ h4:not(:first-of-type))) {
|
|
724
643
|
break-after: avoid;
|
|
725
644
|
}
|
|
726
|
-
|
|
645
|
+
/* In an `@columns` run only the entries are chained. Text before the first `####`
|
|
646
|
+
(a lead) is not chained to that entry: a lead of several paragraphs chained to a
|
|
647
|
+
whole entry is one unbreakable run, and when it cannot fit at a page foot Chromium
|
|
648
|
+
ends the page with the run's first row EMPTY (the bar and a blank box), the
|
|
649
|
+
content starting overleaf. A lead above the run is chained as before. */
|
|
650
|
+
.section.dc-npc-stat > .gp-columns:has(> h4) > :is(h4, h4 ~ *):not(:last-child, :has(+ h4:not(:first-of-type))) {
|
|
651
|
+
break-after: avoid;
|
|
652
|
+
}
|
|
653
|
+
:is(.section.dc-npc-stat, .section.dc-npc-stat > .gp-columns):has(> h4) > :is(h4 ~ p, h4 ~ blockquote) {
|
|
727
654
|
break-inside: avoid;
|
|
728
655
|
}
|
|
729
|
-
.section.dc-npc-stat.dc-allow-split:has(> h4) > :is(h4 ~ p, h4 ~ blockquote) {
|
|
656
|
+
:is(.section.dc-npc-stat.dc-allow-split, .section.dc-npc-stat.dc-allow-split > .gp-columns):has(> h4) > :is(h4 ~ p, h4 ~ blockquote) {
|
|
730
657
|
break-inside: auto;
|
|
731
658
|
}
|
|
732
|
-
.section.dc-npc-stat.dc-allow-split:has(> h4) > :not(:last-child, h1, h2, h3, h4, h5, h6, h4 + *, h4 + * + *, blockquote + p) {
|
|
659
|
+
:is(.section.dc-npc-stat.dc-allow-split, .section.dc-npc-stat.dc-allow-split > .gp-columns):has(> h4) > :not(:last-child, h1, h2, h3, h4, h5, h6, h4 + *, h4 + * + *, blockquote + p) {
|
|
733
660
|
break-after: auto;
|
|
734
661
|
}
|
|
735
662
|
|
|
663
|
+
/* The `@columns` run of an entries block is glued to the bar and intro above it,
|
|
664
|
+
as a direct entry list is (the h3's own `break-after: avoid` plus the chain
|
|
665
|
+
above). Without this, Chromium can end a page with the bar and an EMPTY first
|
|
666
|
+
row of the run, and the first entry starts the next page (measured with
|
|
667
|
+
printToPDF: bar stranded at the foot, `@npc-stat` with `@columns 2` inside;
|
|
668
|
+
the old `@npc-stat .gp-columns-2` never did it). */
|
|
669
|
+
.section.dc-npc-stat > .gp-columns {
|
|
670
|
+
break-before: avoid;
|
|
671
|
+
}
|
|
672
|
+
|
|
736
673
|
.dc-npc-stat h4 {
|
|
737
674
|
border-top: var(--dc-npc-stat-rule-width) solid var(--dc-npc-stat-rule);
|
|
738
675
|
padding-top: 0.05in;
|
|
@@ -802,11 +739,11 @@ h3 + .section {
|
|
|
802
739
|
[repeat for 6+ specialties]
|
|
803
740
|
@end-section
|
|
804
741
|
|
|
805
|
-
The
|
|
806
|
-
columns
|
|
807
|
-
.dc-card-grid section has no card
|
|
808
|
-
wrapper) plus the per-card density
|
|
809
|
-
comfortably on one page. */
|
|
742
|
+
The grid sets no columns of its own: the author puts the cards in
|
|
743
|
+
`@columns 2` … `@end-columns` inside it, like any other columned run. Here
|
|
744
|
+
we own the chrome-reset (the .dc-card-grid section has no card
|
|
745
|
+
background/shadow — it's a pure layout wrapper) plus the per-card density
|
|
746
|
+
tweaks so all 8 specialty cards fit comfortably on one page. */
|
|
810
747
|
|
|
811
748
|
/* Card grid: no panel, no padding; the cards carry the visuals. */
|
|
812
749
|
.section.dc-card-grid {
|
|
@@ -820,22 +757,13 @@ h3 + .section {
|
|
|
820
757
|
/* A layout wrapper, not a cohesive block: it may span pages when the cards
|
|
821
758
|
exceed one page. */
|
|
822
759
|
break-inside: auto;
|
|
760
|
+
/* The cards carry the visuals; no hairline between their columns. */
|
|
761
|
+
--gp-column-rule: none;
|
|
823
762
|
}
|
|
824
763
|
|
|
825
|
-
/*
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
`.gp-columns-2` in components/*.css — an ornament, not a layout rule. */
|
|
829
|
-
|
|
830
|
-
/* ── LAYOUT: CARD GRID (.dc-card-grid section) ─────────────────────────────── */
|
|
831
|
-
/* Specialty card grid — two columns, auto-fill. Layout owns columns + column-
|
|
832
|
-
span here; component styling (chrome reset, card density, break control)
|
|
833
|
-
lives in components/*.css under the same .dc-card-grid selector. */
|
|
834
|
-
.dc-card-grid {
|
|
835
|
-
columns: 2;
|
|
836
|
-
column-gap: var(--gutter);
|
|
837
|
-
column-fill: balance;
|
|
838
|
-
}
|
|
764
|
+
/* ── DENSITY: CARD GRID (.dc-card-grid section) ────────────────────────────── */
|
|
765
|
+
/* The first specialty of the grid never forces a break: it is the first child
|
|
766
|
+
of the section, or of the `@columns` run inside it. */
|
|
839
767
|
.dc-card-grid .dc-specialty:first-child { break-before: avoid; }
|
|
840
768
|
|
|
841
769
|
/* Compact specialty card density: 10 cards on one page in 2 columns */
|
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
--dc-section-accent: var(--brand-magenta); /* brand signal at every section header (was --blood) */
|
|
11
11
|
--dc-section-break-inside: avoid; /* `auto` lets a section split across pages/columns (.dc-allow-split, multicol runs) */
|
|
12
12
|
|
|
13
|
+
/* Column runs (`@columns N`). Core owns every column property; these are its
|
|
14
|
+
two author hooks, set once, with no column selector. Gap: the book's
|
|
15
|
+
structural gutter. Rule: the hairline between columns (needs a Gutterpress
|
|
16
|
+
that reads `--gp-column-rule`; older ones ignore it). */
|
|
17
|
+
--gp-column-gap: var(--gutter);
|
|
18
|
+
--gp-column-rule: 1pt solid var(--border-hairline);
|
|
19
|
+
|
|
13
20
|
/* Section chrome. A variant sets these on its own rule instead of cancelling
|
|
14
21
|
.section's panel. Surface/shadow are the `content` of the ::before/::after
|
|
15
22
|
layers ("" paints, `none` removes the layer); the pads drive .section's
|
package/styles/dc-native.css
CHANGED
|
@@ -245,60 +245,38 @@ figure {
|
|
|
245
245
|
overflow: clip;
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
/* ── 5.
|
|
248
|
+
/* ── 5. COLUMN RUNS: components must fragment naturally inside them ────────
|
|
249
|
+
These rules are components REACTING to sitting inside a column run
|
|
250
|
+
(`@columns N`, which emits `div.gp-columns` + `.gp-columns-N`). They set no
|
|
251
|
+
column property: how many columns, the gap, the rule and the fill are
|
|
252
|
+
core's (`--gp-column-gap` / `--gp-column-rule` are set once, at `:root`, in
|
|
253
|
+
dc-component-defaults.css).
|
|
254
|
+
|
|
249
255
|
`.section` reads `break-inside: var(--dc-section-break-inside)`, default
|
|
250
256
|
`avoid` (components/section.css; token in dc-component-defaults.css). This
|
|
251
257
|
rule sets the TOKEN, not the property, so the component keeps the only
|
|
252
|
-
`break-inside` declaration. Measured DOM shape: `.gp-columns
|
|
253
|
-
container
|
|
254
|
-
taller than one column can then never split, so Chromium drops it
|
|
255
|
-
into one column fragment and the facing column dies — the dead-column
|
|
258
|
+
`break-inside` declaration. Measured DOM shape: `.gp-columns` is a WRAPPER
|
|
259
|
+
div (the multicol container) and the `.section` blocks sit INSIDE it. A
|
|
260
|
+
section taller than one column can then never split, so Chromium drops it
|
|
261
|
+
whole into one column fragment and the facing column dies — the dead-column
|
|
256
262
|
collapse. Removing this override cost 3 pages of pagination churn when
|
|
257
263
|
measured (302pp vs 299pp), so it is load-bearing, not legacy.
|
|
258
264
|
|
|
259
|
-
Keys on the
|
|
260
|
-
fragmentation, and the wrapper
|
|
261
|
-
|
|
265
|
+
Keys on the run, not on any card around it: this is about multicol
|
|
266
|
+
fragmentation, and the wrapper is a `@columns` run. A run is `.gp-columns`
|
|
267
|
+
(what `@columns` emits) or a count class on its own, so the selector takes
|
|
268
|
+
either: `[class*="gp-columns-"]` is the count classes, and the rule reads
|
|
269
|
+
the same for -1 to -5. */
|
|
262
270
|
/* Selector is the descendant form on purpose: a `.section` that is itself the
|
|
263
271
|
multicol container keeps the default. */
|
|
264
|
-
.gp-columns-
|
|
265
|
-
.gp-columns-3 .section {
|
|
272
|
+
:is(.gp-columns, [class*="gp-columns-"]) .section {
|
|
266
273
|
--dc-section-break-inside: auto;
|
|
267
274
|
}
|
|
268
275
|
|
|
269
|
-
/*
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
native print the wrapper fragments across pages, and Chromium's
|
|
274
|
-
balance-in-fragmentation left every non-final fragment with a dead right
|
|
275
|
-
column (measured: proof17 p12/p13), so this rule said `auto`.
|
|
276
|
-
|
|
277
|
-
It says `balance` again (2026-10). `auto` with no fixed height fills the
|
|
278
|
-
first column to the BOTTOM OF THE PAGE, so every short run — a column
|
|
279
|
-
panel, a pair of gear entries — printed as a full-height box with an
|
|
280
|
-
empty right column, and a heading above one was stranded alone on its
|
|
281
|
-
page when the full-height box could not fit under it (design guide
|
|
282
|
-
p.83, p.111). Measured against the change, under gutterpress 0.11.13:
|
|
283
|
-
the Field Guide's 221 pages render pixel-identical with `balance`, and
|
|
284
|
-
the design guide loses 6 pages of holes. A run that genuinely needs
|
|
285
|
-
sequential fill opts in with core's `.gp-columns-flow`.
|
|
286
|
-
|
|
287
|
-
`:not(.gp-columns-flow)` is what makes that opt-in work: core declares
|
|
288
|
-
`.gp-columns-flow { column-fill: auto }` in its own layer, which every
|
|
289
|
-
extension layer outranks, so this rule used to win and the class did
|
|
290
|
-
nothing. `:where()` keeps the specificity at one class. */
|
|
291
|
-
:where(.gp-columns-2, .gp-columns-3):not(.gp-columns-flow) {
|
|
292
|
-
column-fill: balance;
|
|
293
|
-
}
|
|
294
|
-
/* A PAGE that is itself the multicol container (`@page .gp-columns-2`) and
|
|
295
|
-
ends in a full-width spanner: the spanner's bottom margin pushed the page
|
|
296
|
-
box past its one-sheet minimum height, and the overflow printed as a blank
|
|
297
|
-
page (Field Guide: Quickstart vs. Deep Dive, after the DriveThruRPG
|
|
298
|
-
margins). A page's last child needs no bottom margin. */
|
|
299
|
-
.page:is(.gp-columns-2, .gp-columns-3) > :last-child {
|
|
300
|
-
margin-bottom: 0;
|
|
301
|
-
}
|
|
276
|
+
/* `column-fill` is not set anywhere in this package. `balance` is the CSS
|
|
277
|
+
initial value and what a column run gets; core has no sequential-fill
|
|
278
|
+
opt-in. (A rule here used to restate `balance` for `.gp-columns-2/-3`; it
|
|
279
|
+
was a no-op.) */
|
|
302
280
|
|
|
303
281
|
/* ── 7. WALL ART placement is the markdown's, via core's gp-* classes ──────
|
|
304
282
|
Do not add a position/float/margin declaration to a `.fg-art-*` class that
|
|
@@ -320,16 +298,15 @@ figure {
|
|
|
320
298
|
}
|
|
321
299
|
|
|
322
300
|
/* Measured (p243, DOM-inspected): the CORE ELEMENTS heading is a full-width
|
|
323
|
-
spanner
|
|
301
|
+
spanner above an `@columns` run, followed by a dc-alert callout
|
|
324
302
|
whose default break-inside: avoid is UNENFORCEABLE (the card is taller than a
|
|
325
303
|
column). Chromium then emits an empty card fragment under the spanner —
|
|
326
304
|
header stranded, content headerless on the next page. A card that may
|
|
327
305
|
fragment starts filling right under its header instead. Scoped to
|
|
328
|
-
|
|
306
|
+
column runs only; standalone cards keep their avoid. */
|
|
329
307
|
/* Sets --dc-alert-break-inside (callouts.css reads it); a book restores `avoid`
|
|
330
308
|
for one instance by setting the token in context. */
|
|
331
|
-
.gp-columns-
|
|
332
|
-
.gp-columns-3 .dc-alert {
|
|
309
|
+
:is(.gp-columns, [class*="gp-columns-"]) .dc-alert {
|
|
333
310
|
--dc-alert-break-inside: auto;
|
|
334
311
|
}
|
|
335
312
|
|
|
@@ -4,12 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
── ARCHITECTURAL CONTRACT ────────────────────────────────────────────────
|
|
6
6
|
OWNS:
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
OWN internals in its own sheet (cards.css, section.css .dc-card-grid). It
|
|
10
|
-
may NOT set columns on core's `.gp-columns-*` (only their `--gp-column-gap`).
|
|
11
|
-
- .page.* content layout rules (columns, flex, grid on page content divs)
|
|
12
|
-
- .page base geometry, .column-break
|
|
7
|
+
- .page.* content layout rules (flex, grid on page content divs)
|
|
8
|
+
- .page base geometry
|
|
13
9
|
- .dc-full-page full-bleed geometry (its named-page assignment,
|
|
14
10
|
`page: full`, lives in page-rules.css alongside `@page full` — see
|
|
15
11
|
that file's "ONE BLOCK PER NAMED PAGE" contract)
|
|
@@ -23,19 +19,21 @@
|
|
|
23
19
|
- @page declarations or named-page geometry (→ page-rules.css)
|
|
24
20
|
- .dc-* component styles (→ components/*.css)
|
|
25
21
|
- :root token definitions (→ dc-palette.css, dc-component-defaults.css)
|
|
26
|
-
- div.chapter scaffolding unrelated to
|
|
22
|
+
- div.chapter scaffolding unrelated to layout (→ guide.css)
|
|
27
23
|
- @media screen rules (print-only project)
|
|
28
24
|
|
|
29
|
-
COLUMN OWNERSHIP RULE
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
COLUMN OWNERSHIP RULE: this package owns NO column layout. There is one way
|
|
26
|
+
to put content in columns, and it is Gutterpress core's: `@columns N` …
|
|
27
|
+
`@column-break` … `@end-columns` (N from 1 to 5). Core owns the count, the
|
|
28
|
+
gap (`--gp-column-gap`, set once at `:root` in dc-component-defaults.css),
|
|
29
|
+
the rule (`--gp-column-rule`, same place) and `column-fill`. No sheet here
|
|
30
|
+
sets `columns`, `column-count`, `column-gap`, `column-fill` or `column-rule`
|
|
31
|
+
(test/integrity.test.js enforces it). What this package may still own is
|
|
32
|
+
component rules that react to sitting inside a run (dc-native.css §5). The grids inside a single component (`.dc-stat-grid`,
|
|
33
|
+
`.dc-steps`, `.dc-outcome-row`) are not column layout.
|
|
35
34
|
|
|
36
35
|
AGENT RULE: "Does this selector start with .page.*, a general print
|
|
37
|
-
utility, or a div.chapter layout rule
|
|
38
|
-
another file.
|
|
36
|
+
utility, or a div.chapter layout rule?" — if NO, use another file.
|
|
39
37
|
──────────────────────────────────────────────────────────────────────── */
|
|
40
38
|
|
|
41
39
|
@layer dc.templates {
|
|
@@ -129,7 +127,7 @@
|
|
|
129
127
|
is not a sheet — it is a content run that fragments across as many sheets
|
|
130
128
|
as it needs. The component gallery's wrapper measured 27,272px tall (~27
|
|
131
129
|
sheets, 149 children), so an automatic `:has()` reservation squeezed the
|
|
132
|
-
right column out of all 27 — measured: it pushed
|
|
130
|
+
right column out of all 27 — measured: it pushed an at-a-glance card row
|
|
133
131
|
156px past its own grid box 22 sheets away. The rail only makes sense on a
|
|
134
132
|
wrapper that IS one sheet, and `@page` is how an author declares one. So
|
|
135
133
|
opting in is the feature, not a papercut.
|
|
@@ -172,20 +170,10 @@
|
|
|
172
170
|
rendered HTML (`--format html`, grepped for exact class="..." tokens)
|
|
173
171
|
before deleting.
|
|
174
172
|
|
|
175
|
-
`.column-break` (unprefixed
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
marker line core recognizes (markers.js's parseMarkerLine only matches
|
|
180
|
-
lines starting with `@`) and renders `<hr class="column-break">` with no
|
|
181
|
-
`gp-` class at all. The rendered-HTML check that cleared every other
|
|
182
|
-
rule in this pass initially cleared this one too on a broken grep
|
|
183
|
-
pipeline; re-run correctly, it is present in the design guide's DOM. */
|
|
184
|
-
.column-break {
|
|
185
|
-
color: var(--hud-magenta);
|
|
186
|
-
visibility: hidden;
|
|
187
|
-
break-after: column;
|
|
188
|
-
}
|
|
173
|
+
`.column-break` (unprefixed, `---{.column-break}` on a thematic break) was
|
|
174
|
+
here and is DELETED: it was a third spelling of core's `@column-break`.
|
|
175
|
+
The marker line works wherever the `---` did (between blocks, on its own
|
|
176
|
+
line). */
|
|
189
177
|
|
|
190
178
|
/* Specialty card image sizing lives in components/*.css (.dc-specialty-card img)
|
|
191
179
|
via --dc-specialty-card-media-width/height tokens; no page-template override
|
|
@@ -220,37 +208,13 @@
|
|
|
220
208
|
to the page box instead of letting it reach the sheet edge. */
|
|
221
209
|
|
|
222
210
|
|
|
223
|
-
/* ── COLUMN RUNS:
|
|
224
|
-
/* `.two-column` / `.three-column` are GONE (2026-08-21)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
The name was doing two unrelated jobs, which is why the fix was not a
|
|
231
|
-
rename. Job one, the column COUNT, is core's and is deleted from this file:
|
|
232
|
-
nothing here sets `columns` for a column run any more. Job two, a SECTION
|
|
233
|
-
VARIANT (card substrate, the `column-span: all` heading bar, its shared
|
|
234
|
-
preamble paragraph, break-orphan guards, gear-entry min-heights) had no
|
|
235
|
-
business riding on a count class at all; it now has its own name,
|
|
236
|
-
`.dc-column-panel`, in components/*.css. Authors write both:
|
|
237
|
-
`@section .gp-columns-2 .dc-column-panel`.
|
|
238
|
-
|
|
239
|
-
What survives here is the one genuine theme addition — the gap. Core reads
|
|
240
|
-
`--gp-column-gap` (its own default is 1.5em) so the book's structural
|
|
241
|
-
`--gutter` is expressed through core's hook rather than by re-declaring
|
|
242
|
-
`column-gap`. Nothing in this theme may set `columns` on these names again.
|
|
243
|
-
|
|
244
|
-
`column-fill` is NOT set here: it is a per-shape decision that depends on
|
|
245
|
-
whether the run fragments, and dc-native.css §6 owns it with the
|
|
246
|
-
measurements behind each value. */
|
|
247
|
-
.gp-columns-2 {
|
|
248
|
-
--gp-column-gap: var(--gutter);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.gp-columns-3 {
|
|
252
|
-
--gp-column-gap: 0.18in; /* narrower than --gutter: three measures, less air */
|
|
253
|
-
}
|
|
211
|
+
/* ── COLUMN RUNS: nothing here ────────────────────────────────────────────── */
|
|
212
|
+
/* `.two-column` / `.three-column` are GONE (2026-08-21), and since then so is
|
|
213
|
+
every other column rule of this package: `@columns N` is the one way to
|
|
214
|
+
column content, and it is core's. The gap and the hairline are two tokens
|
|
215
|
+
set at `:root` in dc-component-defaults.css (`--gp-column-gap`,
|
|
216
|
+
`--gp-column-rule`), with no column selector. A card that wants columns is
|
|
217
|
+
a plain `@section` with its body in `@columns N`. */
|
|
254
218
|
|
|
255
219
|
.dc-card-grid .dc-specialty,
|
|
256
220
|
.dc-card-grid .dc-specialty-card {
|
|
@@ -271,10 +235,10 @@
|
|
|
271
235
|
# Credits {.dc-chevron}
|
|
272
236
|
@lede ... @end-lede
|
|
273
237
|
@section .credits-colophon
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
238
|
+
@columns 2
|
|
239
|
+
**Role:** Names
|
|
240
|
+
...
|
|
241
|
+
@end-columns
|
|
278
242
|
|
|
279
243
|
── Template public API ──────────────────────────────────────────────────
|
|
280
244
|
--frontmatter-accent Single recolor knob. Drives the lede left-strip,
|
|
@@ -397,7 +361,7 @@
|
|
|
397
361
|
/* Credits colophon section: the bottom block of the frontmatter stack, a flat
|
|
398
362
|
paper-cream host (both section chrome layers off) with the lede's 4px
|
|
399
363
|
rail running down through it. The flex column keeps a trailing
|
|
400
|
-
publisher-mark image bottom-aligned while the
|
|
364
|
+
publisher-mark image bottom-aligned while the `@columns 2` run fills the height.
|
|
401
365
|
Its shadow is a 2pt right-edge strip, not --shadow-poster: the colophon
|
|
402
366
|
runs to the sheet's bottom edge (this template has no bottom margin), so
|
|
403
367
|
the poster shadow's 3pt drop would land past the sheet and print as a
|
|
@@ -410,7 +374,7 @@
|
|
|
410
374
|
--dc-section-pad-right: 0.22in;
|
|
411
375
|
--dc-section-pad-bottom: 0.18in;
|
|
412
376
|
--dc-section-pad-left: 0.22in;
|
|
413
|
-
|
|
377
|
+
--gp-column-rule: none; /* the names run in two plain columns, as they always have */
|
|
414
378
|
display: flex;
|
|
415
379
|
flex-direction: column;
|
|
416
380
|
font-size: 9.5pt;
|
|
@@ -419,10 +383,7 @@
|
|
|
419
383
|
box-shadow: inset 4px 0 0 var(--frontmatter-accent), 2pt 0 0 var(--shadow-ink); /* rail: see .dc-intro */
|
|
420
384
|
}
|
|
421
385
|
|
|
422
|
-
.page.page-credits .section.credits-colophon .
|
|
423
|
-
columns: 2;
|
|
424
|
-
column-gap: var(--gutter);
|
|
425
|
-
column-fill: balance;
|
|
386
|
+
.page.page-credits .section.credits-colophon > .gp-columns {
|
|
426
387
|
flex-grow: 1;
|
|
427
388
|
}
|
|
428
389
|
|