@deckio/deck-engine 1.7.8 → 1.8.1

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.
@@ -1,15 +1,43 @@
1
+ import { useRef, useEffect, useState } from 'react'
1
2
  import { useSlides } from '../context/SlideContext'
2
3
 
4
+ const DEV = typeof import.meta !== 'undefined' && import.meta.env?.DEV
5
+
3
6
  export default function Slide({ index, className = '', children }) {
4
7
  const { current } = useSlides()
8
+ const ref = useRef(null)
9
+ const [overflow, setOverflow] = useState(false)
5
10
 
6
11
  let stateClass = ''
7
12
  if (index === current) stateClass = 'active'
8
13
  else if (index < current) stateClass = 'exit-left'
9
14
 
15
+ useEffect(() => {
16
+ if (!DEV || index !== current || !ref.current) return
17
+ const el = ref.current
18
+ const check = () => {
19
+ // Only check flow-positioned children; ignore absolute/fixed decorations (orbs, accent-bar)
20
+ const hasOverflow = Array.from(el.children).some(c => {
21
+ const pos = getComputedStyle(c).position
22
+ if (pos === 'absolute' || pos === 'fixed') return false
23
+ return c.offsetTop + c.offsetHeight > el.clientHeight
24
+ })
25
+ setOverflow(hasOverflow)
26
+ }
27
+ check()
28
+ const obs = new ResizeObserver(check)
29
+ obs.observe(el)
30
+ return () => obs.disconnect()
31
+ }, [index, current])
32
+
10
33
  return (
11
- <div className={`slide ${stateClass} ${className}`} data-slide={index}>
34
+ <div ref={ref} className={`slide ${stateClass} ${className}`} data-slide={index}>
12
35
  {children}
36
+ {DEV && overflow && (
37
+ <div className="slide-overflow-warn">
38
+ ⚠ Content overflows slide — reduce content or use smaller elements
39
+ </div>
40
+ )}
13
41
  </div>
14
42
  )
15
43
  }
@@ -79,6 +79,14 @@ export async function exportDeckPdf({
79
79
 
80
80
  if (document.fonts?.ready) await document.fonts.ready
81
81
 
82
+ // Force deck to canonical PDF dimensions so slides render at exactly
83
+ // PAGE_W × PAGE_H regardless of the current viewport size.
84
+ const origDeckCss = deck.style.cssText
85
+ deck.style.width = `${PAGE_W}px`
86
+ deck.style.height = `${PAGE_H}px`
87
+ await waitForPaint()
88
+ await wait(SETTLE_MS)
89
+
82
90
  try {
83
91
  for (let i = 0; i < totalSlides; i++) {
84
92
  onProgress?.({ current: i + 1, total: totalSlides })
@@ -95,8 +103,8 @@ export async function exportDeckPdf({
95
103
  let dataUrl
96
104
  try {
97
105
  dataUrl = await domToPng(active, {
98
- width: active.clientWidth || PAGE_W,
99
- height: active.clientHeight || PAGE_H,
106
+ width: PAGE_W,
107
+ height: PAGE_H,
100
108
  backgroundColor: bg,
101
109
  scale,
102
110
  style: {
@@ -114,6 +122,7 @@ export async function exportDeckPdf({
114
122
  pdf.addImage(dataUrl, 'PNG', 0, 0, PAGE_W, PAGE_H, undefined, 'FAST')
115
123
  }
116
124
  } finally {
125
+ deck.style.cssText = origDeckCss
117
126
  goTo(current)
118
127
  await waitForPaint()
119
128
  }
@@ -10,12 +10,13 @@ applyTo: "**/slides/**/*.module.css"
10
10
  ```css
11
11
  .mySlide {
12
12
  background: var(--bg-deep);
13
- flex-direction: column;
14
13
  padding: 0 0 44px 0; /* reserve BottomBar height */
15
14
  }
16
15
  ```
17
16
 
18
- Add `justify-content: center` for cover or thank-you slides.
17
+ The engine's `.slide` class already sets `flex-direction: column`, `justify-content: center`, and `overflow: hidden`. The engine also sets `flex-grow: 0` on all direct slide children, so **content stays at its natural height and is vertically centered by default** — building from the center outward. No scrolling is allowed.
18
+
19
+ For dense slides that need top-alignment, override with `justify-content: flex-start`.
19
20
 
20
21
  ## Orb positioning recipe
21
22
 
@@ -40,12 +41,12 @@ Add `justify-content: center` for cover or thank-you slides.
40
41
  z-index: 10;
41
42
  display: flex;
42
43
  flex-direction: column;
43
- justify-content: center;
44
- flex: 1;
45
- min-height: 0;
44
+ gap: 24px;
46
45
  }
47
46
  ```
48
47
 
48
+ > **Do NOT add `flex: 1` or `flex-grow: 1`** to the body wrapper or any direct slide child — it stretches the wrapper to fill the slide and defeats the engine's built-in vertical centering. The engine sets `flex-grow: 0` on all direct slide children to ensure content builds from the center outward. Inner elements within the body wrapper should also avoid `flex: 1` unless they genuinely need to fill remaining space within the body.
49
+
49
50
  ## Theme variables (always use these, never hard-code colors)
50
51
 
51
52
  | Variable | Value |
@@ -89,3 +90,7 @@ Add `justify-content: center` for cover or thank-you slides.
89
90
  | Subtitle | `17px` | 300–400 | — |
90
91
  | Body | `13px–14px` | 400 | — |
91
92
  | Badge | `10px–11px` | 600–700 | `1.5px` |
93
+
94
+ ## Content density limits
95
+
96
+ Slides must never overflow the viewport. The engine shows a **red dashed border warning** in dev mode when content exceeds the slide bounds. When content doesn't fit, split across multiple slides rather than cramming. A presentation with more slides is better than one with clipped content.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deckio/deck-engine",
3
- "version": "1.7.8",
3
+ "version": "1.8.1",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",
@@ -59,16 +59,16 @@ Create a companion `.module.css` file matching the JSX filename (e.g., `MyNewSli
59
59
  ```css
60
60
  .myNewSlide {
61
61
  background: var(--bg-deep);
62
- flex-direction: column;
63
62
  padding: 0 0 44px 0;
64
63
  }
65
64
  ```
66
65
 
67
66
  - `background: var(--bg-deep)` — dark background on every slide
68
- - `flex-direction: column` — global `.slide` sets `display: flex`; this orients content vertically
69
67
  - `padding: 0 0 44px 0` — reserves space for the 44px BottomBar
70
68
 
71
- Optional: add `justify-content: center` to vertically center content (cover slides, thank-you slides).
69
+ The engine's `.slide` class provides `flex-direction: column`, `justify-content: center`, `align-items: stretch`, and `overflow: hidden` by default. It also sets `flex-grow: 0` on all direct slide children, so **content stays at its natural height and is vertically centered by default** — building from the center outward. No scrolling is allowed.
70
+
71
+ For dense slides that need top-alignment, override with `justify-content: flex-start`.
72
72
 
73
73
  ### Orb positioning (standard recipe)
74
74
 
@@ -85,7 +85,7 @@ Optional: add `justify-content: center` to vertically center content (cover slid
85
85
  }
86
86
  ```
87
87
 
88
- ### Vertical centering body wrapper
88
+ ### Body wrapper
89
89
 
90
90
  ```css
91
91
  .body {
@@ -93,12 +93,12 @@ Optional: add `justify-content: center` to vertically center content (cover slid
93
93
  z-index: 10;
94
94
  display: flex;
95
95
  flex-direction: column;
96
- justify-content: center;
97
- flex: 1;
98
- min-height: 0;
96
+ gap: 24px;
99
97
  }
100
98
  ```
101
99
 
100
+ > **Do NOT add `flex: 1` or `flex-grow: 1`** to the body wrapper or any direct slide child — it stretches the wrapper to fill the slide and defeats the engine's built-in vertical centering. Inner elements within the body should also avoid `flex: 1` unless they genuinely need to fill remaining space within the body.
101
+
102
102
  ### Available CSS custom properties
103
103
 
104
104
  ```
@@ -190,7 +190,23 @@ export default {
190
190
 
191
191
  ---
192
192
 
193
- ## F. Anti-Patterns to Avoid
193
+ ## F. Content Density Limits
194
+
195
+ Slides must never overflow the viewport. The engine shows a **red dashed border warning** in dev mode when content exceeds the slide bounds. Follow these limits:
196
+
197
+ | Layout | Max items | Notes |
198
+ |--------|-----------|-------|
199
+ | Cards (3-col grid) | 6 (2 rows) | Reduce card padding if tight |
200
+ | Cards (2-col grid) | 4 (2 rows) | Preferred for detailed cards |
201
+ | Timeline / event list | 3–4 items | Use compact card height for 4 |
202
+ | Bullet points | 6–8 | Depends on line length |
203
+ | Full-width content blocks | 2–3 | E.g. quote + detail section |
204
+
205
+ **When content exceeds limits**, split across multiple slides rather than cramming.
206
+
207
+ ---
208
+
209
+ ## G. Anti-Patterns to Avoid
194
210
 
195
211
  1. **Missing `accent-bar`** — include on every slide.
196
212
  2. **Missing `content-frame content-gutter`** — content will be full-width without standard margins.
@@ -198,10 +214,13 @@ export default {
198
214
  4. **String paths for images** — always use `import logo from '../data/...'` (Vite resolves to URL).
199
215
  5. **Missing `padding: 0 0 44px 0`** on the slide root CSS class — content will overlap the BottomBar.
200
216
  6. **Inconsistent `BottomBar text`** — check existing slides and match their footer text.
217
+ 7. **Using `flex: 1` on body wrapper** — defeats vertical centering; the body should size to its content.
218
+ 8. **Adding `flex-direction: column` on slide root** — already provided by the engine's `.slide` class.
219
+ 9. **Overloading a slide** — if the dev server shows a red dashed border, the slide has too much content. Split into multiple slides.
201
220
 
202
221
  ---
203
222
 
204
- ## G. Complete Step-by-Step
223
+ ## H. Complete Step-by-Step
205
224
 
206
225
  1. **Create** `src/slides/<SlideName>Slide.jsx` following the mandatory skeleton (section A).
207
226
  2. **Create** `src/slides/<SlideName>Slide.module.css` with required root properties (section B).
@@ -211,7 +230,7 @@ export default {
211
230
  ### Quick checklist
212
231
 
213
232
  - [ ] Created `<SlideName>Slide.jsx` with Slide, accent-bar, orbs, content-frame, BottomBar
214
- - [ ] Created `<SlideName>Slide.module.css` with `background: var(--bg-deep)`, `flex-direction: column`, `padding: 0 0 44px 0`, body centering wrapper
233
+ - [ ] Created `<SlideName>Slide.module.css` with `background: var(--bg-deep)`, `padding: 0 0 44px 0`, body wrapper (no `flex: 1`)
215
234
  - [ ] Import added to `deck.config.js`
216
235
  - [ ] Component added to `slides` array at correct position
217
236
  - [ ] `BottomBar text` matches project convention
@@ -35,8 +35,9 @@ For each slide `.jsx` file in `src/slides/`, verify:
35
35
 
36
36
  For each `.module.css` file, verify the root class has:
37
37
  - [ ] `background: var(--bg-deep)`
38
- - [ ] `flex-direction: column`
39
38
  - [ ] `padding: 0 0 44px 0`
39
+ - [ ] Does NOT use `flex: 1` on the body wrapper (defeats vertical centering)
40
+ - [ ] Does NOT redundantly set `flex-direction: column` (inherited from engine `.slide` class)
40
41
 
41
42
  ---
42
43
 
@@ -76,5 +77,5 @@ Summarize findings:
76
77
  - [ ] Every `.jsx` slide has a companion `.module.css`
77
78
  - [ ] All slides have accent-bar, content-frame, BottomBar
78
79
  - [ ] BottomBar text is consistent across the project
79
- - [ ] CSS root classes have required properties
80
+ - [ ] CSS root classes have required properties (`background`, `padding`) and no `flex: 1` on body wrapper
80
81
  - [ ] Project metadata (id, title, subtitle, icon, accent) is present
package/styles/global.css CHANGED
@@ -40,6 +40,9 @@ html, body, #root {
40
40
  position: absolute;
41
41
  inset: 0;
42
42
  display: flex;
43
+ flex-direction: column;
44
+ justify-content: center;
45
+ align-items: stretch;
43
46
  opacity: 0;
44
47
  pointer-events: none;
45
48
  transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1),
@@ -47,6 +50,9 @@ html, body, #root {
47
50
  transform: translateX(60px);
48
51
  overflow: hidden;
49
52
  }
53
+ .slide > * {
54
+ flex-grow: 0;
55
+ }
50
56
  .slide.active {
51
57
  opacity: 1;
52
58
  pointer-events: auto;
@@ -57,6 +63,24 @@ html, body, #root {
57
63
  transform: translateX(-60px);
58
64
  }
59
65
 
66
+ /* ── Dev-mode overflow warning ── */
67
+ .slide-overflow-warn {
68
+ position: absolute;
69
+ inset: 0;
70
+ border: 3px dashed #f85149;
71
+ pointer-events: none;
72
+ z-index: 9999;
73
+ display: flex;
74
+ align-items: flex-end;
75
+ justify-content: center;
76
+ padding-bottom: 56px;
77
+ background: rgba(248, 81, 73, 0.04);
78
+ font-size: 13px;
79
+ font-weight: 600;
80
+ color: #f85149;
81
+ letter-spacing: 0.3px;
82
+ }
83
+
60
84
  /* ── Shared Decorations ── */
61
85
  .orb {
62
86
  position: absolute;
@@ -98,11 +122,7 @@ html, body, #root {
98
122
  ══════════════════════════════════════════════════ */
99
123
  .deck-ty {
100
124
  background: var(--bg-deep);
101
- flex-direction: column;
102
- align-items: stretch;
103
- justify-content: center;
104
125
  padding: 0 0 44px 0;
105
- overflow: hidden;
106
126
  }
107
127
 
108
128
  /* Ambient glow orbs */