@strategicnerds/slide-nerds 0.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.
Files changed (101) hide show
  1. package/dist/cli/commands/analytics.d.ts +6 -0
  2. package/dist/cli/commands/analytics.d.ts.map +1 -0
  3. package/dist/cli/commands/analytics.js +44 -0
  4. package/dist/cli/commands/analytics.js.map +1 -0
  5. package/dist/cli/commands/create.d.ts +4 -0
  6. package/dist/cli/commands/create.d.ts.map +1 -0
  7. package/dist/cli/commands/create.js +87 -0
  8. package/dist/cli/commands/create.js.map +1 -0
  9. package/dist/cli/commands/export.d.ts +6 -0
  10. package/dist/cli/commands/export.d.ts.map +1 -0
  11. package/dist/cli/commands/export.js +109 -0
  12. package/dist/cli/commands/export.js.map +1 -0
  13. package/dist/cli/index.d.ts +3 -0
  14. package/dist/cli/index.d.ts.map +1 -0
  15. package/dist/cli/index.js +12 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/template-path.d.ts +4 -0
  18. package/dist/cli/template-path.d.ts.map +1 -0
  19. package/dist/cli/template-path.js +13 -0
  20. package/dist/cli/template-path.js.map +1 -0
  21. package/dist/runtime/export-api.d.ts +15 -0
  22. package/dist/runtime/export-api.d.ts.map +1 -0
  23. package/dist/runtime/export-api.js +21 -0
  24. package/dist/runtime/export-api.js.map +1 -0
  25. package/dist/runtime/index.d.ts +12 -0
  26. package/dist/runtime/index.d.ts.map +1 -0
  27. package/dist/runtime/index.js +8 -0
  28. package/dist/runtime/index.js.map +1 -0
  29. package/dist/runtime/light-table.d.ts +8 -0
  30. package/dist/runtime/light-table.d.ts.map +1 -0
  31. package/dist/runtime/light-table.js +104 -0
  32. package/dist/runtime/light-table.js.map +1 -0
  33. package/dist/runtime/presenter-view.d.ts +7 -0
  34. package/dist/runtime/presenter-view.d.ts.map +1 -0
  35. package/dist/runtime/presenter-view.js +62 -0
  36. package/dist/runtime/presenter-view.js.map +1 -0
  37. package/dist/runtime/slide-context.d.ts +16 -0
  38. package/dist/runtime/slide-context.d.ts.map +1 -0
  39. package/dist/runtime/slide-context.js +18 -0
  40. package/dist/runtime/slide-context.js.map +1 -0
  41. package/dist/runtime/slide-controls.d.ts +3 -0
  42. package/dist/runtime/slide-controls.d.ts.map +1 -0
  43. package/dist/runtime/slide-controls.js +177 -0
  44. package/dist/runtime/slide-controls.js.map +1 -0
  45. package/dist/runtime/slide-dom.d.ts +17 -0
  46. package/dist/runtime/slide-dom.d.ts.map +1 -0
  47. package/dist/runtime/slide-dom.js +89 -0
  48. package/dist/runtime/slide-dom.js.map +1 -0
  49. package/dist/runtime/slide-runtime.d.ts +7 -0
  50. package/dist/runtime/slide-runtime.d.ts.map +1 -0
  51. package/dist/runtime/slide-runtime.js +125 -0
  52. package/dist/runtime/slide-runtime.js.map +1 -0
  53. package/dist/runtime/slide-shape.d.ts +21 -0
  54. package/dist/runtime/slide-shape.d.ts.map +1 -0
  55. package/dist/runtime/slide-shape.js +115 -0
  56. package/dist/runtime/slide-shape.js.map +1 -0
  57. package/dist/runtime/types.d.ts +150 -0
  58. package/dist/runtime/types.d.ts.map +1 -0
  59. package/dist/runtime/types.js +38 -0
  60. package/dist/runtime/types.js.map +1 -0
  61. package/dist/runtime/use-presenter-mode.d.ts +14 -0
  62. package/dist/runtime/use-presenter-mode.d.ts.map +1 -0
  63. package/dist/runtime/use-presenter-mode.js +52 -0
  64. package/dist/runtime/use-presenter-mode.js.map +1 -0
  65. package/dist/runtime/use-slide-navigation.d.ts +13 -0
  66. package/dist/runtime/use-slide-navigation.d.ts.map +1 -0
  67. package/dist/runtime/use-slide-navigation.js +230 -0
  68. package/dist/runtime/use-slide-navigation.js.map +1 -0
  69. package/package.json +64 -0
  70. package/skills/accessibility/SKILL.md +236 -0
  71. package/skills/advanced-layouts/SKILL.md +429 -0
  72. package/skills/analytics/SKILL.md +97 -0
  73. package/skills/animation/SKILL.md +364 -0
  74. package/skills/brand/SKILL.md +200 -0
  75. package/skills/data-visualization/SKILL.md +533 -0
  76. package/skills/deck-templates/SKILL.md +93 -0
  77. package/skills/diagrams/SKILL.md +395 -0
  78. package/skills/export/SKILL.md +119 -0
  79. package/skills/interactive/SKILL.md +292 -0
  80. package/skills/layout/SKILL.md +178 -0
  81. package/skills/narrative-frameworks/SKILL.md +250 -0
  82. package/skills/react-component-embeds/SKILL.md +73 -0
  83. package/skills/slide-types/SKILL.md +384 -0
  84. package/skills/slidenerds-runtime/SKILL.md +163 -0
  85. package/skills/speaker-notes/SKILL.md +128 -0
  86. package/skills/strategic-frameworks/SKILL.md +392 -0
  87. package/skills/visual-design/SKILL.md +373 -0
  88. package/templates/analytics/custom.tsx.tmpl +20 -0
  89. package/templates/analytics/ga4.tsx.tmpl +15 -0
  90. package/templates/analytics/gtm.tsx.tmpl +9 -0
  91. package/templates/analytics/plausible.tsx.tmpl +10 -0
  92. package/templates/analytics/posthog.tsx.tmpl +14 -0
  93. package/templates/next-app/CLAUDE.md.tmpl +574 -0
  94. package/templates/next-app/README.md.tmpl +35 -0
  95. package/templates/next-app/app/globals.css.tmpl +274 -0
  96. package/templates/next-app/app/layout.tsx.tmpl +31 -0
  97. package/templates/next-app/app/page.tsx.tmpl +38 -0
  98. package/templates/next-app/brand.config.ts.tmpl +32 -0
  99. package/templates/next-app/package.json.tmpl +25 -0
  100. package/templates/next-app/postcss.config.mjs.tmpl +8 -0
  101. package/templates/next-app/tsconfig.json.tmpl +21 -0
@@ -0,0 +1,292 @@
1
+ ---
2
+ name: interactive
3
+ description: Embedded video, QR codes, clickable links, live data, and audience interaction patterns for slidenerds slides
4
+ ---
5
+
6
+ # Interactive elements skill
7
+
8
+ Use this skill when slides need dynamic or interactive content beyond static text and charts. Each pattern here produces a functional component that works within the slidenerds runtime.
9
+
10
+ ## Embedded video
11
+
12
+ Use for: product demos, customer testimonials, recorded walkthroughs.
13
+
14
+ ### YouTube / Vimeo embed
15
+
16
+ ```tsx
17
+ <section data-slide="">
18
+ <div className="flex flex-col justify-center items-center w-full min-h-screen"
19
+ style={{ padding: '4rem 6rem' }}>
20
+ <p className="section-label mb-3">Demo</p>
21
+ <h2 className="text-[2.5rem] font-bold mb-10">See it in action</h2>
22
+ <div data-step="" className="step-fade w-full" style={{ maxWidth: 900 }}>
23
+ <div className="card-surface overflow-hidden" style={{ aspectRatio: '16/9' }}>
24
+ <iframe
25
+ src="https://www.youtube.com/embed/VIDEO_ID?rel=0&modestbranding=1"
26
+ title="Product demo"
27
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
28
+ allowFullScreen
29
+ style={{ width: '100%', height: '100%', border: 'none' }}
30
+ />
31
+ </div>
32
+ </div>
33
+ <div data-notes="">
34
+ Play the video. Pause at 1:45 to highlight the auto-scaling feature.
35
+ </div>
36
+ </div>
37
+ </section>
38
+ ```
39
+
40
+ ### Self-hosted video
41
+
42
+ ```tsx
43
+ <div className="card-surface overflow-hidden" style={{ aspectRatio: '16/9' }}>
44
+ <video
45
+ src="/demo.mp4"
46
+ controls
47
+ playsInline
48
+ style={{ width: '100%', height: '100%', objectFit: 'cover' }}
49
+ />
50
+ </div>
51
+ ```
52
+
53
+ ### Video rules
54
+
55
+ - Always wrap in a card surface with 16:9 aspect ratio.
56
+ - Use `rel=0&modestbranding=1` for YouTube to hide related videos and branding.
57
+ - Never autoplay. The presenter controls playback.
58
+ - Keep videos under 2 minutes for live presentations.
59
+ - Include speaker notes that describe what to narrate during the video.
60
+
61
+ ## QR code
62
+
63
+ Use for: audience participation, resource links, app downloads, feedback forms.
64
+
65
+ Install `qrcode.react`:
66
+
67
+ ```bash
68
+ npm install qrcode.react
69
+ ```
70
+
71
+ ```tsx
72
+ import { QRCodeSVG } from 'qrcode.react'
73
+
74
+ <section data-slide="">
75
+ <div className="flex items-center justify-center w-full min-h-screen"
76
+ style={{ padding: '4rem 6rem' }}>
77
+ <div className="flex items-center gap-16">
78
+ <div>
79
+ <p className="section-label mb-3">Try it now</p>
80
+ <h2 className="text-[2.5rem] font-bold mb-4">Scan to get started</h2>
81
+ <p className="text-lg" style={{ color: 'var(--color-text-secondary)' }}>
82
+ Free trial, no credit card required.
83
+ </p>
84
+ <p className="mt-6 text-sm font-mono" style={{ color: 'var(--color-text-tertiary)' }}>
85
+ acme.dev/start
86
+ </p>
87
+ </div>
88
+ <div className="card-surface p-6">
89
+ <QRCodeSVG
90
+ value="https://acme.dev/start"
91
+ size={200}
92
+ bgColor="transparent"
93
+ fgColor="var(--color-text)"
94
+ level="M"
95
+ />
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </section>
100
+ ```
101
+
102
+ ### QR code rules
103
+
104
+ - Size: at least 200x200px on screen. Must be scannable from 3 meters.
105
+ - Always include the URL in plain text below or beside the QR code.
106
+ - Use `level="M"` (medium error correction) for reliability.
107
+ - Place the QR code on the right side of the slide (audience's eye tracks left-to-right, reads context first, then scans).
108
+ - Use `bgColor="transparent"` to match the slide background.
109
+
110
+ ## Clickable links
111
+
112
+ Use for: resources, documentation, signup pages within the deck.
113
+
114
+ ```tsx
115
+ <a
116
+ href="https://docs.acme.dev"
117
+ target="_blank"
118
+ rel="noopener noreferrer"
119
+ className="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium"
120
+ style={{
121
+ background: 'var(--color-accent-dim)',
122
+ color: 'var(--color-accent)',
123
+ textDecoration: 'none',
124
+ transition: 'background 150ms ease',
125
+ }}
126
+ >
127
+ View documentation
128
+ <svg width={14} height={14} viewBox="0 0 24 24" fill="none"
129
+ stroke="currentColor" strokeWidth={2} strokeLinecap="round">
130
+ <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" />
131
+ <polyline points="15 3 21 3 21 9" />
132
+ <line x1="10" y1="14" x2="21" y2="3" />
133
+ </svg>
134
+ </a>
135
+ ```
136
+
137
+ ### Link rules
138
+
139
+ - Style links as pill buttons, not underlined text. Underlines are hard to see at projection distance.
140
+ - Always use `target="_blank"` and `rel="noopener noreferrer"`.
141
+ - Include an external-link icon to signal the link opens a new tab.
142
+ - Place links at the bottom of slides, not inline with body text.
143
+ - Limit to 1-2 links per slide.
144
+
145
+ ## Embedded web content
146
+
147
+ Use for: live dashboards, interactive prototypes, web apps.
148
+
149
+ ```tsx
150
+ <section data-slide="">
151
+ <div className="flex flex-col justify-center w-full min-h-screen"
152
+ style={{ padding: '4rem 6rem' }}>
153
+ <p className="section-label mb-3">Live</p>
154
+ <h2 className="text-[2.5rem] font-bold mb-8">Real-time dashboard</h2>
155
+ <div className="card-surface overflow-hidden flex-1" style={{ minHeight: 400 }}>
156
+ <iframe
157
+ src="https://dashboard.acme.dev/embed?token=xyz"
158
+ title="Live metrics dashboard"
159
+ sandbox="allow-scripts allow-same-origin"
160
+ style={{ width: '100%', height: '100%', border: 'none' }}
161
+ />
162
+ </div>
163
+ <div data-notes="">
164
+ This is a live embed. Refresh if data looks stale. Have a screenshot backup ready.
165
+ </div>
166
+ </div>
167
+ </section>
168
+ ```
169
+
170
+ ### Embed rules
171
+
172
+ - Always use the `sandbox` attribute to restrict iframe capabilities.
173
+ - Have a static screenshot as a fallback in case of network issues.
174
+ - Test the embed before presenting -- auth tokens, CORS, and CSP can block iframes.
175
+ - Include a note in speaker notes about fallback behavior.
176
+
177
+ ## Presenter timer with alerts
178
+
179
+ The runtime's presenter view shows elapsed time. For more control, add configurable alerts:
180
+
181
+ ```tsx
182
+ const ALERT_TIMES = [
183
+ { seconds: 15 * 60, label: '15 min remaining', color: '#22c55e' },
184
+ { seconds: 18 * 60, label: '2 min remaining', color: '#f59e0b' },
185
+ { seconds: 19 * 60, label: '1 min remaining', color: '#ef4444' },
186
+ ]
187
+ ```
188
+
189
+ Display these as toast notifications in the presenter view when the timer crosses each threshold.
190
+
191
+ ## Click-to-zoom on charts and images
192
+
193
+ For detailed charts that need closer inspection:
194
+
195
+ ```tsx
196
+ const [zoomed, setZoomed] = useState(false)
197
+
198
+ <div onClick={() => setZoomed(!zoomed)}
199
+ style={{
200
+ cursor: 'zoom-in',
201
+ transition: 'transform 300ms ease',
202
+ transform: zoomed ? 'scale(1.5)' : 'scale(1)',
203
+ transformOrigin: 'center',
204
+ zIndex: zoomed ? 100 : 'auto',
205
+ position: 'relative',
206
+ }}>
207
+ <ResponsiveContainer width="100%" height={400}>
208
+ {/* Chart */}
209
+ </ResponsiveContainer>
210
+ </div>
211
+ ```
212
+
213
+ ### Zoom rules
214
+
215
+ - Only use on data-dense charts that benefit from closer inspection.
216
+ - Scale factor: 1.5x (not more -- it pushes content off screen).
217
+ - Click to zoom in, click again to zoom out.
218
+ - Not appropriate for keynote-style presentations. Use only for working sessions and reviews.
219
+
220
+ ## Audience interaction patterns
221
+
222
+ ### Live poll (requires backend)
223
+
224
+ For real-time polling during presentations, integrate with a polling service or build a custom WebSocket solution:
225
+
226
+ ```tsx
227
+ <section data-slide="">
228
+ <div className="flex flex-col items-center justify-center w-full min-h-screen"
229
+ style={{ padding: '4rem 6rem' }}>
230
+ <h2 className="text-[2.5rem] font-bold mb-4">Quick poll</h2>
231
+ <p className="text-lg mb-10" style={{ color: 'var(--color-text-secondary)' }}>
232
+ What is your biggest deployment challenge?
233
+ </p>
234
+ <div className="card-surface p-6 w-full" style={{ maxWidth: 600 }}>
235
+ {/* Poll results render here -- driven by real-time data */}
236
+ <div className="space-y-3">
237
+ {['Speed', 'Reliability', 'Cost', 'Complexity'].map((option) => (
238
+ <div key={option} className="flex items-center gap-3">
239
+ <div className="h-8 rounded"
240
+ style={{ width: `${Math.random() * 80 + 20}%`, background: 'var(--color-accent)', opacity: 0.6 }} />
241
+ <span className="text-sm">{option}</span>
242
+ </div>
243
+ ))}
244
+ </div>
245
+ </div>
246
+ <p className="mt-8 text-sm" style={{ color: 'var(--color-text-tertiary)' }}>
247
+ Vote at acme.dev/poll
248
+ </p>
249
+ </div>
250
+ </section>
251
+ ```
252
+
253
+ ### Feedback / Q&A prompt
254
+
255
+ Simple pattern for soliciting audience questions:
256
+
257
+ ```tsx
258
+ <section data-slide="">
259
+ <div className="flex flex-col items-center justify-center w-full min-h-screen text-center"
260
+ style={{ padding: '4rem 6rem' }}>
261
+ <h2 className="text-[3.5rem] font-bold mb-4">Questions?</h2>
262
+ <p className="text-lg" style={{ color: 'var(--color-text-secondary)' }}>
263
+ Submit at acme.dev/ask or raise your hand.
264
+ </p>
265
+ <div className="mt-10 card-surface p-6">
266
+ <QRCodeSVG value="https://acme.dev/ask" size={160}
267
+ bgColor="transparent" fgColor="var(--color-text)" level="M" />
268
+ </div>
269
+ </div>
270
+ </section>
271
+ ```
272
+
273
+ ## Interactive element selection guide
274
+
275
+ | Need | Element | Dependency |
276
+ |---|---|---|
277
+ | Product walkthrough | YouTube/Vimeo embed | None (iframe) |
278
+ | Recorded demo | Self-hosted video | None (HTML5 video) |
279
+ | Resource link | Pill button with icon | None |
280
+ | Audience participation | QR code | `qrcode.react` |
281
+ | Live metrics | Embedded iframe | Backend/dashboard service |
282
+ | Data inspection | Click-to-zoom chart | None (CSS transform) |
283
+ | Audience voting | Live poll | WebSocket backend |
284
+ | Feedback collection | Q&A QR code slide | `qrcode.react` |
285
+
286
+ ## What to avoid
287
+
288
+ - Autoplay on any media. The presenter controls pacing.
289
+ - More than one interactive element per slide.
290
+ - Relying on network connectivity without a fallback.
291
+ - Interactive prototypes without a pre-recorded backup video.
292
+ - Polls or live data in the first half of a presentation. Build credibility before asking for participation.
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: layout
3
+ description: Alignment, grid, and arrangement patterns for slidenerds slides using Tailwind CSS
4
+ ---
5
+
6
+ # Layout skill
7
+
8
+ Alignment and arrangement primitives for slide content. Use these patterns instead of guessing at Tailwind classes.
9
+
10
+ ## Alignment
11
+
12
+ ### Center content (horizontal and vertical)
13
+
14
+ ```tsx
15
+ <section data-slide="">
16
+ <div className="flex items-center justify-center min-h-screen">
17
+ <div className="text-center">
18
+ <h1 className="text-5xl font-bold">Centered title</h1>
19
+ </div>
20
+ </div>
21
+ </section>
22
+ ```
23
+
24
+ ### Left-align a group of elements
25
+
26
+ ```tsx
27
+ <div className="flex flex-col items-start gap-4" style={{ padding: 'var(--slide-padding)' }}>
28
+ <h2 className="text-3xl font-semibold">Left-aligned heading</h2>
29
+ <p className="text-lg">Body text aligned to the left edge</p>
30
+ <img src="/chart.png" alt="Chart" className="w-full max-w-md" />
31
+ </div>
32
+ ```
33
+
34
+ ### Right-align a group of elements
35
+
36
+ ```tsx
37
+ <div className="flex flex-col items-end gap-4" style={{ padding: 'var(--slide-padding)' }}>
38
+ <p className="text-lg">Right-aligned caption</p>
39
+ <p className="text-sm opacity-70">Source: Company data</p>
40
+ </div>
41
+ ```
42
+
43
+ ### Bottom-align content
44
+
45
+ ```tsx
46
+ <section data-slide="">
47
+ <div className="flex flex-col justify-end min-h-screen" style={{ padding: 'var(--slide-padding)' }}>
48
+ <h2 className="text-3xl font-semibold">Pinned to bottom</h2>
49
+ </div>
50
+ </section>
51
+ ```
52
+
53
+ ## Grid arrangements
54
+
55
+ ### Two-column layout
56
+
57
+ ```tsx
58
+ <section data-slide="">
59
+ <div className="grid grid-cols-2 gap-8 min-h-screen items-center" style={{ padding: 'var(--slide-padding)' }}>
60
+ <div>
61
+ <h2 className="text-3xl font-semibold">Left column</h2>
62
+ <p className="mt-4 text-lg">Explanation text</p>
63
+ </div>
64
+ <div>
65
+ <img src="/visual.png" alt="Visual" className="w-full rounded-lg" />
66
+ </div>
67
+ </div>
68
+ </section>
69
+ ```
70
+
71
+ ### Three-column layout
72
+
73
+ ```tsx
74
+ <div className="grid grid-cols-3 gap-6" style={{ padding: 'var(--slide-padding)' }}>
75
+ <div className="text-center">
76
+ <p className="text-4xl font-bold">$4.2M</p>
77
+ <p className="text-sm opacity-70 mt-2">ARR</p>
78
+ </div>
79
+ <div className="text-center">
80
+ <p className="text-4xl font-bold">142%</p>
81
+ <p className="text-sm opacity-70 mt-2">Growth</p>
82
+ </div>
83
+ <div className="text-center">
84
+ <p className="text-4xl font-bold">98%</p>
85
+ <p className="text-sm opacity-70 mt-2">Retention</p>
86
+ </div>
87
+ </div>
88
+ ```
89
+
90
+ ### Four-column layout
91
+
92
+ ```tsx
93
+ <div className="grid grid-cols-4 gap-4" style={{ padding: 'var(--slide-padding)' }}>
94
+ {/* Four equal columns */}
95
+ </div>
96
+ ```
97
+
98
+ ## Distribute elements evenly
99
+
100
+ ### Horizontal distribution
101
+
102
+ ```tsx
103
+ <div className="flex justify-between items-center" style={{ padding: 'var(--slide-padding)' }}>
104
+ <div>Item 1</div>
105
+ <div>Item 2</div>
106
+ <div>Item 3</div>
107
+ </div>
108
+ ```
109
+
110
+ ### Vertical distribution
111
+
112
+ ```tsx
113
+ <div className="flex flex-col justify-between min-h-screen" style={{ padding: 'var(--slide-padding)' }}>
114
+ <div>Top</div>
115
+ <div>Middle</div>
116
+ <div>Bottom</div>
117
+ </div>
118
+ ```
119
+
120
+ ## Hero + body layout
121
+
122
+ Large heading at top, body content below:
123
+
124
+ ```tsx
125
+ <section data-slide="">
126
+ <div className="flex flex-col min-h-screen" style={{ padding: 'var(--slide-padding)' }}>
127
+ <h1 className="text-6xl font-bold">Hero headline</h1>
128
+ <div className="mt-auto">
129
+ <p className="text-xl">Supporting body content goes here</p>
130
+ </div>
131
+ </div>
132
+ </section>
133
+ ```
134
+
135
+ ## Full-bleed vs. contained
136
+
137
+ ### Full-bleed (edge to edge)
138
+
139
+ ```tsx
140
+ <section data-slide="">
141
+ <div className="relative min-h-screen">
142
+ <img src="/bg.jpg" alt="" className="absolute inset-0 w-full h-full object-cover" />
143
+ <div className="relative z-10 flex items-center justify-center min-h-screen">
144
+ <h1 className="text-5xl font-bold text-white">Full-bleed image</h1>
145
+ </div>
146
+ </div>
147
+ </section>
148
+ ```
149
+
150
+ ### Contained (with slide padding)
151
+
152
+ ```tsx
153
+ <section data-slide="">
154
+ <div style={{ padding: 'var(--slide-padding)' }}>
155
+ <h2 className="text-3xl font-semibold">Contained content</h2>
156
+ </div>
157
+ </section>
158
+ ```
159
+
160
+ ## Spacing tokens
161
+
162
+ Always use the spacing tokens from `brand.config.ts` via CSS custom properties:
163
+
164
+ - `var(--slide-padding)` for the outer slide padding (default: 4rem)
165
+ - Tailwind gap utilities (`gap-4`, `gap-8`) for element spacing within a slide
166
+ - Never hardcode pixel or rem values for slide-level spacing
167
+
168
+ ## Aspect ratio preservation
169
+
170
+ For media elements that need to maintain aspect ratio:
171
+
172
+ ```tsx
173
+ <div className="aspect-video w-full">
174
+ <img src="/screenshot.png" alt="Screenshot" className="w-full h-full object-contain" />
175
+ </div>
176
+ ```
177
+
178
+ Common aspect ratios: `aspect-video` (16:9), `aspect-square` (1:1), `aspect-[4/3]` (4:3).