gp-dimm-city 1.2.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 +135 -0
- package/components.yaml +17 -45
- package/package.json +2 -2
- package/plugin.js +34 -15
- package/snippets/card-grid.md +4 -0
- package/snippets/credits-page.md +2 -2
- package/styles/components/callouts.css +7 -1
- package/styles/components/cards.css +49 -88
- package/styles/components/chrome.css +0 -11
- package/styles/components/data.css +9 -15
- package/styles/components/images.css +6 -17
- package/styles/components/section.css +69 -82
- package/styles/dc-component-defaults.css +7 -0
- package/styles/dc-core.css +19 -7
- package/styles/dc-native.css +24 -47
- package/styles/page-rules.css +9 -0
- package/styles/page-templates.css +38 -72
- package/snippets/column-panel.md +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,141 @@ 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
|
+
|
|
80
|
+
## [1.3.0] - 2026-10-11
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
- **The die chip now covers every wording of the instruction and no longer
|
|
85
|
+
disturbs the line it sits in.** Besides `ROLL THE DIE!`, the chip wraps
|
|
86
|
+
`ROLL A DIE!`, `ROLLS A DIE!` and `ROLLING THE DIE!` (uppercase, as written;
|
|
87
|
+
the text is never altered). It is redrawn as a stamped label (cream fill,
|
|
88
|
+
blood-red lettering, magenta rule and offset shadow) set as a plain inline
|
|
89
|
+
box, so a line holding a chip keeps the line height of the lines around it
|
|
90
|
+
and a chip never splits across two lines. Headings are left as plain heading
|
|
91
|
+
text. `--dc-roll-the-die-color` and `--dc-roll-the-die-border` work as before. (#18)
|
|
92
|
+
- **A run of cards or gear entries can pack two-up** (#15). Core's `.gp-grid-2` and
|
|
93
|
+
`.gp-grid-3` did nothing on a `@section` or `@column-panel`: the plugin's
|
|
94
|
+
float-containment default (`display: flow-root`) sat in a later cascade layer
|
|
95
|
+
and beat core's `display: grid` at any specificity, so every card took a full
|
|
96
|
+
row. The default now steps aside for the two grid classes, and the header bar
|
|
97
|
+
and preamble paragraph of a grid panel span the first row.
|
|
98
|
+
`@column-panel .gp-grid-2 .dc-allow-split` is the gear and item layout; the
|
|
99
|
+
design guide's Gear section shows it.
|
|
100
|
+
|
|
101
|
+
### Fixed
|
|
102
|
+
|
|
103
|
+
- A heading word wider than its box now breaks instead of running under the
|
|
104
|
+
next column or being clipped at the edge. This applies to `h1`-`h6` and the
|
|
105
|
+
`.dc-h3`-`.dc-h6` looks in every context (a two-column run is about 3.4in
|
|
106
|
+
per column), not only inside cards. Headings whose words fit are unchanged,
|
|
107
|
+
and an author's soft hyphen (`­`) still picks the break. (#13)
|
|
108
|
+
- In a loose bulleted list (blank lines between items) the `»` bullet no longer
|
|
109
|
+
prints alone on a line above the item's text. A loose item's text is wrapped
|
|
110
|
+
in a paragraph, so the bullet is now drawn on that first paragraph and sits
|
|
111
|
+
inline with the first line, in alerts and callouts too. Tight lists and
|
|
112
|
+
every other list render exactly as before. (#17)
|
|
113
|
+
- **`@npc-stat` blocks with `####` entries no longer split a creature across
|
|
114
|
+
pages or columns** (#16). The block may now break between entries (so
|
|
115
|
+
`@npc-stat .gp-columns-2` no longer jumps whole to the next page and leaves
|
|
116
|
+
a large empty area), the title bar stays with the first entry, and each
|
|
117
|
+
entry's name, stat lines and traits are kept together. Add `.dc-allow-split`
|
|
118
|
+
for an unusually long entry. A block with no `####` is unchanged.
|
|
119
|
+
- The chapter opener's `# Title` is never split across two sheets: it keeps
|
|
120
|
+
together and stays with the block after it, so a long opener moves the
|
|
121
|
+
whole title to the next page instead of leaving one line of it at the
|
|
122
|
+
foot of the page before. (#12)
|
|
123
|
+
- A callout label (`@callout gear label="SPECS"`, `[!NOTE]`, any variant) no
|
|
124
|
+
longer sits alone at the foot of a column or page with its body in the next
|
|
125
|
+
one: `.dc-alert-label` now carries `break-after: avoid`, which matters in the
|
|
126
|
+
multicol runs where a callout is allowed to split. A `@column-panel` header
|
|
127
|
+
bar can still be left at a page foot when its first block is not a
|
|
128
|
+
paragraph; that is a Chromium limit (it ignores `break-after: avoid` on a
|
|
129
|
+
`column-span: all` box), documented in `section.css`. (#14)
|
|
130
|
+
- **A split column panel that ends in an `@card` or `@outcome` shrinks to its
|
|
131
|
+
content** (#15). Both drew a decoration with an absolutely positioned box,
|
|
132
|
+
which stopped Chromium balancing the panel's last fragment: it ran the full
|
|
133
|
+
page height, so the next panel could not start beneath it. The `@card` body's
|
|
134
|
+
decorative tick is now the skill card's alone (it was never part of the
|
|
135
|
+
`@card` primitive's tokens), and the outcome ladder's inner hairline is an
|
|
136
|
+
inset shadow.
|
|
137
|
+
- **A column panel that ends flush with the page foot no longer spills its
|
|
138
|
+
bottom edge onto an extra page** (#15). The panel's bottom padding and rule
|
|
139
|
+
are painted as shadows below a box that ends with its content, so there is
|
|
140
|
+
no trailing box edge to push to the next sheet. Same picture, same spacing;
|
|
141
|
+
a neighbour with a larger top margin than the panel's bottom margin can now
|
|
142
|
+
sit up to 10px closer, because the padding that used to sit inside the panel
|
|
143
|
+
no longer adds to that gap.
|
|
144
|
+
|
|
10
145
|
## [1.2.0] - 2026-10-10
|
|
11
146
|
|
|
12
147
|
Requires Gutterpress 0.11.16 or newer (the desktop app or CLI): older
|
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."
|
|
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
|
|
@@ -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
|
|
@@ -428,14 +429,6 @@ components:
|
|
|
428
429
|
breaks: none
|
|
429
430
|
|
|
430
431
|
# ── Author classes on core markers, attrs and raw HTML ────────────────
|
|
431
|
-
- name: column-panel
|
|
432
|
-
status: live
|
|
433
|
-
origin: plugin
|
|
434
|
-
recipe: "@column-panel [.gp-columns-2] … @end-column-panel (or @section .gp-columns-2 .dc-column-panel)"
|
|
435
|
-
emits: div.section.dc-column-panel
|
|
436
|
-
css: styles/components/section.css
|
|
437
|
-
breaks: "first heading glued to its first paragraph"
|
|
438
|
-
|
|
439
432
|
- name: tabbed
|
|
440
433
|
status: live
|
|
441
434
|
origin: plugin
|
|
@@ -448,15 +441,16 @@ components:
|
|
|
448
441
|
- name: card-grid
|
|
449
442
|
status: live
|
|
450
443
|
origin: plugin
|
|
451
|
-
recipe: "@card-grid … @end-card-grid (or @section .dc-card-grid)"
|
|
452
|
-
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
|
|
453
446
|
css: [styles/components/section.css, styles/page-templates.css]
|
|
454
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."
|
|
455
449
|
|
|
456
450
|
- name: citizen-walkthrough
|
|
457
451
|
status: live
|
|
458
452
|
origin: plugin
|
|
459
|
-
recipe: "@citizen-walkthrough [
|
|
453
|
+
recipe: "@citizen-walkthrough [@columns 2 … @end-columns] … @end-citizen-walkthrough (or @section .dc-citizen-walkthrough)"
|
|
460
454
|
emits: div.section.dc-citizen-walkthrough
|
|
461
455
|
css: [styles/components/section.css, styles/dc-native.css]
|
|
462
456
|
notes: "Themed through the section chassis tokens --dc-section-accent / --dc-section-bg (see section), which the design guide documents."
|
|
@@ -487,7 +481,7 @@ components:
|
|
|
487
481
|
- name: npc-stat
|
|
488
482
|
status: live
|
|
489
483
|
origin: plugin
|
|
490
|
-
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)"
|
|
491
485
|
emits: div.section.dc-npc-stat
|
|
492
486
|
css: [styles/components/section.css]
|
|
493
487
|
tokens:
|
|
@@ -496,6 +490,7 @@ components:
|
|
|
496
490
|
- --dc-npc-stat-label-color
|
|
497
491
|
- --dc-npc-stat-rule
|
|
498
492
|
- --dc-npc-stat-rule-width
|
|
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"
|
|
499
494
|
|
|
500
495
|
- name: chapter-start
|
|
501
496
|
status: live
|
|
@@ -511,14 +506,7 @@ components:
|
|
|
511
506
|
recipe: "@page .page-credits .dc-credits"
|
|
512
507
|
emits: div.page.dc-credits
|
|
513
508
|
css: [styles/page-templates.css, styles/dc-native.css]
|
|
514
|
-
notes: "Styled in page-templates.css and dc-native.css — carried in from the books in 1.0.0."
|
|
515
|
-
|
|
516
|
-
- name: colophon-grid
|
|
517
|
-
status: live
|
|
518
|
-
origin: author
|
|
519
|
-
recipe: "raw <div class=\"dc-colophon-grid\"> on the credits page"
|
|
520
|
-
emits: div.dc-colophon-grid
|
|
521
|
-
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`."
|
|
522
510
|
|
|
523
511
|
- name: chevron
|
|
524
512
|
status: live
|
|
@@ -567,11 +555,11 @@ components:
|
|
|
567
555
|
css: styles/components/images.css
|
|
568
556
|
breaks: "break-inside: avoid"
|
|
569
557
|
|
|
570
|
-
- name: at-a-glance-
|
|
558
|
+
- name: at-a-glance-card
|
|
571
559
|
status: live
|
|
572
560
|
origin: author
|
|
573
|
-
recipe: "raw <div class=\"dc-at-a-glance-
|
|
574
|
-
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
|
|
575
563
|
css: styles/components/data.css
|
|
576
564
|
|
|
577
565
|
- name: stat-block
|
|
@@ -594,22 +582,6 @@ components:
|
|
|
594
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)"
|
|
595
583
|
notes: "The plugin reads this class (to drop data-break-inside=avoid) but never adds it."
|
|
596
584
|
|
|
597
|
-
- name: two-col
|
|
598
|
-
status: live
|
|
599
|
-
origin: author
|
|
600
|
-
recipe: "@skill {.dc-two-col}"
|
|
601
|
-
emits: div.dc-skill-card.dc-two-col
|
|
602
|
-
css: styles/components/cards.css
|
|
603
|
-
breaks: "list items and paragraphs break-inside: avoid within the columns"
|
|
604
|
-
|
|
605
|
-
- name: cards-two-col
|
|
606
|
-
status: live
|
|
607
|
-
origin: author
|
|
608
|
-
recipe: "@specialty .augmerc .dc-cards-two-col (or on @chapter / @page / @section)"
|
|
609
|
-
emits: .dc-cards-two-col (on the ancestor it is written on; styles the .dc-skill-card inside it)
|
|
610
|
-
css: styles/components/cards.css
|
|
611
|
-
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"
|
|
612
|
-
|
|
613
585
|
- name: snug
|
|
614
586
|
status: live
|
|
615
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
|
@@ -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 = [];
|
|
@@ -884,6 +886,8 @@ function dcSkillCards(state) {
|
|
|
884
886
|
const emit = (html) => out.push(makeToken('html_block', html));
|
|
885
887
|
let inCard = false;
|
|
886
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
|
|
887
891
|
let title = { name: '', tier: '' }; // the current card's title, for @continue
|
|
888
892
|
|
|
889
893
|
const cardOpen = (cont) => {
|
|
@@ -900,7 +904,9 @@ function dcSkillCards(state) {
|
|
|
900
904
|
for (let i = 0; i < content.length; i++) {
|
|
901
905
|
const tok = content[i];
|
|
902
906
|
|
|
903
|
-
|
|
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) {
|
|
904
910
|
endCard();
|
|
905
911
|
const parsed = parseSkillTitle(content[i + 1].content || '');
|
|
906
912
|
title = { name: parsed.name, tier: parsed.tier };
|
|
@@ -950,6 +956,20 @@ function dcSkillCards(state) {
|
|
|
950
956
|
do depth += content[end++].nesting; while (depth > 0);
|
|
951
957
|
belongsToCard.add(tok).add(content[end - 1]);
|
|
952
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);
|
|
953
973
|
if (tok.type.startsWith('layout_') && !belongsToCard.has(tok)) endCard();
|
|
954
974
|
|
|
955
975
|
if (!inCard) {
|
|
@@ -1357,8 +1377,7 @@ export const markers = {
|
|
|
1357
1377
|
// styles/components/section.css. No variants: none of these is styled per
|
|
1358
1378
|
// specialty (that lives on `.dc-specialty.<name>`, from `@specialty`), and
|
|
1359
1379
|
// modifiers such as `.dc-plain`, `.dc-snug`, `.dc-allow-split` and
|
|
1360
|
-
// `.gp-columns-2` stay author classes
|
|
1361
|
-
'column-panel': { section: true, class: 'dc-column-panel' },
|
|
1380
|
+
// `.gp-columns-2` stay author classes.
|
|
1362
1381
|
tabbed: { section: true, class: 'dc-tabbed' },
|
|
1363
1382
|
'card-grid': { section: true, class: 'dc-card-grid' },
|
|
1364
1383
|
'citizen-walkthrough': { section: true, class: 'dc-citizen-walkthrough' },
|
package/snippets/card-grid.md
CHANGED
package/snippets/credits-page.md
CHANGED
|
@@ -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) ─────────────────────────────── */
|
|
@@ -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,11 +323,18 @@
|
|
|
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
|
|
331
|
+
box, and an absolutely positioned box inside a multicol run stops Chromium
|
|
332
|
+
balancing the run's last fragment: it stretches to the full page height
|
|
333
|
+
instead of shrinking to its content, so a column run (`@columns 2`)
|
|
334
|
+
that ends in a `@card` took a page of its own and spilled its bottom edge
|
|
335
|
+
onto one more. The `@card` primitive shares the `.dc-card-body` class and
|
|
336
|
+
never meant to carry this tick (its tokens are the skill card's). */
|
|
337
|
+
.dc-skill-card > .dc-card-body::before {
|
|
391
338
|
content: '';
|
|
392
339
|
position: absolute;
|
|
393
340
|
top: 10px;
|
|
@@ -475,21 +422,35 @@
|
|
|
475
422
|
font-weight: 600;
|
|
476
423
|
}
|
|
477
424
|
|
|
478
|
-
/* ROLL THE DIE! emphasis
|
|
479
|
-
--
|
|
480
|
-
|
|
425
|
+
/* ROLL THE DIE! emphasis chip — tokens: --dc-roll-the-die-color (text, default
|
|
426
|
+
--blood), --dc-roll-the-die-border (default --hud-magenta).
|
|
427
|
+
A stamped label for an instruction to act. It is a plain non-replaced
|
|
428
|
+
`display: inline` box on purpose: an inline box's vertical padding, border
|
|
429
|
+
and shadow paint around its text without adding to the line box, so a line
|
|
430
|
+
holding a chip is exactly as tall as one without, and nothing around it
|
|
431
|
+
moves. `line-height: 0` keeps the chip's own font from growing the line,
|
|
432
|
+
the font is smaller than the body, and `white-space: nowrap` moves a chip that
|
|
433
|
+
would straddle a line end to the next line whole (it still adds no height).
|
|
434
|
+
`box-decoration-break: clone` keeps both edges if one ever does fragment.
|
|
435
|
+
The fill is opaque paper, so contrast is fixed whatever it sits on: --blood on --paper-cream is 7.7:1
|
|
436
|
+
(it failed at 2.64:1 on skill-card paper-aged as --crimson text). */
|
|
481
437
|
.dc-roll-the-die {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
438
|
+
display: inline;
|
|
439
|
+
font-family: var(--font-display);
|
|
440
|
+
font-size: 0.8em;
|
|
441
|
+
font-weight: 700;
|
|
442
|
+
line-height: 0;
|
|
443
|
+
white-space: nowrap;
|
|
444
|
+
letter-spacing: 0.06em;
|
|
445
|
+
text-transform: uppercase;
|
|
446
|
+
color: var(--dc-roll-the-die-color);
|
|
447
|
+
background-color: var(--paper-cream);
|
|
448
|
+
padding: 0.14em 0.35em;
|
|
449
|
+
margin-inline: 0.05em;
|
|
450
|
+
border: 1.5pt solid var(--dc-roll-the-die-border);
|
|
451
|
+
border-bottom-width: 3pt;
|
|
452
|
+
box-shadow: 1pt 1pt 0 var(--dc-roll-the-die-border);
|
|
453
|
+
box-decoration-break: clone;
|
|
493
454
|
}
|
|
494
455
|
|
|
495
456
|
/* ── AP base + author-facing variants ─────────────────────────────────── */
|