@brandon_m_behring/book-scaffold-astro 4.0.0 → 4.1.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/CLAUDE.md CHANGED
@@ -66,7 +66,9 @@ Two callout families coexist. Authors import what they need.
66
66
 
67
67
  **Academic family** (`src/components/callouts/`, 10 components): `NoteBox`, `ExampleBox`, `DynConnect`, `InsightBox`, `WarnBox`, `CounterBox`, `TipBox`, `OpenQuestion`, `PaperBox`, `ResultBox`. Plus `Theorem` (unified for theorem/proposition/lemma/corollary/definition/example/exercise/remark/proof).
68
68
 
69
- **Utility components** (`src/components/`, any profile): `Cite`, `XRef`, `Figure`, `MarginNote`, `Sidenote`, `WeekRef`, `CodeRef`, `CodeBlock`, `Tag`, `StatusBadge`.
69
+ **Pedagogy family** (v4.1.0+, any profile, 3 components): `Pitfall` (rose; "common mistake" — distinct from `WarnBox`'s preemptive warning), `WorkedExample` (plum; collapsible `<details>` block with `#worked-example-{id}` anchor for deep links), `YouWillLearn` (gold; chapter-opener with optional `prerequisites` prop). Slot bullets/code freely; render at any preset.
70
+
71
+ **Utility components** (`src/components/`, any profile): `Cite`, `XRef`, `Figure`, `MarginNote`, `Sidenote`, `WeekRef`, `CodeRef`, `CodeBlock`, `Tag`, `StatusBadge`, `PocLayout` (v4.1.0+; wraps slot in a per-`kind` layout shell — 5 closed-union kinds; see `recipes/15-defining-styles.md`).
70
72
 
71
73
  Full reference in `recipes/04-component-library.md`.
72
74
 
@@ -0,0 +1,17 @@
1
+ ---
2
+ /**
3
+ * Pitfall — distinct from generic <WarnBox>.
4
+ * Retrospective "this often goes wrong" callout (vs WarnBox's preemptive
5
+ * "this could go wrong"). React.dev "Pitfall" vocabulary. Closes #58.
6
+ *
7
+ * Family: crimson (--callout-pitfall, deeper than warn-rose).
8
+ */
9
+ interface Props {
10
+ title?: string;
11
+ }
12
+ const { title = 'Common mistake' } = Astro.props;
13
+ ---
14
+ <aside class="callout callout-pitfall" role="note">
15
+ <strong class="callout-title">{title}</strong>
16
+ <div class="callout-body"><slot /></div>
17
+ </aside>
@@ -0,0 +1,28 @@
1
+ ---
2
+ /**
3
+ * PocLayout — per-PoC-kind layout selector (closes #56).
4
+ *
5
+ * Wraps slotted content in a <div> that swaps 3 CSS variables per kind
6
+ * (line-length, vertical rhythm, heading emphasis). 5 closed-union kinds.
7
+ *
8
+ * CSS variable surface defined in package/styles/poc-layouts.css. Authors
9
+ * can override per-kind in their own stylesheets via `:where(.poc-layout-X)`.
10
+ *
11
+ * Closed `kind` union: discriminated literal type. To add a 6th kind in
12
+ * a future release, expand the union + add a CSS block in poc-layouts.css.
13
+ */
14
+ export type PocLayoutKind =
15
+ | 'tutorial'
16
+ | 'how-to'
17
+ | 'tldr'
18
+ | 'part-summary'
19
+ | 'cheat-sheet';
20
+
21
+ interface Props {
22
+ kind: PocLayoutKind;
23
+ }
24
+ const { kind } = Astro.props;
25
+ ---
26
+ <div class={`poc-layout poc-layout-${kind}`}>
27
+ <slot />
28
+ </div>
@@ -0,0 +1,29 @@
1
+ ---
2
+ /**
3
+ * WorkedExample — collapsible demonstration block (closes #57).
4
+ *
5
+ * Pedagogical worked-example-effect treatment (Sweller/Cooper). Uses
6
+ * native <details>; collapsed by default unless `expanded` prop set.
7
+ * The outer aside carries `id="worked-example-{id}"` for deep links
8
+ * from TL;DRs or cheat-sheets. Prefix avoids collision with author
9
+ * heading anchors.
10
+ *
11
+ * Family: plum (--callout-worked, reuses --callout-official authority hue).
12
+ */
13
+ interface Props {
14
+ id: string;
15
+ title: string;
16
+ expanded?: boolean;
17
+ }
18
+ const { id, title, expanded = false } = Astro.props;
19
+ const anchorId = `worked-example-${id}`;
20
+ ---
21
+ <aside class="callout callout-worked" id={anchorId} role="note">
22
+ <details open={expanded}>
23
+ <summary>
24
+ <strong class="callout-title">{title}</strong>
25
+ <span class="callout-chip">Worked example</span>
26
+ </summary>
27
+ <div class="callout-body"><slot /></div>
28
+ </details>
29
+ </aside>
@@ -0,0 +1,24 @@
1
+ ---
2
+ /**
3
+ * YouWillLearn — chapter-opener "what this chapter delivers" callout
4
+ * (closes #59). React.dev pedagogy vocabulary; Bloom's-taxonomy framing.
5
+ *
6
+ * Slotted body — author writes markdown bullets directly.
7
+ * Optional `prerequisites` prop renders a small "Before you start" sub-block.
8
+ *
9
+ * Family: gold (--callout-learn, reuses --callout-insight to signal importance).
10
+ */
11
+ interface Props {
12
+ prerequisites?: string;
13
+ }
14
+ const { prerequisites } = Astro.props;
15
+ ---
16
+ <aside class="callout callout-learn" role="note">
17
+ {prerequisites && (
18
+ <div class="callout-prereq">
19
+ <strong class="callout-prereq-label">Before you start:</strong> {prerequisites}
20
+ </div>
21
+ )}
22
+ <strong class="callout-title">You will learn</strong>
23
+ <div class="callout-body"><slot /></div>
24
+ </aside>
package/dist/schemas.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/schemas-entry.ts
2
- import { existsSync as existsSync2 } from "fs";
2
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
3
3
  import { defineCollection } from "astro:content";
4
4
  import { glob, file } from "astro/loaders";
5
5
 
@@ -565,6 +565,15 @@ function resolvePreset(explicitPreset, explicitProfile) {
565
565
  }
566
566
 
567
567
  // src/schemas-entry.ts
568
+ function isYamlEmpty(path) {
569
+ try {
570
+ const raw = readFileSync2(path, "utf8");
571
+ const stripped = raw.split(/\r?\n/).map((line) => line.replace(/#.*$/, "").trim()).filter((line) => line.length > 0).join("");
572
+ return stripped === "" || stripped === "[]";
573
+ } catch {
574
+ return false;
575
+ }
576
+ }
568
577
  function frontmatterCollection(schema, base = "./src/content/frontmatter") {
569
578
  return defineCollection({
570
579
  loader: glob({
@@ -589,7 +598,7 @@ function defineBookSchemas(opts = {}) {
589
598
  const collections = {
590
599
  chapters
591
600
  };
592
- if (existsSync2("./sources/manifest.yaml")) {
601
+ if (existsSync2("./sources/manifest.yaml") && !isYamlEmpty("./sources/manifest.yaml")) {
593
602
  collections.sources = defineCollection({
594
603
  loader: file("sources/manifest.yaml"),
595
604
  schema: sourcesSchema
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@brandon_m_behring/book-scaffold-astro",
3
3
  "description": "Astro 6 + MDX toolkit for long-form technical books. Profile-aware (academic / tools / minimal); ships Tufte typography, KaTeX, BibTeX citations, Pagefind, Cloudflare Workers deploy. See PACKAGE_DESIGN.md for the API contract.",
4
- "version": "4.0.0",
4
+ "version": "4.1.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Brandon Behring",
@@ -65,6 +65,8 @@
65
65
  "./components/OpenQuestion.astro": "./components/OpenQuestion.astro",
66
66
  "./components/PaperBox.astro": "./components/PaperBox.astro",
67
67
  "./components/PatternTimeline.astro": "./components/PatternTimeline.astro",
68
+ "./components/Pitfall.astro": "./components/Pitfall.astro",
69
+ "./components/PocLayout.astro": "./components/PocLayout.astro",
68
70
  "./components/PolicyRef.astro": "./components/PolicyRef.astro",
69
71
  "./components/PreReleaseBanner.astro": "./components/PreReleaseBanner.astro",
70
72
  "./components/Recovery.astro": "./components/Recovery.astro",
@@ -88,7 +90,9 @@
88
90
  },
89
91
  "./components/WarnBox.astro": "./components/WarnBox.astro",
90
92
  "./components/WeekRef.astro": "./components/WeekRef.astro",
93
+ "./components/WorkedExample.astro": "./components/WorkedExample.astro",
91
94
  "./components/XRef.astro": "./components/XRef.astro",
95
+ "./components/YouWillLearn.astro": "./components/YouWillLearn.astro",
92
96
  "./styles/tokens.css": "./styles/tokens.css",
93
97
  "./styles/layout.css": "./styles/layout.css",
94
98
  "./styles/callouts.css": "./styles/callouts.css",
@@ -96,6 +100,7 @@
96
100
  "./styles/typography.css": "./styles/typography.css",
97
101
  "./styles/print.css": "./styles/print.css",
98
102
  "./styles/convergence.css": "./styles/convergence.css",
103
+ "./styles/poc-layouts.css": "./styles/poc-layouts.css",
99
104
  "./styles/tool-filter.css": "./styles/tool-filter.css",
100
105
  "./layouts/Base.astro": "./layouts/Base.astro",
101
106
  "./layouts/Chapter.astro": "./layouts/Chapter.astro",
@@ -162,6 +162,54 @@
162
162
  background: var(--color-bg-subtle);
163
163
  }
164
164
 
165
+ /* Crimson — Pitfall (v4.1.0, #58). Distinct from warn-rose: deeper, more
166
+ * saturated. Used for "common mistake" / retrospective error patterns. */
167
+ .callout-pitfall {
168
+ border-left-color: var(--callout-pitfall);
169
+ background: var(--warm-crimson-tint);
170
+ }
171
+
172
+ /* Plum-dashed — WorkedExample (v4.1.0, #57). Reuses callout-official
173
+ * (plum) bar; <details>/<summary> chrome is plain — no extra structure. */
174
+ .callout-worked {
175
+ border-left-color: var(--callout-worked);
176
+ background: var(--warm-plum-tint);
177
+ }
178
+ .callout-worked summary {
179
+ cursor: pointer;
180
+ list-style: none;
181
+ display: flex;
182
+ align-items: baseline;
183
+ gap: var(--space-3);
184
+ }
185
+ .callout-worked summary::-webkit-details-marker {
186
+ display: none;
187
+ }
188
+ .callout-worked .callout-chip {
189
+ font-size: var(--text-xs);
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.05em;
192
+ padding: 0 var(--space-2);
193
+ border: 1px solid var(--callout-worked);
194
+ border-radius: var(--radius-sm);
195
+ color: var(--callout-worked);
196
+ }
197
+
198
+ /* Gold — YouWillLearn (v4.1.0, #59). Reuses callout-insight (gold) hue;
199
+ * "Before you start" prereq sub-block sits above the title in muted style. */
200
+ .callout-learn {
201
+ border-left-color: var(--callout-learn);
202
+ background: var(--warm-gold-tint);
203
+ }
204
+ .callout-learn .callout-prereq {
205
+ font-size: var(--text-sm);
206
+ color: var(--color-text-muted);
207
+ margin-bottom: var(--space-2);
208
+ }
209
+ .callout-learn .callout-prereq-label {
210
+ color: var(--color-text);
211
+ }
212
+
165
213
  /* ===== Theorem family (Theorem.astro) =====
166
214
  * Plain (theorem/proposition/lemma/corollary): italic body
167
215
  * Definition family (definition/example/exercise/remark/proof): upright body
@@ -0,0 +1,57 @@
1
+ /* poc-layouts.css — per-PoC-kind layout variables (v4.1.0, closes #56).
2
+ *
3
+ * Each kind swaps 3 CSS variables that downstream stylesheets (chapter.css,
4
+ * layout.css) can read. Consumers can override per-kind in their own
5
+ * stylesheets via `:where(.poc-layout-X) { --bs-content-line-length: ... }`.
6
+ *
7
+ * Variable surface (intentionally narrow):
8
+ * --bs-content-line-length — main column max line length (ch)
9
+ * --bs-content-vertical-rhythm — body line-height multiplier
10
+ * --bs-heading-emphasis — heading font-weight (400-900)
11
+ */
12
+
13
+ :where(.poc-layout-tutorial) {
14
+ --bs-content-line-length: 70ch;
15
+ --bs-content-vertical-rhythm: 1.6;
16
+ --bs-heading-emphasis: 600;
17
+ }
18
+
19
+ :where(.poc-layout-how-to) {
20
+ --bs-content-line-length: 68ch;
21
+ --bs-content-vertical-rhythm: 1.5;
22
+ --bs-heading-emphasis: 700;
23
+ }
24
+
25
+ :where(.poc-layout-tldr) {
26
+ --bs-content-line-length: 65ch;
27
+ --bs-content-vertical-rhythm: 1.4;
28
+ --bs-heading-emphasis: 500;
29
+ }
30
+
31
+ :where(.poc-layout-part-summary) {
32
+ --bs-content-line-length: 90ch;
33
+ --bs-content-vertical-rhythm: 1.5;
34
+ --bs-heading-emphasis: 600;
35
+ }
36
+
37
+ :where(.poc-layout-cheat-sheet) {
38
+ --bs-content-line-length: 55ch;
39
+ --bs-content-vertical-rhythm: 1.3;
40
+ --bs-heading-emphasis: 600;
41
+ }
42
+
43
+ /* All kinds: apply the variables to the wrapper itself.
44
+ * Authors can read these vars from descendants for tighter control. */
45
+ .poc-layout {
46
+ max-width: var(--bs-content-line-length, 70ch);
47
+ line-height: var(--bs-content-vertical-rhythm, 1.6);
48
+ }
49
+
50
+ .poc-layout :is(h1, h2, h3, h4, h5, h6) {
51
+ font-weight: var(--bs-heading-emphasis, 600);
52
+ }
53
+
54
+ /* Cheat-sheet: tables should fill the available width since the column is narrower. */
55
+ .poc-layout-cheat-sheet table {
56
+ width: 100%;
57
+ }
package/styles/tokens.css CHANGED
@@ -14,6 +14,7 @@
14
14
  --warm-green: #4A7E3F; /* positive: tips, practitioner */
15
15
  --warm-plum: #8A4E82; /* authority: official, exercises */
16
16
  --warm-gold: #C09840; /* insight: convergence, reasoning */
17
+ --warm-crimson:#A03838; /* pitfall: deeper red distinct from warm-rose */
17
18
 
18
19
  /* Warm neutrals (Anthropic-derived) */
19
20
  --paper: #FDFCF9; /* page background — slightly off-white */
@@ -27,6 +28,7 @@
27
28
  --warm-green-tint: color-mix(in srgb, var(--warm-green) 6%, var(--paper));
28
29
  --warm-plum-tint: color-mix(in srgb, var(--warm-plum) 6%, var(--paper));
29
30
  --warm-gold-tint: color-mix(in srgb, var(--warm-gold) 6%, var(--paper));
31
+ --warm-crimson-tint:color-mix(in srgb, var(--warm-crimson) 6%, var(--paper));
30
32
 
31
33
  /* ===== Layer 2: Semantic roles (light mode) ===== */
32
34
  --color-bg: var(--paper);
@@ -45,6 +47,9 @@
45
47
  --callout-tip: var(--warm-green);
46
48
  --callout-official: var(--warm-plum);
47
49
  --callout-insight: var(--warm-gold);
50
+ --callout-pitfall: var(--warm-crimson);
51
+ --callout-worked: var(--warm-plum);
52
+ --callout-learn: var(--warm-gold);
48
53
 
49
54
  /* ===== Typography scale ===== */
50
55
  --font-body: 'Roboto Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;