@honeydeck/honeydeck 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 (144) hide show
  1. package/AGENTS.md +25 -0
  2. package/DEVELOPMENT.md +522 -0
  3. package/LICENSE +21 -0
  4. package/Readme.md +49 -0
  5. package/SPEC.md +88 -0
  6. package/docs/components.md +63 -0
  7. package/docs/configuration.md +91 -0
  8. package/docs/getting-started.md +116 -0
  9. package/docs/kit-authoring.md +207 -0
  10. package/docs/kits.md +387 -0
  11. package/docs/local-development.md +95 -0
  12. package/docs/mermaid.md +198 -0
  13. package/docs/mobile.md +108 -0
  14. package/docs/navigation.md +93 -0
  15. package/docs/next-steps.md +377 -0
  16. package/docs/pdf-export.md +91 -0
  17. package/docs/presenter-mode.md +104 -0
  18. package/docs/slides.md +130 -0
  19. package/docs/slidev-migration.md +42 -0
  20. package/docs/steps-and-reveals.md +171 -0
  21. package/package.json +134 -0
  22. package/skills/SPEC.md +21 -0
  23. package/skills/honeydeck/SKILL.md +65 -0
  24. package/skills/presentation-writing/SKILL.md +75 -0
  25. package/skills/slidev-migration/SKILL.md +153 -0
  26. package/src/SPEC.md +89 -0
  27. package/src/assets.d.ts +30 -0
  28. package/src/cli/SPEC.md +230 -0
  29. package/src/cli/args.ts +3 -0
  30. package/src/cli/banner.ts +9 -0
  31. package/src/cli/bin.js +5 -0
  32. package/src/cli/build.ts +229 -0
  33. package/src/cli/deck-path.ts +32 -0
  34. package/src/cli/dev.ts +263 -0
  35. package/src/cli/index.ts +126 -0
  36. package/src/cli/init.ts +369 -0
  37. package/src/cli/pdf.ts +923 -0
  38. package/src/cli/skill.ts +75 -0
  39. package/src/cli/templates/SPEC.md +70 -0
  40. package/src/cli/templates/deck-mdx.ts +15 -0
  41. package/src/cli/templates/package-json.ts +36 -0
  42. package/src/cli/templates/sparkle-button.ts +15 -0
  43. package/src/cli/templates/starter/components/SparkleButton.tsx +84 -0
  44. package/src/cli/templates/starter/deck.mdx +153 -0
  45. package/src/cli/templates/starter/styles.css +14 -0
  46. package/src/cli/templates/styles-css.ts +14 -0
  47. package/src/defaults.ts +1 -0
  48. package/src/layouts/ColorModeImage.tsx +55 -0
  49. package/src/layouts/SPEC.md +393 -0
  50. package/src/layouts/SlideFrame.tsx +48 -0
  51. package/src/layouts/bee/Blank.tsx +12 -0
  52. package/src/layouts/bee/Cover.tsx +70 -0
  53. package/src/layouts/bee/Default.tsx +42 -0
  54. package/src/layouts/bee/Image/Image.tsx +151 -0
  55. package/src/layouts/bee/Image/placeholder-dark.webp +0 -0
  56. package/src/layouts/bee/Image/placeholder-vertical-dark.webp +0 -0
  57. package/src/layouts/bee/Image/placeholder-vertical.webp +0 -0
  58. package/src/layouts/bee/Image/placeholder.webp +0 -0
  59. package/src/layouts/bee/ImageLeft.tsx +27 -0
  60. package/src/layouts/bee/ImageRight.tsx +27 -0
  61. package/src/layouts/bee/ImageSide.tsx +107 -0
  62. package/src/layouts/bee/Section.tsx +40 -0
  63. package/src/layouts/bee/TwoCol.tsx +108 -0
  64. package/src/layouts/bee/index.ts +40 -0
  65. package/src/layouts/clean/Blank.tsx +12 -0
  66. package/src/layouts/clean/Cover.tsx +58 -0
  67. package/src/layouts/clean/Default.tsx +33 -0
  68. package/src/layouts/clean/Image/Image.tsx +103 -0
  69. package/src/layouts/clean/ImageLeft.tsx +27 -0
  70. package/src/layouts/clean/ImageRight.tsx +27 -0
  71. package/src/layouts/clean/ImageSide.tsx +113 -0
  72. package/src/layouts/clean/Section.tsx +35 -0
  73. package/src/layouts/clean/TwoCol.tsx +63 -0
  74. package/src/layouts/clean/index.ts +40 -0
  75. package/src/layouts/index.ts +60 -0
  76. package/src/layouts/placeholders.ts +9 -0
  77. package/src/layouts/utils.ts +13 -0
  78. package/src/remark/SPEC.md +49 -0
  79. package/src/remark/h1-extract.ts +124 -0
  80. package/src/remark/index.ts +4 -0
  81. package/src/remark/shiki-code-blocks.ts +325 -0
  82. package/src/remark/step-numbering.ts +412 -0
  83. package/src/runtime/Deck.tsx +533 -0
  84. package/src/runtime/SPEC.md +256 -0
  85. package/src/runtime/SlideCanvas.tsx +95 -0
  86. package/src/runtime/TimelineContext.tsx +122 -0
  87. package/src/runtime/app-shell/index.html +31 -0
  88. package/src/runtime/app-shell/main.tsx +42 -0
  89. package/src/runtime/aspectRatio.ts +34 -0
  90. package/src/runtime/colorMode.ts +23 -0
  91. package/src/runtime/components/BrowserFrame.tsx +233 -0
  92. package/src/runtime/components/Button.tsx +57 -0
  93. package/src/runtime/components/CodeBlock.tsx +210 -0
  94. package/src/runtime/components/ColorModeCycleButton.tsx +59 -0
  95. package/src/runtime/components/ErrorBoundary.tsx +125 -0
  96. package/src/runtime/components/Keyboard.tsx +87 -0
  97. package/src/runtime/components/ListStyle.tsx +203 -0
  98. package/src/runtime/components/NavBar.tsx +223 -0
  99. package/src/runtime/components/NavBarButton.tsx +47 -0
  100. package/src/runtime/components/NavBarDivider.tsx +3 -0
  101. package/src/runtime/components/Notes.tsx +171 -0
  102. package/src/runtime/components/Reveal.tsx +82 -0
  103. package/src/runtime/components/RevealGroup.tsx +193 -0
  104. package/src/runtime/components/SPEC.md +263 -0
  105. package/src/runtime/components/SlideNumberBadge.tsx +11 -0
  106. package/src/runtime/components/TimelineSteps.tsx +115 -0
  107. package/src/runtime/components/index.ts +55 -0
  108. package/src/runtime/index.ts +42 -0
  109. package/src/runtime/inputOwnership.ts +68 -0
  110. package/src/runtime/keyboardTarget.ts +7 -0
  111. package/src/runtime/lastSlideRoute.ts +56 -0
  112. package/src/runtime/navigation.ts +211 -0
  113. package/src/runtime/router.ts +157 -0
  114. package/src/runtime/slideData.ts +137 -0
  115. package/src/runtime/sync.ts +267 -0
  116. package/src/runtime/types.ts +182 -0
  117. package/src/runtime/useKeyboardNav.ts +138 -0
  118. package/src/runtime/useSwipeNav.ts +257 -0
  119. package/src/runtime/views/DocsView.tsx +74 -0
  120. package/src/runtime/views/OverviewView.tsx +386 -0
  121. package/src/runtime/views/PresenterNotesPanel.tsx +76 -0
  122. package/src/runtime/views/PresenterView.tsx +340 -0
  123. package/src/runtime/views/SPEC.md +152 -0
  124. package/src/runtime/views/docs/ComponentsTab.tsx +178 -0
  125. package/src/runtime/views/docs/DocsHeader.tsx +101 -0
  126. package/src/runtime/views/docs/Intro.tsx +20 -0
  127. package/src/runtime/views/docs/LayoutsTab.tsx +324 -0
  128. package/src/runtime/views/docs/ThemeTab.tsx +110 -0
  129. package/src/runtime/views/index.ts +7 -0
  130. package/src/runtime/views/overviewGrid.ts +106 -0
  131. package/src/runtime/views/presenterPreview.ts +27 -0
  132. package/src/runtime/virtual-modules.d.ts +98 -0
  133. package/src/theme/SPEC.md +179 -0
  134. package/src/theme/base.css +623 -0
  135. package/src/theme/bee.css +35 -0
  136. package/src/theme/clean.css +38 -0
  137. package/src/vite-plugin/SPEC.md +114 -0
  138. package/src/vite-plugin/component-doc-crawler.ts +350 -0
  139. package/src/vite-plugin/deck-loader.ts +148 -0
  140. package/src/vite-plugin/index.ts +373 -0
  141. package/src/vite-plugin/layout-demo-crawler.ts +802 -0
  142. package/src/vite-plugin/splitter.ts +353 -0
  143. package/src/vite-plugin/token-manifest.ts +163 -0
  144. package/src/vite-plugin/virtual-modules.ts +587 -0
@@ -0,0 +1,393 @@
1
+ # Honeydeck Layouts and Kits Specification
2
+
3
+ > Observable behavior for kits, layout maps, built-in layouts, layout props, and layout demos.
4
+
5
+ ## Kits — Theme, Layouts, Components
6
+
7
+ A **kit** is an npm package (or local folder) that bundles three concerns. Honeydeck does not require a special registry mechanism; themes are CSS imports, layouts are default-exported layout maps, and components are normal React/MDX imports:
8
+
9
+ | Concern | What it is | How it's used |
10
+ |---------|-----------|---------------|
11
+ | Theme | CSS file (tokens, colors, typography) | explicit CSS import from MDX or another CSS file |
12
+ | Layouts | Layout component map | `layouts:` in frontmatter |
13
+ | Components | Reusable React components | `import` in MDX |
14
+
15
+ ### Kit Package Structure
16
+
17
+ ```txt
18
+ @company/honeydeck-kit-brand/
19
+ package.json
20
+ theme.css ← CSS tokens/variables
21
+ layouts/
22
+ index.ts ← exports LayoutMap
23
+ Blank.tsx
24
+ Default.tsx
25
+ Cover.tsx
26
+ Section.tsx
27
+ TwoCol.tsx
28
+ Image.tsx
29
+ ImageLeft.tsx
30
+ ImageRight.tsx
31
+ components/
32
+ Callout.tsx
33
+ Badge.tsx
34
+ ```
35
+
36
+ ### Using a Kit
37
+
38
+ ```mdx
39
+ ---
40
+ title: My Talk
41
+ layouts: "@company/honeydeck-kit-brand/layouts"
42
+ ---
43
+
44
+ import '@company/honeydeck-kit-brand/theme.css'
45
+ import './my-overrides.css'
46
+
47
+ import { Callout } from '@company/honeydeck-kit-brand/components'
48
+
49
+ # First slide
50
+
51
+ <Callout>Important!</Callout>
52
+ ```
53
+
54
+ ### Mix and Match
55
+
56
+ Theme CSS from one kit, layouts from another, components from a third:
57
+
58
+ ```mdx
59
+ ---
60
+ layouts: "@company/honeydeck-kit-brand/layouts"
61
+ ---
62
+
63
+ import '@other/honeydeck-kit-minimal/theme.css'
64
+ import { Badge } from '@third/honeydeck-kit-fancy/components'
65
+ ```
66
+
67
+ ### Local Kits
68
+
69
+ Create a kit in your project folder:
70
+
71
+ ```txt
72
+ awesome-talk/
73
+ theme.css
74
+ layouts/
75
+ index.ts
76
+ MyCustom.tsx
77
+ ```
78
+
79
+ Reference locally (relative to the deck entry file):
80
+
81
+ ```yaml
82
+ ---
83
+ layouts: "./layouts"
84
+ ---
85
+ ```
86
+
87
+ ```mdx
88
+ import './theme.css'
89
+ ```
90
+
91
+ ### Kit CSS Inheritance
92
+
93
+ Use standard CSS `@import` for extending another kit's theme:
94
+
95
+ ```css
96
+ /* theme.css — extends company kit */
97
+ @import '@company/honeydeck-kit-brand/theme.css';
98
+
99
+ :root {
100
+ --honeydeck-primary: oklch(55% 0.25 145);
101
+ }
102
+ ```
103
+
104
+ ### Layout Inheritance
105
+
106
+ Use JS spread to compose layouts from multiple sources:
107
+
108
+ ```ts
109
+ // layouts/index.ts
110
+ import companyLayouts from '@company/honeydeck-kit-brand/layouts'
111
+ import fancyLayouts from '@fancy/honeydeck-kit/layouts'
112
+ import { MyCustomCover } from './Cover'
113
+
114
+ export default {
115
+ ...companyLayouts,
116
+ Section: fancyLayouts.Section,
117
+ Cover: MyCustomCover,
118
+ } satisfies LayoutMap
119
+ ```
120
+
121
+ ### Zero Config
122
+
123
+ A plain `deck.mdx` file with no frontmatter still works with built-in layouts:
124
+
125
+ ```mdx
126
+ # Hello world
127
+
128
+ This renders with the built-in default layout.
129
+ ```
130
+
131
+ Styling is provided only by explicit CSS imports. Honeydeck does not auto-inject Tailwind, `@honeydeck/honeydeck/theme.css`, or kit theme CSS. The starter project imports `./styles.css` from `deck.mdx`; `styles.css` imports Tailwind and `@honeydeck/honeydeck/theme.css`. Without that import, slides still render with built-in layouts, but mostly with browser/default styling. User imports override via cascade.
132
+
133
+ ### Bundled Theme Layers
134
+
135
+ Honeydeck ships optional theme layers that are imported after the base theme. Theme layers are token overrides and are not standalone replacements for the base theme:
136
+
137
+ ```css
138
+ @import "tailwindcss";
139
+ @import "@honeydeck/honeydeck/theme.css";
140
+ @import "@honeydeck/honeydeck/themes/clean.css";
141
+ ```
142
+
143
+ Honeydeck includes:
144
+
145
+ | Theme import | Purpose |
146
+ |--------------|---------|
147
+ | `@honeydeck/honeydeck/theme.css` | Preferred base theme import; clean black/white/grey defaults |
148
+ | `@honeydeck/honeydeck/themes/base.css` | Package export alias for the base theme |
149
+ | `@honeydeck/honeydeck/themes/clean.css` | Optional clean visual style layer |
150
+ | `@honeydeck/honeydeck/themes/bee.css` | Bee theme layer for the original playful palette |
151
+
152
+ Use `@honeydeck/honeydeck/layouts` for clean default layouts. Use `@honeydeck/honeydeck/layouts/bee` together with `@honeydeck/honeydeck/themes/bee.css` for the Bee visual style.
153
+
154
+ ### Progressive Customization
155
+
156
+ ```txt
157
+ Level 0: plain `deck.mdx` → built-in layouts, browser/default styling
158
+ Level 1: import CSS → Tailwind, base styling, and token overrides
159
+ Level 2: set layouts: → custom layout map
160
+ Level 3: local kit folder → full control
161
+ Level 4: publish npm kit → share across projects
162
+ ```
163
+
164
+ ---
165
+
166
+ ---
167
+
168
+ ## Layouts
169
+
170
+ ### Default Layouts
171
+
172
+ Honeydeck provides eight clean default layouts. Honeydeck also ships `@honeydeck/honeydeck/layouts/clean` as a named clean kit import path and `@honeydeck/honeydeck/layouts/bee` for the playful Bee layouts:
173
+
174
+ | Layout | Purpose |
175
+ |--------|---------|
176
+ | `Blank` | Empty slide just rendering children |
177
+ | `Default` | Title top-left, body flows below in a padded content frame |
178
+ | `Section` | Display-sized centered heading for section breaks |
179
+ | `Cover` | Opening/closing slide (title, body, author/date) |
180
+ | `TwoCol` | Default title area plus two equal body columns |
181
+ | `Image` | Default title area plus image stage and optional caption |
182
+ | `ImageLeft` | Left image pane, title and body content on the right |
183
+ | `ImageRight` | Right image pane, title and body content on the left |
184
+
185
+ ### Layout Selection
186
+
187
+ Via frontmatter (PascalCase by convention — Honeydeck treats the value as a string key in the layout map):
188
+
189
+ ```mdx
190
+ ---
191
+ layout: Cover
192
+ author: Hendrik
193
+ ---
194
+
195
+ # Welcome to Honeydeck
196
+
197
+ A modern slide framework.
198
+ ```
199
+
200
+ If no `layout:` is specified, the deck's `defaultLayout` is used (`"Default"` unless overridden). In dev, unknown layouts warn in the browser console and fall back to the configured default, then `Default`, then the first available layout. In production/PDF, unknown layouts throw.
201
+
202
+ ### Layout Props
203
+
204
+ Layouts receive parsed content:
205
+
206
+ ```ts
207
+ type LayoutProps<F extends Record<string, unknown> = Record<string, unknown>> = {
208
+ title: ReactNode | null // currently plain text from the first h1, or null
209
+ children: ReactNode // remaining content (first h1 removed; later h1s remain)
210
+ rawChildren: ReactNode // currently same compiled content as children
211
+ frontmatter: F // slide frontmatter fields
212
+ }
213
+ ```
214
+
215
+ The first `h1` is extracted as plain text and provided as `title`. This enables layouts to position titles independently from body content. Titles stay in the same position at all times — revealed body content must not shift the title.
216
+
217
+ Built-in layouts share a `SlideFrame` wrapper that provides full-canvas sizing, `--honeydeck-slide-padding`, `bg-background`, `text-foreground`, `font-body`, and overflow clipping. `Blank` renders an empty `SlideFrame` without title and display children. `Default` owns the common headline/body structure. `TwoCol` and `Image` compose with `Default`; `ImageLeft` and `ImageRight` use a shared side-image helper with matching title styling.
218
+
219
+ ### Layout-Specific Typed Frontmatter
220
+
221
+ Layouts type their accepted frontmatter via the generic parameter:
222
+
223
+ ```tsx
224
+ type CoverFrontmatter = {
225
+ author?: string
226
+ date?: string
227
+ }
228
+
229
+ export default function CoverLayout({ title, children, frontmatter }: LayoutProps<CoverFrontmatter>) {
230
+ const { author, date } = frontmatter
231
+ return (/* ... */)
232
+ }
233
+ ```
234
+
235
+ This enables editor tooling to extract props and provide autocomplete.
236
+
237
+ ### Layout Demos
238
+
239
+ Layouts optionally export a `demo` for the docs reference page:
240
+
241
+ ```tsx
242
+ export const demo: LayoutDemo<CoverFrontmatter> = {
243
+ mdx: `---
244
+ layout: Cover
245
+ author: Hendrik
246
+ ---
247
+
248
+ # Welcome to My Talk
249
+
250
+ Building the future of presentations.`,
251
+ }
252
+ ```
253
+
254
+ `mdx` is required on `LayoutDemo` and is the single source for both the live visual preview and the copyable snippet shown in the layouts docs tab. Honeydeck compiles this MDX with the same slide MDX compiler family, extracts frontmatter/title/steps from it, and renders the resulting slide through the active layout map. Honeydeck statically crawls analyzable active layout maps at build time and discovers colocated `demo` exports from layout modules. Dynamic maps, computed entries, non-static imports, and demos whose `mdx` value is not a static string may be skipped with warnings. If no static MDX demo is discovered, the layout still appears in the reference pages with a "No demo MDX provided" hint.
255
+
256
+ ### TwoCol Slot Components
257
+
258
+ The `TwoCol` layout composes with `Default` for the shared title area, then renders its body as a Tailwind-styled two-column grid (`grid h-full grid-cols-2 gap-12`). It exports `<Left>` and `<Right>` slot components. These are thin wrappers that render `<div data-honeydeck-slot="left|right">` with Tailwind column utilities (`col-start-1` / `col-start-2`) and `overflow-hidden`.
259
+
260
+ This works because MDX components return fragments: the slot divs become direct DOM children of the grid container without any intermediate wrapper. No JavaScript slot-detection logic is needed.
261
+
262
+ ```mdx
263
+ ---
264
+ layout: TwoCol
265
+ ---
266
+
267
+ import { Left, Right } from '@honeydeck/honeydeck/layouts/TwoCol'
268
+
269
+ <Left>
270
+ ## Pros
271
+ - Fast
272
+ - Simple
273
+ </Left>
274
+
275
+ <Right>
276
+ ## Cons
277
+ - Limited
278
+ </Right>
279
+ ```
280
+
281
+ Implementation uses Tailwind utility classes:
282
+
283
+ ```tsx
284
+ <div className="grid h-full grid-cols-2 gap-12">
285
+ {children}
286
+ </div>
287
+
288
+ export function Left({ children }) {
289
+ return <div data-honeydeck-slot="left" className="col-start-1 overflow-hidden">{children}</div>
290
+ }
291
+
292
+ export function Right({ children }) {
293
+ return <div data-honeydeck-slot="right" className="col-start-2 overflow-hidden">{children}</div>
294
+ }
295
+ ```
296
+
297
+ ### Image Layout
298
+
299
+ The `Image` layout composes with `Default` for the shared title area, then renders a large centered image stage in the remaining space.
300
+
301
+ Frontmatter:
302
+
303
+ | Property | Type | Default | Description |
304
+ |----------|------|---------|-------------|
305
+ | `image` | `string` | none | Image URL/path (`/foo.png` for `public/foo.png`, relative import output, or external URL) |
306
+ | `darkImage` | `string` | falls back to `image` | Optional image URL/path used when Honeydeck's effective color mode is dark |
307
+ | `alt` | `string` | `""` | Alt text forwarded to the `<img>` |
308
+
309
+ Behavior:
310
+
311
+ - The image is wrapped in a surface-colored frame with shadow and border ring.
312
+ - The frame clings to the intrinsic image size (`object-contain`) instead of filling the full stage.
313
+ - If `darkImage` is provided, Honeydeck renders both variants and uses Tailwind utility classes keyed by Honeydeck's effective color mode to display the dark variant in dark mode, including pinned dark mode, system dark mode, and PDF `--mode dark`.
314
+ - MDX body content after the title becomes a `<figcaption>` below the image with an accent left border.
315
+ - If `image` is omitted, Honeydeck shows a bundled placeholder image plus a hint to add `image: /path/to/image.png`; the placeholder also uses its bundled dark variant in dark mode.
316
+
317
+ ```yaml
318
+ ---
319
+ layout: Image
320
+ image: /diagrams/architecture.png
321
+ darkImage: /diagrams/architecture-dark.png
322
+ alt: High-level architecture diagram
323
+ ---
324
+
325
+ # System Architecture
326
+
327
+ Our distributed system in production.
328
+ ```
329
+
330
+ ### Side Image Layouts
331
+
332
+ `ImageLeft` and `ImageRight` place an image beside the slide content. They use the same `image`, `darkImage`, and `alt` frontmatter as `Image`.
333
+
334
+ Frontmatter:
335
+
336
+ | Property | Type | Default | Description |
337
+ |----------|------|---------|-------------|
338
+ | `image` | `string` | none | Image URL/path (`/foo.png` for `public/foo.png`, relative import output, or external URL) |
339
+ | `darkImage` | `string` | falls back to `image` | Optional image URL/path used when Honeydeck's effective color mode is dark |
340
+ | `alt` | `string` | `""` | Alt text forwarded to the `<img>` |
341
+
342
+ Behavior:
343
+ - `ImageLeft` renders the image on the left and title/body content on the right.
344
+ - `ImageRight` renders title/body content on the left and the image on the right.
345
+ - The image pane fills half of the slide and uses `object-cover`.
346
+ - If `darkImage` is provided, Honeydeck renders both variants and uses Tailwind utility classes keyed by Honeydeck's effective color mode to display the dark variant in dark mode, including pinned dark mode, system dark mode, and PDF `--mode dark`.
347
+ - If `image` is omitted, Honeydeck shows a bundled vertical placeholder image; the placeholder also uses its bundled dark variant in dark mode.
348
+
349
+ ```yaml
350
+ ---
351
+ layout: ImageLeft
352
+ image: /photos/product.jpg
353
+ darkImage: /photos/product-dark.jpg
354
+ alt: Product detail
355
+ ---
356
+
357
+ # Product Detail
358
+
359
+ Supporting copy beside the image.
360
+ ```
361
+
362
+ ### Custom Layouts (Project-Local)
363
+
364
+ Project-local custom layouts are usually exposed through a layout map and selected with `layouts: "./layouts"` plus per-slide `layout:`. You can also use ad-hoc React components via explicit import and wrapping:
365
+
366
+ ```mdx
367
+ ---
368
+
369
+ import { CustomLayout } from './components/CustomLayout'
370
+
371
+ <CustomLayout>
372
+ # Special Slide
373
+
374
+ With custom styling.
375
+ </CustomLayout>
376
+
377
+ ---
378
+ ```
379
+
380
+ ### Layout Resolution
381
+
382
+ ```txt
383
+ layouts: not specified → built-in default layout map
384
+ layouts: "@pkg/layouts" → layout map from npm package
385
+ layouts: "./layouts" → local layout map default export, relative to entry MDX
386
+ ```
387
+
388
+ Within a layout map, per-slide:
389
+
390
+ ```txt
391
+ layout: "Cover" → layoutMap["Cover"]
392
+ layout: not set → layoutMap[defaultLayout]
393
+ ```
@@ -0,0 +1,48 @@
1
+ /**
2
+ * SlideFrame — shared wrapper for all slide layouts.
3
+ *
4
+ * Provides the common structure every layout needs:
5
+ * - Full-size container (fills the deck canvas)
6
+ * - Content padding from `--honeydeck-slide-padding` CSS variable
7
+ * - Flex column layout
8
+ * - Overflow clipping
9
+ *
10
+ * Layouts compose on top of this by passing `className` for their specific
11
+ * styling (background, text color, alignment, etc.).
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <SlideFrame className="bg-primary text-primary-foreground items-center justify-center text-center">
16
+ * <h1>Section Title</h1>
17
+ * </SlideFrame>
18
+ * ```
19
+ */
20
+
21
+ import type { ReactNode } from "react";
22
+ import { cn } from "./utils.ts";
23
+
24
+ export type SlideFrameProps = {
25
+ children: ReactNode;
26
+ /** Additional Tailwind classes for layout-specific styling (bg, alignment, etc.) */
27
+ className?: string;
28
+ /** Set to false for layouts that manage their own padding (e.g. Image with edge-to-edge elements). */
29
+ padded?: boolean;
30
+ };
31
+
32
+ export function SlideFrame({
33
+ children,
34
+ className = "",
35
+ padded = true,
36
+ }: SlideFrameProps) {
37
+ return (
38
+ <div
39
+ className={cn(
40
+ "relative size-full flex flex-col overflow-hidden bg-background text-foreground font-body",
41
+ padded && "p-[var(--honeydeck-slide-padding)]",
42
+ className,
43
+ )}
44
+ >
45
+ {children}
46
+ </div>
47
+ );
48
+ }
@@ -0,0 +1,12 @@
1
+ import type { LayoutDemo, LayoutProps } from "../../runtime/types.ts";
2
+ import { SlideFrame } from "../SlideFrame.tsx";
3
+
4
+ export default function BlankLayout(props: LayoutProps) {
5
+ return <SlideFrame>{props.children}</SlideFrame>;
6
+ }
7
+
8
+ export const demo: LayoutDemo = {
9
+ mdx: `---
10
+ layout: Blank
11
+ ---`,
12
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Cover layout — centered title, body copy, and author.
3
+ *
4
+ * Designed for opening and closing slides. Title is large and centered;
5
+ * body content and optional `author` frontmatter appear below.
6
+ */
7
+
8
+ import type { LayoutDemo, LayoutProps } from "../../runtime/types.ts";
9
+ import { SlideFrame } from "../SlideFrame.tsx";
10
+ import { hasTitle } from "../utils.ts";
11
+
12
+ type CoverFrontmatter = {
13
+ /** Speaker or organization shown below the cover body. */
14
+ author?: string;
15
+ /** Date or event label shown below the cover body. */
16
+ date?: string;
17
+ };
18
+
19
+ export default function CoverLayout({
20
+ title,
21
+ children,
22
+ frontmatter,
23
+ }: LayoutProps<CoverFrontmatter>) {
24
+ const { author, date } = frontmatter;
25
+
26
+ return (
27
+ <SlideFrame className="items-center justify-center text-center">
28
+ {/* Large centered title */}
29
+ {hasTitle(title) && (
30
+ <h1 className="font-heading font-bold text-[length:var(--honeydeck-font-size-h1)] leading-tight text-primary mb-14">
31
+ {title}
32
+ </h1>
33
+ )}
34
+
35
+ {/* Slide body (MDX content after title) */}
36
+ {children && (
37
+ <div className="text-[length:var(--honeydeck-font-size-body)] text-surface-foreground font-light mb-18 max-w-4xl">
38
+ {children}
39
+ </div>
40
+ )}
41
+
42
+ {/* Author / date metadata */}
43
+ {(author || date) && (
44
+ <div className="flex items-center gap-14 text-[length:var(--honeydeck-font-size-small)] text-surface-foreground mt-9">
45
+ {author && <span>{author}</span>}
46
+ {author && date && <span className="text-border">·</span>}
47
+ {date && <span>{date}</span>}
48
+ </div>
49
+ )}
50
+
51
+ {/* Decorative accent bar */}
52
+ <div
53
+ className="absolute bottom-0 left-0 right-0 h-5 bg-accent"
54
+ aria-hidden="true"
55
+ />
56
+ </SlideFrame>
57
+ );
58
+ }
59
+
60
+ export const demo: LayoutDemo<CoverFrontmatter> = {
61
+ mdx: `---
62
+ layout: Cover
63
+ author: The honeydeck team
64
+ date: 2026
65
+ ---
66
+
67
+ # Welcome to My Talk
68
+
69
+ A modern slide framework.`,
70
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Default layout — title top-left, body flows below.
3
+ *
4
+ * The most versatile layout for content slides. Title is pinned to the
5
+ * upper-left area and body content fills the remaining space.
6
+ */
7
+
8
+ import type { LayoutDemo, LayoutProps } from "../../runtime/types.ts";
9
+ import { SlideFrame } from "../SlideFrame.tsx";
10
+ import { hasTitle } from "../utils.ts";
11
+
12
+ export default function DefaultLayout({ title, children }: LayoutProps) {
13
+ return (
14
+ <SlideFrame>
15
+ {/* Title — always visible, layout-stable */}
16
+ {hasTitle(title) && (
17
+ <header className="mb-8 flex-shrink-0">
18
+ <h1 className="font-heading font-bold text-[length:var(--honeydeck-font-size-h2)] leading-tight text-primary">
19
+ {title}
20
+ </h1>
21
+ <div
22
+ className="mt-4 h-2 w-28 rounded-full bg-accent"
23
+ aria-hidden="true"
24
+ />
25
+ </header>
26
+ )}
27
+
28
+ {/* Body content */}
29
+ <div className="flex-1 min-h-0 overflow-hidden">{children}</div>
30
+ </SlideFrame>
31
+ );
32
+ }
33
+
34
+ export const demo: LayoutDemo = {
35
+ mdx: `---
36
+ layout: Default
37
+ ---
38
+
39
+ # Default Layout
40
+
41
+ Body content flows naturally below the title.`,
42
+ };