@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.
- package/AGENTS.md +25 -0
- package/DEVELOPMENT.md +522 -0
- package/LICENSE +21 -0
- package/Readme.md +49 -0
- package/SPEC.md +88 -0
- package/docs/components.md +63 -0
- package/docs/configuration.md +91 -0
- package/docs/getting-started.md +116 -0
- package/docs/kit-authoring.md +207 -0
- package/docs/kits.md +387 -0
- package/docs/local-development.md +95 -0
- package/docs/mermaid.md +198 -0
- package/docs/mobile.md +108 -0
- package/docs/navigation.md +93 -0
- package/docs/next-steps.md +377 -0
- package/docs/pdf-export.md +91 -0
- package/docs/presenter-mode.md +104 -0
- package/docs/slides.md +130 -0
- package/docs/slidev-migration.md +42 -0
- package/docs/steps-and-reveals.md +171 -0
- package/package.json +134 -0
- package/skills/SPEC.md +21 -0
- package/skills/honeydeck/SKILL.md +65 -0
- package/skills/presentation-writing/SKILL.md +75 -0
- package/skills/slidev-migration/SKILL.md +153 -0
- package/src/SPEC.md +89 -0
- package/src/assets.d.ts +30 -0
- package/src/cli/SPEC.md +230 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/banner.ts +9 -0
- package/src/cli/bin.js +5 -0
- package/src/cli/build.ts +229 -0
- package/src/cli/deck-path.ts +32 -0
- package/src/cli/dev.ts +263 -0
- package/src/cli/index.ts +126 -0
- package/src/cli/init.ts +369 -0
- package/src/cli/pdf.ts +923 -0
- package/src/cli/skill.ts +75 -0
- package/src/cli/templates/SPEC.md +70 -0
- package/src/cli/templates/deck-mdx.ts +15 -0
- package/src/cli/templates/package-json.ts +36 -0
- package/src/cli/templates/sparkle-button.ts +15 -0
- package/src/cli/templates/starter/components/SparkleButton.tsx +84 -0
- package/src/cli/templates/starter/deck.mdx +153 -0
- package/src/cli/templates/starter/styles.css +14 -0
- package/src/cli/templates/styles-css.ts +14 -0
- package/src/defaults.ts +1 -0
- package/src/layouts/ColorModeImage.tsx +55 -0
- package/src/layouts/SPEC.md +393 -0
- package/src/layouts/SlideFrame.tsx +48 -0
- package/src/layouts/bee/Blank.tsx +12 -0
- package/src/layouts/bee/Cover.tsx +70 -0
- package/src/layouts/bee/Default.tsx +42 -0
- package/src/layouts/bee/Image/Image.tsx +151 -0
- package/src/layouts/bee/Image/placeholder-dark.webp +0 -0
- package/src/layouts/bee/Image/placeholder-vertical-dark.webp +0 -0
- package/src/layouts/bee/Image/placeholder-vertical.webp +0 -0
- package/src/layouts/bee/Image/placeholder.webp +0 -0
- package/src/layouts/bee/ImageLeft.tsx +27 -0
- package/src/layouts/bee/ImageRight.tsx +27 -0
- package/src/layouts/bee/ImageSide.tsx +107 -0
- package/src/layouts/bee/Section.tsx +40 -0
- package/src/layouts/bee/TwoCol.tsx +108 -0
- package/src/layouts/bee/index.ts +40 -0
- package/src/layouts/clean/Blank.tsx +12 -0
- package/src/layouts/clean/Cover.tsx +58 -0
- package/src/layouts/clean/Default.tsx +33 -0
- package/src/layouts/clean/Image/Image.tsx +103 -0
- package/src/layouts/clean/ImageLeft.tsx +27 -0
- package/src/layouts/clean/ImageRight.tsx +27 -0
- package/src/layouts/clean/ImageSide.tsx +113 -0
- package/src/layouts/clean/Section.tsx +35 -0
- package/src/layouts/clean/TwoCol.tsx +63 -0
- package/src/layouts/clean/index.ts +40 -0
- package/src/layouts/index.ts +60 -0
- package/src/layouts/placeholders.ts +9 -0
- package/src/layouts/utils.ts +13 -0
- package/src/remark/SPEC.md +49 -0
- package/src/remark/h1-extract.ts +124 -0
- package/src/remark/index.ts +4 -0
- package/src/remark/shiki-code-blocks.ts +325 -0
- package/src/remark/step-numbering.ts +412 -0
- package/src/runtime/Deck.tsx +533 -0
- package/src/runtime/SPEC.md +256 -0
- package/src/runtime/SlideCanvas.tsx +95 -0
- package/src/runtime/TimelineContext.tsx +122 -0
- package/src/runtime/app-shell/index.html +31 -0
- package/src/runtime/app-shell/main.tsx +42 -0
- package/src/runtime/aspectRatio.ts +34 -0
- package/src/runtime/colorMode.ts +23 -0
- package/src/runtime/components/BrowserFrame.tsx +233 -0
- package/src/runtime/components/Button.tsx +57 -0
- package/src/runtime/components/CodeBlock.tsx +210 -0
- package/src/runtime/components/ColorModeCycleButton.tsx +59 -0
- package/src/runtime/components/ErrorBoundary.tsx +125 -0
- package/src/runtime/components/Keyboard.tsx +87 -0
- package/src/runtime/components/ListStyle.tsx +203 -0
- package/src/runtime/components/NavBar.tsx +223 -0
- package/src/runtime/components/NavBarButton.tsx +47 -0
- package/src/runtime/components/NavBarDivider.tsx +3 -0
- package/src/runtime/components/Notes.tsx +171 -0
- package/src/runtime/components/Reveal.tsx +82 -0
- package/src/runtime/components/RevealGroup.tsx +193 -0
- package/src/runtime/components/SPEC.md +263 -0
- package/src/runtime/components/SlideNumberBadge.tsx +11 -0
- package/src/runtime/components/TimelineSteps.tsx +115 -0
- package/src/runtime/components/index.ts +55 -0
- package/src/runtime/index.ts +42 -0
- package/src/runtime/inputOwnership.ts +68 -0
- package/src/runtime/keyboardTarget.ts +7 -0
- package/src/runtime/lastSlideRoute.ts +56 -0
- package/src/runtime/navigation.ts +211 -0
- package/src/runtime/router.ts +157 -0
- package/src/runtime/slideData.ts +137 -0
- package/src/runtime/sync.ts +267 -0
- package/src/runtime/types.ts +182 -0
- package/src/runtime/useKeyboardNav.ts +138 -0
- package/src/runtime/useSwipeNav.ts +257 -0
- package/src/runtime/views/DocsView.tsx +74 -0
- package/src/runtime/views/OverviewView.tsx +386 -0
- package/src/runtime/views/PresenterNotesPanel.tsx +76 -0
- package/src/runtime/views/PresenterView.tsx +340 -0
- package/src/runtime/views/SPEC.md +152 -0
- package/src/runtime/views/docs/ComponentsTab.tsx +178 -0
- package/src/runtime/views/docs/DocsHeader.tsx +101 -0
- package/src/runtime/views/docs/Intro.tsx +20 -0
- package/src/runtime/views/docs/LayoutsTab.tsx +324 -0
- package/src/runtime/views/docs/ThemeTab.tsx +110 -0
- package/src/runtime/views/index.ts +7 -0
- package/src/runtime/views/overviewGrid.ts +106 -0
- package/src/runtime/views/presenterPreview.ts +27 -0
- package/src/runtime/virtual-modules.d.ts +98 -0
- package/src/theme/SPEC.md +179 -0
- package/src/theme/base.css +623 -0
- package/src/theme/bee.css +35 -0
- package/src/theme/clean.css +38 -0
- package/src/vite-plugin/SPEC.md +114 -0
- package/src/vite-plugin/component-doc-crawler.ts +350 -0
- package/src/vite-plugin/deck-loader.ts +148 -0
- package/src/vite-plugin/index.ts +373 -0
- package/src/vite-plugin/layout-demo-crawler.ts +802 -0
- package/src/vite-plugin/splitter.ts +353 -0
- package/src/vite-plugin/token-manifest.ts +163 -0
- 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
|
+
};
|