@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,110 @@
1
+ import type { TokenManifestEntry } from "virtual:honeydeck/token-manifest";
2
+ import * as tokenManifest from "virtual:honeydeck/token-manifest";
3
+ import { useEffect, useState } from "react";
4
+ import { Intro } from "./Intro.tsx";
5
+
6
+ const { tokens } = tokenManifest;
7
+
8
+ function colorLike(token: TokenManifestEntry): boolean {
9
+ const value = token.defaultValue.toLowerCase();
10
+ return (
11
+ value.startsWith("#") ||
12
+ value.startsWith("rgb(") ||
13
+ value.startsWith("hsl(") ||
14
+ value.startsWith("oklch(")
15
+ );
16
+ }
17
+
18
+ function TokenValue({ value }: { value: string }) {
19
+ return (
20
+ <code
21
+ className="block max-w-full overflow-hidden text-ellipsis whitespace-nowrap rounded-xs bg-surface px-2 py-1 font-mono text-xs text-surface-foreground"
22
+ title={value}
23
+ >
24
+ {value}
25
+ </code>
26
+ );
27
+ }
28
+
29
+ function ComputedTokenValue({ name }: { name: string }) {
30
+ const [value, setValue] = useState("");
31
+
32
+ useEffect(() => {
33
+ function read() {
34
+ setValue(
35
+ getComputedStyle(document.documentElement)
36
+ .getPropertyValue(name)
37
+ .trim(),
38
+ );
39
+ }
40
+
41
+ read();
42
+ const observer = new MutationObserver(read);
43
+ observer.observe(document.documentElement, {
44
+ attributes: true,
45
+ attributeFilter: ["data-honeydeck-color-mode"],
46
+ });
47
+ return () => observer.disconnect();
48
+ }, [name]);
49
+
50
+ return <TokenValue value={value || "—"} />;
51
+ }
52
+
53
+ export function ThemeTab() {
54
+ return (
55
+ <>
56
+ <Intro title="Theme tokens">
57
+ All{" "}
58
+ <code className="rounded-xs bg-surface px-1 py-0.5 font-mono">
59
+ --honeydeck-*
60
+ </code>{" "}
61
+ tokens. Descriptions come from CSS comments; computed values come from{" "}
62
+ <code className="rounded-xs bg-surface px-1 py-0.5 font-mono">
63
+ getComputedStyle
64
+ </code>
65
+ .
66
+ </Intro>
67
+
68
+ <div className="overflow-x-auto rounded-md border border-border">
69
+ <table className="w-full min-w-240 table-fixed border-collapse text-left text-md">
70
+ <thead className="border-b border-border bg-surface text-xs uppercase tracking-wider text-surface-foreground/60">
71
+ <tr>
72
+ <th className="w-[28%] px-4 py-3 font-medium">Token</th>
73
+ <th className="w-[32%] px-4 py-3 font-medium">Description</th>
74
+ <th className="w-[20%] px-4 py-3 font-medium">Default</th>
75
+ <th className="w-[20%] px-4 py-3 font-medium">Computed</th>
76
+ </tr>
77
+ </thead>
78
+ <tbody className="divide-y divide-border">
79
+ {tokens.map((token) => (
80
+ <tr key={token.name}>
81
+ <td className="px-4 py-3 align-top">
82
+ <div className="flex items-center gap-2">
83
+ {colorLike(token) && (
84
+ <span
85
+ className="size-5 shrink-0 rounded-xs border border-border"
86
+ style={{ backgroundColor: `var(${token.name})` }}
87
+ />
88
+ )}
89
+ <code className="font-mono text-sm font-medium text-foreground">
90
+ {token.name}
91
+ </code>
92
+ </div>
93
+ </td>
94
+ <td className="px-4 py-3 align-top leading-6 text-foreground/70">
95
+ {token.description || "—"}
96
+ </td>
97
+ <td className="px-4 py-3 align-top">
98
+ <TokenValue value={token.defaultValue} />
99
+ </td>
100
+ <td className="px-4 py-3 align-top">
101
+ <ComputedTokenValue name={token.name} />
102
+ </td>
103
+ </tr>
104
+ ))}
105
+ </tbody>
106
+ </table>
107
+ </div>
108
+ </>
109
+ );
110
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * View exports for Honeydeck.
3
+ */
4
+
5
+ export { DocsView } from "./DocsView.tsx";
6
+ export { OverviewView } from "./OverviewView.tsx";
7
+ export { PresenterView } from "./PresenterView.tsx";
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Helpers for overview-grid keyboard navigation.
3
+ *
4
+ * Arrow up/down must follow the grid the browser actually rendered. This keeps
5
+ * keyboard movement aligned with CSS after first layout and after resizes.
6
+ */
7
+
8
+ export function countGridColumnsFromChildren(children: HTMLCollection): number {
9
+ const firstChild = children.item(0) as HTMLElement | null;
10
+ if (!firstChild) return 0;
11
+
12
+ const firstRowTop = firstChild.offsetTop;
13
+ let columns = 0;
14
+
15
+ for (const child of Array.from(children) as HTMLElement[]) {
16
+ if (Math.abs(child.offsetTop - firstRowTop) > 1) break;
17
+ columns += 1;
18
+ }
19
+
20
+ return columns;
21
+ }
22
+
23
+ export function countGridTemplateColumns(templateColumns: string): number {
24
+ const trimmed = templateColumns.trim();
25
+ if (!trimmed || trimmed === "none") return 0;
26
+
27
+ let depth = 0;
28
+ let count = 0;
29
+ let inTrack = false;
30
+
31
+ for (const char of trimmed) {
32
+ if (char === "(" || char === "[") depth += 1;
33
+ if (char === ")" || char === "]") depth = Math.max(0, depth - 1);
34
+
35
+ if (/\s/.test(char) && depth === 0) {
36
+ if (inTrack) count += 1;
37
+ inTrack = false;
38
+ continue;
39
+ }
40
+
41
+ inTrack = true;
42
+ }
43
+
44
+ if (inTrack) count += 1;
45
+
46
+ return count;
47
+ }
48
+
49
+ export function getOverviewGridColumnCount(grid: HTMLElement | null): number {
50
+ if (!grid) return 1;
51
+
52
+ const measuredColumns = countGridColumnsFromChildren(grid.children);
53
+ if (measuredColumns > 0) return measuredColumns;
54
+
55
+ const templateColumns = window.getComputedStyle(grid).gridTemplateColumns;
56
+ const computedColumns = countGridTemplateColumns(templateColumns);
57
+
58
+ return Math.max(1, computedColumns);
59
+ }
60
+
61
+ export type OverviewGridSelectionDirection =
62
+ | "ArrowRight"
63
+ | "ArrowLeft"
64
+ | "ArrowDown"
65
+ | "ArrowUp";
66
+
67
+ export type OverviewGridSelectionMove = {
68
+ selected: number;
69
+ didMove: boolean;
70
+ };
71
+
72
+ export function getOverviewGridSelectionMove(
73
+ selected: number,
74
+ total: number,
75
+ columns: number,
76
+ direction: OverviewGridSelectionDirection,
77
+ ): OverviewGridSelectionMove {
78
+ if (total <= 0) return { selected: 0, didMove: false };
79
+
80
+ const columnCount = Math.max(1, columns);
81
+ const current = Math.min(Math.max(selected, 0), total - 1);
82
+
83
+ let next = current;
84
+
85
+ switch (direction) {
86
+ case "ArrowRight":
87
+ next = Math.min(current + 1, total - 1);
88
+ break;
89
+ case "ArrowLeft":
90
+ next = Math.max(current - 1, 0);
91
+ break;
92
+ case "ArrowDown": {
93
+ const lastRowStart = Math.floor((total - 1) / columnCount) * columnCount;
94
+ next =
95
+ current >= lastRowStart
96
+ ? current
97
+ : Math.min(current + columnCount, total - 1);
98
+ break;
99
+ }
100
+ case "ArrowUp":
101
+ next = current < columnCount ? current : current - columnCount;
102
+ break;
103
+ }
104
+
105
+ return { selected: next, didMove: next !== current };
106
+ }
@@ -0,0 +1,27 @@
1
+ export type PresenterNextPreview = {
2
+ slideIndex: number;
3
+ stepIndex: number;
4
+ } | null;
5
+
6
+ export function getPresenterNextPreview({
7
+ currentIndex,
8
+ step,
9
+ stepCount,
10
+ totalSlides,
11
+ }: {
12
+ currentIndex: number;
13
+ step: number;
14
+ stepCount: number;
15
+ totalSlides: number;
16
+ }): PresenterNextPreview {
17
+ if (step < stepCount) {
18
+ return { slideIndex: currentIndex, stepIndex: step + 1 };
19
+ }
20
+
21
+ const nextSlideIndex = currentIndex + 1;
22
+ if (nextSlideIndex < totalSlides) {
23
+ return { slideIndex: nextSlideIndex, stepIndex: 0 };
24
+ }
25
+
26
+ return null;
27
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * TypeScript ambient declarations for Honeydeck virtual modules.
3
+ *
4
+ * These modules don't exist on disk — they're synthesised at build/dev time
5
+ * by `src/vite-plugin/virtual-modules.ts`. Without these declarations,
6
+ * TypeScript would reject any `import` statement targeting them.
7
+ */
8
+
9
+ /** Barrel re-export of all slides in the deck. */
10
+ declare module "virtual:honeydeck/slides" {
11
+ /** Total number of slides in the deck. */
12
+ export const slideCount: number;
13
+
14
+ // Individual slide components are exported as Slide0, Slide1, Slide2, …
15
+ // Per-slide metadata is exported as stepCount0, slideTitle0, slideFrontmatter0, slideLayout0, …
16
+ // They cannot be statically typed here because the count is dynamic.
17
+ // Access them via a cast:
18
+ // import * as slideModules from 'virtual:honeydeck/slides';
19
+ // const C = (slideModules as Record<string, unknown>)[`Slide${i}`] as ComponentType;
20
+ // const title = (slideModules as Record<string, unknown>)[`slideTitle${i}`] as string;
21
+ }
22
+
23
+ /**
24
+ * CSS token manifest — all --honeydeck-* tokens with descriptions and default values.
25
+ * Generated at build time from src/theme/base.css.
26
+ */
27
+ declare module "virtual:honeydeck/token-manifest" {
28
+ export type TokenManifestEntry = {
29
+ name: string;
30
+ description: string;
31
+ defaultValue: string;
32
+ };
33
+ export const tokens: TokenManifestEntry[];
34
+ export default tokens;
35
+ }
36
+
37
+ /** Active layout map and docs reference demo metadata. */
38
+ declare module "virtual:honeydeck/layouts" {
39
+ import type {
40
+ CompiledLayoutDemo,
41
+ LayoutMap,
42
+ LayoutPropDoc,
43
+ } from "./types.ts";
44
+
45
+ export const layoutMap: LayoutMap;
46
+ export const layoutNames: string[];
47
+ export const layoutDemos: Record<string, CompiledLayoutDemo>;
48
+ export const layoutPropDocs: Record<string, LayoutPropDoc[]>;
49
+ export const layoutDemoWarnings: string[];
50
+ export default layoutMap;
51
+ }
52
+
53
+ /** Built-in component reference documentation metadata. */
54
+ declare module "virtual:honeydeck/components" {
55
+ import type { ComponentType } from "react";
56
+ import type { ComponentDoc } from "./types.ts";
57
+
58
+ export const componentMap: Record<
59
+ string,
60
+ ComponentType<Record<string, unknown>>
61
+ >;
62
+ export const componentNames: string[];
63
+ export const componentDocs: Record<string, ComponentDoc>;
64
+ export const componentDocWarnings: string[];
65
+ export default componentMap;
66
+ }
67
+
68
+ /** Parsed deck-level frontmatter (the YAML block at the top of deck.mdx). */
69
+ declare module "virtual:honeydeck/config" {
70
+ const config: Record<string, unknown>;
71
+
72
+ export { config };
73
+ export default config;
74
+ }
75
+
76
+ /**
77
+ * Individual compiled slide modules.
78
+ * Each slide exports its component as default, plus metadata named exports.
79
+ */
80
+ declare module "virtual:honeydeck/slide/*.mdx" {
81
+ import type { ComponentType } from "react";
82
+
83
+ /** Compiled slide React component */
84
+ const Component: ComponentType;
85
+ export default Component;
86
+
87
+ /** Number of timeline steps on this slide (from remarkStepNumbering) */
88
+ export const stepCount: number;
89
+
90
+ /** Plain-text content of the first h1 (from remarkH1Extract) */
91
+ export const slideTitle: string;
92
+
93
+ /** Parsed YAML frontmatter for this slide (from remarkH1Extract) */
94
+ export const slideFrontmatter: Record<string, unknown>;
95
+
96
+ /** Layout name from frontmatter.layout, or '' for default */
97
+ export const slideLayout: string;
98
+ }
@@ -0,0 +1,179 @@
1
+ # Honeydeck Theme Specification
2
+
3
+ > Observable behavior for design tokens, theme CSS, Tailwind mapping, and color modes.
4
+
5
+ ## Design Token System
6
+
7
+ Honeydeck defines a CSS custom property contract. In the base theme, colors derive from a primary color using `oklch` relative color syntax. Light and dark modes each have their own primary. Theme layers may override any token directly.
8
+
9
+ ### Color Tokens (per mode)
10
+
11
+ ```css
12
+ /* Source color — brand identity for this mode */
13
+ --honeydeck-primary
14
+
15
+ /* Text color on primary backgrounds */
16
+ --honeydeck-primary-foreground
17
+
18
+ /* Main slide background (derived from primary) */
19
+ --honeydeck-background
20
+
21
+ /* Main text color (derived from primary) */
22
+ --honeydeck-foreground
23
+
24
+ /* Secondary surfaces — cards, code blocks, callouts */
25
+ --honeydeck-surface
26
+
27
+ /* Text color on surface backgrounds */
28
+ --honeydeck-surface-foreground
29
+
30
+ /* Border/divider color */
31
+ --honeydeck-border
32
+
33
+ /* Accent — computed as the complementary hue by default */
34
+ --honeydeck-accent
35
+
36
+ /* Text on accent backgrounds */
37
+ --honeydeck-accent-foreground
38
+
39
+ /* Link color */
40
+ --honeydeck-link-color /* default: inherit */
41
+
42
+ /* Border radius for cards, code blocks, buttons */
43
+ --honeydeck-border-radius
44
+ ```
45
+
46
+ ### Typography Tokens
47
+
48
+ Typography tokens are **shared across color modes** (not redefined per light/dark).
49
+
50
+ The base font size seeds `rem` for the entire slide canvas (`font-size` on `:root`). All other sizes are expressed in `rem` so they scale proportionally.
51
+
52
+ ```css
53
+ /* Font families */
54
+ --honeydeck-font-heading
55
+ --honeydeck-font-body
56
+ --honeydeck-font-mono
57
+
58
+ /* Base font size — sets :root font-size, everything scales relative to this */
59
+ --honeydeck-font-size-base /* default: 36px */
60
+
61
+ /* Scale factor for heading progression (e.g. 1.25 = major third) */
62
+ --honeydeck-font-scale /* default: 1.25 */
63
+
64
+ /* Body / paragraph text */
65
+ --honeydeck-font-size-body /* 1rem (= base) */
66
+
67
+ /* Computed heading sizes (1rem × scale^n), individually overridable */
68
+ --honeydeck-font-size-display /* 1rem × scale⁵ */
69
+ --honeydeck-font-size-h1 /* 1rem × scale⁴ */
70
+ --honeydeck-font-size-h2 /* 1rem × scale³ */
71
+ --honeydeck-font-size-h3 /* 1rem × scale² */
72
+ --honeydeck-font-size-h4 /* 1rem × scale¹ */
73
+ --honeydeck-font-size-small /* 1rem ÷ scale */
74
+
75
+ /* Code blocks */
76
+ --honeydeck-font-size-code /* 0.83rem */
77
+ ```
78
+
79
+ ### Layout Token
80
+
81
+ ```css
82
+ /* Internal padding between slide edge and layout content */
83
+ --honeydeck-slide-padding /* default: 2rem */
84
+ ```
85
+
86
+ Built-in layouts apply this via the shared `SlideFrame` wrapper. This is content padding inside the slide canvas, not a viewport inset.
87
+
88
+ ### Code Token
89
+
90
+ ```css
91
+ /* Opacity for non-highlighted lines during step-through (0–1) */
92
+ --honeydeck-code-line-dim-opacity /* default: 0.4 */
93
+ ```
94
+
95
+ > **Note:** Code block syntax highlighting colors come from Honeydeck's built-in syntax themes, not from Honeydeck tokens. Honeydeck does not expose custom syntax theme configuration. Only the dim-opacity token is part of the Honeydeck token contract; plain fallback code blocks use Honeydeck CSS tokens.
96
+
97
+ ### Light/Dark Mode
98
+
99
+ Each mode defines its own `--honeydeck-primary`. All other color tokens derive from it:
100
+
101
+ ```css
102
+ [data-honeydeck-color-mode="light"] {
103
+ --honeydeck-primary: oklch(50% 0.2 250);
104
+ --honeydeck-primary-foreground: oklch(from var(--honeydeck-primary) 98% 0.01 h);
105
+ --honeydeck-background: oklch(from var(--honeydeck-primary) 98% 0.01 h);
106
+ --honeydeck-foreground: oklch(from var(--honeydeck-primary) 15% 0.02 h);
107
+ --honeydeck-surface: oklch(from var(--honeydeck-primary) 94% 0.02 h);
108
+ --honeydeck-surface-foreground: oklch(from var(--honeydeck-primary) 20% 0.02 h);
109
+ --honeydeck-border: oklch(from var(--honeydeck-primary) 85% 0.03 h);
110
+ --honeydeck-accent: oklch(from var(--honeydeck-primary) l c calc(h + 180));
111
+ --honeydeck-accent-foreground: oklch(from var(--honeydeck-accent) 15% 0.01 h);
112
+ --honeydeck-link-color: inherit;
113
+ --honeydeck-border-radius: 0.5rem;
114
+ }
115
+
116
+ [data-honeydeck-color-mode="dark"] {
117
+ --honeydeck-primary: oklch(70% 0.2 250);
118
+ --honeydeck-primary-foreground: oklch(from var(--honeydeck-primary) 10% 0.01 h);
119
+ --honeydeck-background: oklch(from var(--honeydeck-primary) 12% 0.02 h);
120
+ --honeydeck-foreground: oklch(from var(--honeydeck-primary) 95% 0.01 h);
121
+ --honeydeck-surface: oklch(from var(--honeydeck-primary) 20% 0.03 h);
122
+ --honeydeck-surface-foreground: oklch(from var(--honeydeck-primary) 90% 0.02 h);
123
+ --honeydeck-border: oklch(from var(--honeydeck-primary) 30% 0.03 h);
124
+ --honeydeck-accent: oklch(from var(--honeydeck-primary) l c calc(h + 180));
125
+ --honeydeck-accent-foreground: oklch(from var(--honeydeck-accent) 95% 0.01 h);
126
+ --honeydeck-link-color: inherit;
127
+ --honeydeck-border-radius: 0.5rem;
128
+ }
129
+ ```
130
+
131
+ Any derived token can be explicitly overridden.
132
+
133
+ ### Tailwind Mapping
134
+
135
+ Honeydeck uses Tailwind v4 `@theme` in `base.css` to map tokens to utilities:
136
+
137
+ ```css
138
+ @theme {
139
+ --color-primary: var(--honeydeck-primary);
140
+ --color-primary-foreground: var(--honeydeck-primary-foreground);
141
+ --color-background: var(--honeydeck-background);
142
+ --color-foreground: var(--honeydeck-foreground);
143
+ --color-surface: var(--honeydeck-surface);
144
+ --color-surface-foreground: var(--honeydeck-surface-foreground);
145
+ --color-border: var(--honeydeck-border);
146
+ --color-accent: var(--honeydeck-accent);
147
+ --color-accent-foreground: var(--honeydeck-accent-foreground);
148
+ --color-link: var(--honeydeck-link-color);
149
+
150
+ --radius-honeydeck: var(--honeydeck-border-radius);
151
+ --font-heading: var(--honeydeck-font-heading);
152
+ --font-body: var(--honeydeck-font-body);
153
+ --font-mono: var(--honeydeck-font-mono);
154
+ }
155
+ ```
156
+
157
+ `base.css` also defines runtime fallback colors (`bg-error-surface`, `bg-void`, etc.) plus pixel-based spacing/text-size scales so runtime UI is not affected by the slide root font size.
158
+
159
+ Base theme CSS owns global and generated-content styling that cannot be attached at a React call site: slide Markdown typography, documentation Markdown typography, Shiki-generated code descendants, and generated plain-code fallback markup. Honeydeck-owned React component wrappers use Tailwind utilities for their own layout and visual styling while preserving stable `honeydeck-*` class hooks where runtime code, PDF export, tests, or generated descendant styling need to find them.
160
+
161
+ The base CSS also applies `--honeydeck-link-color` to slide anchors and underlines links. `cursor: pointer` is reserved for real links only: anchors with `href` (`a[href]`) in slide/doc content and runtime route links. Buttons and other non-link controls must not set `cursor: pointer`; they keep the default cursor. Theme CSS is exported as `@honeydeck/honeydeck/theme.css`, `@honeydeck/honeydeck/themes/base.css`, `@honeydeck/honeydeck/themes/clean.css`, and `@honeydeck/honeydeck/themes/bee.css`.
162
+
163
+ Usage in MDX:
164
+
165
+ ```mdx
166
+ <div className="bg-surface text-surface-foreground border border-border rounded-honeydeck">
167
+ Card content
168
+ </div>
169
+ ```
170
+
171
+ ### Color Mode Behavior
172
+
173
+ - Browser defaults to system preference
174
+ - User can toggle via navigation controls (system / light / dark)
175
+ - `colorMode:` frontmatter can pin the deck to one mode
176
+ - PDF ignores system preference; if no PDF override or pinned deck mode exists, it uses light
177
+ - PDF color mode resolves as CLI `--mode` > `pdfColorMode` > pinned `colorMode` (`light`/`dark`) > `light`
178
+ - Renderer applies: `data-honeydeck-color-mode="light"` or `data-honeydeck-color-mode="dark"`
179
+ - Base theme CSS sets browser `color-scheme` to match the effective Honeydeck color mode so built-in browser defaults and embedded rendered assets stay visually consistent in static builds and PDF export