kitfly 0.2.0 → 0.2.3

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 (126) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +25 -10
  3. package/VERSION +1 -1
  4. package/dist/_raw/content/guide/branding.md +146 -0
  5. package/dist/_raw/content/guide/data-driven-content.md +204 -0
  6. package/dist/_raw/content/reference/configuration.md +145 -7
  7. package/dist/_raw/content/reference/environment-variables.md +26 -1
  8. package/dist/_raw/content/reference/glossary.md +25 -1
  9. package/dist/_raw/content/reference/key-concepts.md +30 -2
  10. package/dist/_raw/content/reference/plugins.md +14 -0
  11. package/dist/_raw/content/reference/slides-authoring-guidelines.md +129 -0
  12. package/dist/_raw/content/reference.md +1 -0
  13. package/dist/_raw/docs/decisions/ADR-0006-data-driven-content.md +350 -0
  14. package/dist/content/deployment/preflight.html +10 -6
  15. package/dist/content/deployment/recipes/aws-s3.html +10 -6
  16. package/dist/content/deployment/recipes/cloudflare-pages.html +10 -6
  17. package/dist/content/deployment/recipes/cloudflare-r2.html +10 -6
  18. package/dist/content/deployment/recipes/fly-io.html +10 -6
  19. package/dist/content/deployment/recipes/github-pages.html +10 -6
  20. package/dist/content/deployment/recipes/netlify.html +10 -6
  21. package/dist/content/deployment/recipes/vercel.html +10 -6
  22. package/dist/content/deployment/secrets-and-env-vars.html +10 -6
  23. package/dist/content/deployment.html +10 -6
  24. package/dist/content/guide/approaches.html +10 -6
  25. package/dist/content/guide/branding.html +510 -0
  26. package/dist/content/guide/data-driven-content.html +543 -0
  27. package/dist/content/guide/features.html +10 -6
  28. package/dist/content/guide/getting-started.html +10 -6
  29. package/dist/content/guide/kitfly-overview.html +10 -6
  30. package/dist/content/reference/configuration.html +135 -9
  31. package/dist/content/reference/design-catalog.html +10 -6
  32. package/dist/content/reference/environment-variables.html +50 -8
  33. package/dist/content/reference/glossary.html +24 -8
  34. package/dist/content/reference/key-concepts.html +33 -9
  35. package/dist/content/reference/plugins.html +22 -7
  36. package/dist/content/reference/slides-authoring-guidelines.html +422 -0
  37. package/dist/content/reference/structure.html +10 -6
  38. package/dist/content/reference.html +11 -6
  39. package/dist/content/templates/crucible.html +10 -6
  40. package/dist/content/templates/handbook.html +10 -6
  41. package/dist/content/templates/minimal.html +10 -6
  42. package/dist/content/templates/overview.html +10 -6
  43. package/dist/content/templates/pipeline.html +10 -6
  44. package/dist/content/templates/productbook.html +10 -6
  45. package/dist/content/templates/runbook.html +10 -6
  46. package/dist/content/templates/servicebook.html +10 -6
  47. package/dist/content-index.json +38 -2
  48. package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +10 -6
  49. package/dist/docs/decisions/ADR-0002-ai-accessibility.html +10 -6
  50. package/dist/docs/decisions/ADR-0003-single-file-bundle.html +10 -6
  51. package/dist/docs/decisions/ADR-0004-bun-runtime.html +10 -6
  52. package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +10 -6
  53. package/dist/docs/decisions/ADR-0006-data-driven-content.html +752 -0
  54. package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +10 -6
  55. package/dist/docs/decisions/DDR-0002-theme-system.html +10 -6
  56. package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +10 -6
  57. package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +10 -6
  58. package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +10 -6
  59. package/dist/docs/userguide/cli/build.html +10 -6
  60. package/dist/docs/userguide/cli/bundle.html +10 -6
  61. package/dist/docs/userguide/cli/dev.html +10 -6
  62. package/dist/docs/userguide/cli/init.html +10 -6
  63. package/dist/docs/userguide/cli/servers.html +10 -6
  64. package/dist/docs/userguide/cli/stop.html +10 -6
  65. package/dist/docs/userguide/cli/update.html +10 -6
  66. package/dist/docs/userguide/cli/version.html +10 -6
  67. package/dist/docs/userguide/cli.html +10 -6
  68. package/dist/docs/userguide/sharing.html +10 -6
  69. package/dist/index.html +10 -6
  70. package/dist/llms.txt +3 -3
  71. package/dist/provenance.json +4 -4
  72. package/dist/schemas/plugin-registry.schema.html +10 -6
  73. package/dist/schemas/plugin-schemas-notes.html +10 -6
  74. package/dist/schemas/plugin.schema.html +10 -6
  75. package/dist/schemas/plugins.schema.html +10 -6
  76. package/dist/schemas/v0/common.schema.html +14 -10
  77. package/dist/schemas/v0/plugin-registry.schema.html +13 -9
  78. package/dist/schemas/v0/plugin.schema.html +13 -9
  79. package/dist/schemas/v0/plugins.schema.html +13 -9
  80. package/dist/schemas/v0/site.schema.html +67 -7
  81. package/dist/schemas/v0/theme.schema.html +21 -17
  82. package/dist/schemas.html +10 -6
  83. package/dist/styles.css +39 -4
  84. package/package.json +1 -1
  85. package/plugins-dist/latex-runtime.js +140 -0
  86. package/plugins-dist/latex.js +178 -0
  87. package/plugins-dist/slides-charts-lite-runtime.js +179 -0
  88. package/plugins-dist/slides-charts-lite.js +198 -0
  89. package/plugins-dist/slides-visuals.css +166 -0
  90. package/plugins-dist/slides-visuals.js +124 -33
  91. package/registry/plugins.yaml +30 -5
  92. package/schemas/v0/site.schema.json +56 -0
  93. package/scripts/build.ts +195 -70
  94. package/scripts/bundle.ts +122 -11
  95. package/scripts/dev.ts +345 -178
  96. package/src/__tests__/brief.test.ts +151 -0
  97. package/src/__tests__/build.test.ts +234 -4
  98. package/src/__tests__/bundle.test.ts +134 -0
  99. package/src/__tests__/dev-plugin-errors.test.ts +20 -0
  100. package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-branching-no-source.md +5 -0
  101. package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-converging-no-target.md +6 -0
  102. package/src/__tests__/fixtures/fences/slides-visuals/invalid/staircase-empty-steps.md +3 -0
  103. package/src/__tests__/fixtures/fences/slides-visuals/invalid/timeline-horizontal-no-events.md +2 -0
  104. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching-no-split.md +7 -0
  105. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching.md +8 -0
  106. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging-no-merge.md +7 -0
  107. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging.md +8 -0
  108. package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase-down.md +7 -0
  109. package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase.md +8 -0
  110. package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-horizontal.md +9 -0
  111. package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-vertical.md +10 -0
  112. package/src/__tests__/init.test.ts +51 -2
  113. package/src/__tests__/latex-runtime.bun.test.ts +35 -0
  114. package/src/__tests__/shared.test.ts +621 -1
  115. package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
  116. package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +33 -0
  117. package/src/cli.ts +11 -4
  118. package/src/commands/init.ts +1 -1
  119. package/src/shared.ts +761 -18
  120. package/src/site/styles.css +39 -4
  121. package/src/site/template.html +5 -2
  122. package/src/templates/brief.ts +486 -0
  123. package/src/templates/deck.ts +59 -0
  124. package/src/templates/driver.ts +46 -13
  125. package/src/templates/handbook.ts +32 -0
  126. package/src/templates/runbook.ts +32 -0
@@ -0,0 +1,510 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Branding - Kitfly Docs</title>
7
+ <link rel="icon" type="image/png" sizes="32x32" href="../../assets/brand/kitfly-favicon-32.png">
8
+ <link rel="icon" type="image/png" sizes="64x64" href="../../assets/brand/kitfly-neon-256.png">
9
+ <link rel="stylesheet" href="../../styles.css">
10
+ <style id="kitfly-theme">
11
+ :root { --color-bg: #ffffff;
12
+ --color-bg-sidebar: #f5f7f8;
13
+ --color-text: #374151;
14
+ --color-text-muted: #6b7280;
15
+ --color-border: #e5e7eb;
16
+ --color-link: #007182;
17
+ --color-link-hover: #0a6172;
18
+ --color-accent: #152F46;
19
+ --color-code-bg: #f5f7f8;
20
+ --color-logo: #152F46;
21
+ --sidebar-width: 280px;
22
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
23
+ --font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
24
+ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
25
+ html { font-size: 16px; }
26
+ @media (prefers-color-scheme: dark) {
27
+ :root:not([data-theme="light"]) { --color-bg: #0d1117;
28
+ --color-bg-sidebar: #152F46;
29
+ --color-text: #e5e7eb;
30
+ --color-text-muted: #9ca3af;
31
+ --color-border: #374151;
32
+ --color-link: #709EA6;
33
+ --color-link-hover: #8fb5bc;
34
+ --color-accent: #f9fafb;
35
+ --color-code-bg: #152F46;
36
+ --color-logo: #f9fafb; }
37
+ }
38
+ [data-theme="dark"] { --color-bg: #0d1117;
39
+ --color-bg-sidebar: #152F46;
40
+ --color-text: #e5e7eb;
41
+ --color-text-muted: #9ca3af;
42
+ --color-border: #374151;
43
+ --color-link: #709EA6;
44
+ --color-link-hover: #8fb5bc;
45
+ --color-accent: #f9fafb;
46
+ --color-code-bg: #152F46;
47
+ --color-logo: #f9fafb; }
48
+ [data-theme="light"] { --color-bg: #ffffff;
49
+ --color-bg-sidebar: #f5f7f8;
50
+ --color-text: #374151;
51
+ --color-text-muted: #6b7280;
52
+ --color-border: #e5e7eb;
53
+ --color-link: #007182;
54
+ --color-link-hover: #0a6172;
55
+ --color-accent: #152F46;
56
+ --color-code-bg: #f5f7f8;
57
+ --color-logo: #152F46;
58
+ --sidebar-width: 280px;
59
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
60
+ --font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
61
+ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
62
+ </style>
63
+ <!-- Syntax highlighting - Prism.js -->
64
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.min.css" id="prism-light">
65
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-okaidia.min.css" id="prism-dark" disabled>
66
+
67
+ <script>
68
+ // Apply saved theme immediately to prevent flash
69
+ (function() {
70
+ const saved = localStorage.getItem('theme');
71
+ if (saved) {
72
+ document.documentElement.setAttribute('data-theme', saved);
73
+ }
74
+ // Set Prism theme based on saved or system preference
75
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
76
+ const isDark = saved === 'dark' || (!saved && prefersDark);
77
+ if (isDark) {
78
+ document.getElementById('prism-light')?.setAttribute('disabled', '');
79
+ document.getElementById('prism-dark')?.removeAttribute('disabled');
80
+ }
81
+ })();
82
+ </script>
83
+ </head>
84
+ <body class="mode-docs">
85
+ <div class="mobile-header">
86
+ <button class="nav-toggle" onclick="toggleNav()" aria-label="Toggle navigation">
87
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
88
+ <path d="M3 12h18M3 6h18M3 18h18"/>
89
+ </svg>
90
+ </button>
91
+ <a href="../../" class="mobile-logo" title="Home" data-initial="K">
92
+ <img src="../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img logo-icon" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')">
93
+ </a>
94
+ <button class="mobile-theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
95
+ <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
96
+ <circle cx="12" cy="12" r="5"/>
97
+ <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
98
+ </svg>
99
+ <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
100
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
101
+ </svg>
102
+ </button>
103
+ </div>
104
+ <div class="layout">
105
+ <nav class="sidebar">
106
+ <div class="sidebar-header">
107
+ <div class="logo logo-icon">
108
+ <a href="/" class="logo-icon" data-initial="K">
109
+ <img src="../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')">
110
+ </a>
111
+ <span class="logo-text">
112
+ <a href="/" class="brand">Kitfly</a>
113
+ <a href="../../" class="product">Kitfly Docs</a>
114
+ </span>
115
+ </div>
116
+ <div class="header-tools">
117
+ <button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
118
+ <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
119
+ <circle cx="12" cy="12" r="5"/>
120
+ <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
121
+ </svg>
122
+ <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
123
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
124
+ </svg>
125
+ </button>
126
+ <div class="sidebar-meta">
127
+ <span class="meta-version">v0.2.3</span>
128
+ <span class="meta-branch">HEAD</span>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ <div class="sidebar-nav">
133
+ <ul><li><a href="../../index.html" class="nav-home">Home</a></li><li><span class="nav-section">Guide</span><ul><li><a href="../../content/guide/approaches.html">approaches</a></li><li><a href="../../content/guide/branding.html" class="active">branding</a></li><li><a href="../../content/guide/data-driven-content.html">data-driven-content</a></li><li><a href="../../content/guide/features.html">features</a></li><li><a href="../../content/guide/getting-started.html">getting-started</a></li><li><a href="../../content/guide/kitfly-overview.html">kitfly-overview</a></li></ul></li><li><span class="nav-section">Templates</span><ul><li><a href="../../content/templates/crucible.html">crucible</a></li><li><a href="../../content/templates/handbook.html">handbook</a></li><li><a href="../../content/templates/minimal.html">minimal</a></li><li><a href="../../content/templates/overview.html">overview</a></li><li><a href="../../content/templates/pipeline.html">pipeline</a></li><li><a href="../../content/templates/productbook.html">productbook</a></li><li><a href="../../content/templates/runbook.html">runbook</a></li><li><a href="../../content/templates/servicebook.html">servicebook</a></li></ul></li><li><a href="../../content/reference.html" class="nav-section">Reference</a><ul><li><a href="../../content/reference/configuration.html">configuration</a></li><li><a href="../../content/reference/design-catalog.html">design-catalog</a></li><li><a href="../../content/reference/environment-variables.html">environment-variables</a></li><li><a href="../../content/reference/glossary.html">glossary</a></li><li><a href="../../content/reference/key-concepts.html">key-concepts</a></li><li><a href="../../content/reference/plugins.html">plugins</a></li><li><a href="../../content/reference/slides-authoring-guidelines.html">slides-authoring-guidelines</a></li><li><a href="../../content/reference/structure.html">structure</a></li></ul></li><li><a href="../../content/deployment.html" class="nav-section">Deployment</a><ul><li><a href="../../content/deployment/preflight.html">preflight</a></li><li><details><summary class="nav-group">recipes</summary><ul><li><a href="../../content/deployment/recipes/aws-s3.html">aws-s3</a></li><li><a href="../../content/deployment/recipes/cloudflare-pages.html">cloudflare-pages</a></li><li><a href="../../content/deployment/recipes/cloudflare-r2.html">cloudflare-r2</a></li><li><a href="../../content/deployment/recipes/fly-io.html">fly-io</a></li><li><a href="../../content/deployment/recipes/github-pages.html">github-pages</a></li><li><a href="../../content/deployment/recipes/netlify.html">netlify</a></li><li><a href="../../content/deployment/recipes/vercel.html">vercel</a></li></ul></details></li><li><a href="../../content/deployment/secrets-and-env-vars.html">secrets-and-env-vars</a></li></ul></li><li><span class="nav-section">User Guide</span><ul><li><details><summary class="nav-group"><a href="../../docs/userguide/cli.html">cli</a></summary><ul><li><a href="../../docs/userguide/cli/build.html">build</a></li><li><a href="../../docs/userguide/cli/bundle.html">bundle</a></li><li><a href="../../docs/userguide/cli/dev.html">dev</a></li><li><a href="../../docs/userguide/cli/init.html">init</a></li><li><a href="../../docs/userguide/cli/servers.html">servers</a></li><li><a href="../../docs/userguide/cli/stop.html">stop</a></li><li><a href="../../docs/userguide/cli/update.html">update</a></li><li><a href="../../docs/userguide/cli/version.html">version</a></li></ul></details></li><li><a href="../../docs/userguide/sharing.html">sharing</a></li></ul></li><li><span class="nav-section">Decisions</span><ul><li><a href="../../docs/decisions/ADR-0001-minimalist-site-code.html">ADR-0001-minimalist-site-code</a></li><li><a href="../../docs/decisions/ADR-0002-ai-accessibility.html">ADR-0002-ai-accessibility</a></li><li><a href="../../docs/decisions/ADR-0003-single-file-bundle.html">ADR-0003-single-file-bundle</a></li><li><a href="../../docs/decisions/ADR-0004-bun-runtime.html">ADR-0004-bun-runtime</a></li><li><a href="../../docs/decisions/ADR-0005-plugin-contract-and-distribution.html">ADR-0005-plugin-contract-and-distribution</a></li><li><a href="../../docs/decisions/ADR-0006-data-driven-content.html">ADR-0006-data-driven-content</a></li><li><a href="../../docs/decisions/DDR-0001-viewport-locked-layout.html">DDR-0001-viewport-locked-layout</a></li><li><a href="../../docs/decisions/DDR-0002-theme-system.html">DDR-0002-theme-system</a></li><li><a href="../../docs/decisions/DDR-0003-bounded-logo-slot.html">DDR-0003-bounded-logo-slot</a></li><li><a href="../../docs/decisions/DDR-0004-slides-rendering-model.html">DDR-0004-slides-rendering-model</a></li><li><a href="../../docs/decisions/DDR-0005-deterministic-layout-boundary.html">DDR-0005-deterministic-layout-boundary</a></li></ul></li><li><a href="../../schemas.html" class="nav-section">Schemas</a><ul><li><a href="../../schemas/plugin-registry.schema.html">plugin-registry.schema</a></li><li><a href="../../schemas/plugin-schemas-notes.html">plugin-schemas-notes</a></li><li><a href="../../schemas/plugin.schema.html">plugin.schema</a></li><li><a href="../../schemas/plugins.schema.html">plugins.schema</a></li><li><details><summary class="nav-group">v0</summary><ul><li><a href="../../schemas/v0/common.schema.html">common.schema</a></li><li><a href="../../schemas/v0/plugin-registry.schema.html">plugin-registry.schema</a></li><li><a href="../../schemas/v0/plugin.schema.html">plugin.schema</a></li><li><a href="../../schemas/v0/plugins.schema.html">plugins.schema</a></li><li><a href="../../schemas/v0/site.schema.html">site.schema</a></li><li><a href="../../schemas/v0/theme.schema.html">theme.schema</a></li></ul></details></li></ul></li></ul>
134
+ </div>
135
+ </nav>
136
+ <main class="content">
137
+ <article class="prose">
138
+ <nav class="breadcrumbs"><a href="../../content/guide/approaches.html">Content</a><span class="separator">›</span><a href="../../content/guide/approaches.html">Guide</a><span class="separator">›</span><span>branding</span></nav>
139
+
140
+ <h1 id="branding">Branding</h1>
141
+ <p>Kitfly sites display your brand in two locations: the <strong>header</strong> (sidebar logo + site title) and the <strong>footer ribbon</strong> (optional logo, copyright, links). Both support light/dark mode variants.</p>
142
+ <h2 id="header-logo">Header Logo</h2>
143
+ <p>The header logo appears in the sidebar above the navigation. Configure it in <code>site.yaml</code>:</p>
144
+ <pre><code class="language-yaml">brand:
145
+ name: &quot;My Project&quot;
146
+ url: &quot;/&quot;
147
+ logo: &quot;assets/brand/logo.png&quot;
148
+ </code></pre>
149
+ <p>The logo renders inside a bounded slot that preserves aspect ratio. Both square icons and wide wordmarks work — set <code>logoType</code> to tell kitfly which you&#39;re using:</p>
150
+ <pre><code class="language-yaml">brand:
151
+ logo: &quot;assets/brand/logo.png&quot;
152
+ logoType: &quot;icon&quot; # square mark (default)
153
+ </code></pre>
154
+ <pre><code class="language-yaml">brand:
155
+ logo: &quot;assets/brand/wordmark.svg&quot;
156
+ logoType: &quot;wordmark&quot; # wide logo
157
+ </code></pre>
158
+ <table>
159
+ <thead>
160
+ <tr>
161
+ <th>Breakpoint</th>
162
+ <th>Max Height</th>
163
+ <th>Max Width</th>
164
+ </tr>
165
+ </thead>
166
+ <tbody><tr>
167
+ <td>Desktop</td>
168
+ <td>64px</td>
169
+ <td>180px</td>
170
+ </tr>
171
+ <tr>
172
+ <td>Tablet (≤1024px)</td>
173
+ <td>56px</td>
174
+ <td>150px</td>
175
+ </tr>
176
+ <tr>
177
+ <td>Mobile (≤768px)</td>
178
+ <td>48px</td>
179
+ <td>130px</td>
180
+ </tr>
181
+ </tbody></table>
182
+ <p>SVG and PNG formats are supported. For SVGs, ensure the <code>viewBox</code> is tightly cropped to the artwork.</p>
183
+ <p>If the logo image is missing or fails to load, kitfly falls back to displaying the first letter of your brand name.</p>
184
+ <h2 id="footer-logo">Footer Logo</h2>
185
+ <p>Add a logo to the footer ribbon — typically a parent company, client, or partner logo that differs from the header brand:</p>
186
+ <pre><code class="language-yaml">footer:
187
+ logo: &quot;assets/brand/footer-logo.png&quot;
188
+ </code></pre>
189
+ <p>The footer logo renders at the leading edge of the ribbon, before the version and publish date.</p>
190
+ <h3 id="footer-logo-options">Footer Logo Options</h3>
191
+ <table>
192
+ <thead>
193
+ <tr>
194
+ <th>Field</th>
195
+ <th>Default</th>
196
+ <th>Description</th>
197
+ </tr>
198
+ </thead>
199
+ <tbody><tr>
200
+ <td><code>logo</code></td>
201
+ <td><em>(none)</em></td>
202
+ <td>Path to footer logo image</td>
203
+ </tr>
204
+ <tr>
205
+ <td><code>logoUrl</code></td>
206
+ <td><em>(none)</em></td>
207
+ <td>Make the logo a clickable link</td>
208
+ </tr>
209
+ <tr>
210
+ <td><code>logoAlt</code></td>
211
+ <td>Copyright text or brand name</td>
212
+ <td>Alt text for accessibility</td>
213
+ </tr>
214
+ <tr>
215
+ <td><code>logoHeight</code></td>
216
+ <td><code>20</code></td>
217
+ <td>Max height in pixels (range: 10-40)</td>
218
+ </tr>
219
+ </tbody></table>
220
+ <pre><code class="language-yaml">footer:
221
+ logo: &quot;assets/brand/footer-logo.png&quot;
222
+ logoUrl: &quot;https://example.com&quot;
223
+ logoAlt: &quot;Example Corp&quot;
224
+ logoHeight: 24
225
+ </code></pre>
226
+ <p>If no <code>footer.logo</code> is set, the footer renders as text only (version, copyright, links, attribution) — no change from the default.</p>
227
+ <h2 id="dark-mode-variants">Dark Mode Variants</h2>
228
+ <p>By default, kitfly auto-adjusts logo brightness in dark mode using a CSS filter. This works for many logos but can distort brand colors or fail on dark logos with transparent backgrounds.</p>
229
+ <p>For precise control, provide a separate dark mode image:</p>
230
+ <h3 id="header">Header</h3>
231
+ <pre><code class="language-yaml">brand:
232
+ logo: &quot;assets/brand/logo.png&quot;
233
+ logoDark: &quot;assets/brand/logo-dark.png&quot;
234
+ </code></pre>
235
+ <h3 id="footer">Footer</h3>
236
+ <pre><code class="language-yaml">footer:
237
+ logo: &quot;assets/brand/footer-logo.png&quot;
238
+ logoDark: &quot;assets/brand/footer-logo-dark.png&quot;
239
+ </code></pre>
240
+ <h3 id="how-it-works">How It Works</h3>
241
+ <p>When <code>logoDark</code> is set, kitfly emits both images and uses CSS to show the correct one based on the active theme. No JavaScript is involved — the swap is instant on theme toggle.</p>
242
+ <table>
243
+ <thead>
244
+ <tr>
245
+ <th>Configuration</th>
246
+ <th>Light Mode</th>
247
+ <th>Dark Mode</th>
248
+ </tr>
249
+ </thead>
250
+ <tbody><tr>
251
+ <td><code>logo</code> only</td>
252
+ <td>Shows logo</td>
253
+ <td>Shows logo with brightness filter</td>
254
+ </tr>
255
+ <tr>
256
+ <td><code>logo</code> + <code>logoDark</code></td>
257
+ <td>Shows logo</td>
258
+ <td>Shows logoDark (no filter)</td>
259
+ </tr>
260
+ </tbody></table>
261
+ <h3 id="recommendations">Recommendations</h3>
262
+ <ul>
263
+ <li>Use the <strong>single logo</strong> approach when your logo works on both light and dark backgrounds (e.g. a colorful icon on transparent background)</li>
264
+ <li>Use <strong>light + dark variants</strong> when your logo has a specific background assumption (e.g. dark wordmark that disappears on dark backgrounds)</li>
265
+ <li>Keep both variants at the <strong>same dimensions</strong> so the layout doesn&#39;t shift on theme toggle</li>
266
+ <li>SVG is ideal for both variants — resolution-independent and small file size</li>
267
+ </ul>
268
+ <h2 id="recommended-asset-files">Recommended Asset Files</h2>
269
+ <table>
270
+ <thead>
271
+ <tr>
272
+ <th>Asset</th>
273
+ <th>Location</th>
274
+ <th>Size / Format</th>
275
+ </tr>
276
+ </thead>
277
+ <tbody><tr>
278
+ <td>Logo (light)</td>
279
+ <td><code>assets/brand/logo.png</code></td>
280
+ <td>200x50px or SVG</td>
281
+ </tr>
282
+ <tr>
283
+ <td>Logo (dark)</td>
284
+ <td><code>assets/brand/logo-dark.png</code></td>
285
+ <td>Same as logo</td>
286
+ </tr>
287
+ <tr>
288
+ <td>Footer logo (light)</td>
289
+ <td><code>assets/brand/footer-logo.png</code></td>
290
+ <td>Max height 20-40px</td>
291
+ </tr>
292
+ <tr>
293
+ <td>Footer logo (dark)</td>
294
+ <td><code>assets/brand/footer-logo-dark.png</code></td>
295
+ <td>Same as footer logo</td>
296
+ </tr>
297
+ <tr>
298
+ <td>Favicon</td>
299
+ <td><code>assets/brand/favicon.ico</code></td>
300
+ <td>32x32px</td>
301
+ </tr>
302
+ </tbody></table>
303
+ <h2 id="full-example">Full Example</h2>
304
+ <p>A site with separate header and footer logos, both with dark mode variants:</p>
305
+ <pre><code class="language-yaml"># site.yaml
306
+ title: &quot;Product Brief&quot;
307
+
308
+ brand:
309
+ name: &quot;Product Name&quot;
310
+ url: &quot;/&quot;
311
+ logo: &quot;assets/brand/product-logo.png&quot;
312
+ logoDark: &quot;assets/brand/product-logo-dark.png&quot;
313
+ logoType: &quot;wordmark&quot;
314
+
315
+ footer:
316
+ copyright: &quot;© 2026 Parent Company, Inc.&quot;
317
+ copyrightUrl: &quot;https://example.com&quot;
318
+ logo: &quot;assets/brand/parent-logo.png&quot;
319
+ logoDark: &quot;assets/brand/parent-logo-dark.png&quot;
320
+ logoAlt: &quot;Parent Company&quot;
321
+ logoHeight: 20
322
+ attribution: true
323
+ </code></pre>
324
+
325
+ </article>
326
+ <aside class="toc"><span class="toc-title">On this page</span><ul><li><a href="#header-logo">Header Logo</a></li><li><a href="#footer-logo">Footer Logo</a></li><li class="toc-h3"><a href="#footer-logo-options">Footer Logo Options</a></li><li><a href="#dark-mode-variants">Dark Mode Variants</a></li><li class="toc-h3"><a href="#header">Header</a></li><li class="toc-h3"><a href="#footer">Footer</a></li><li class="toc-h3"><a href="#how-it-works">How It Works</a></li><li class="toc-h3"><a href="#recommendations">Recommendations</a></li><li><a href="#recommended-asset-files">Recommended Asset Files</a></li><li><a href="#full-example">Full Example</a></li></ul></aside>
327
+ </main>
328
+ </div>
329
+
330
+ <footer class="site-footer">
331
+ <div class="footer-content">
332
+ <div class="footer-left">
333
+
334
+ <span class="footer-version">v0.2.3</span>
335
+ <span class="footer-separator">·</span>
336
+ <span class="footer-commit" title="Commit: 664328f">Published 2026-02-18</span>
337
+ </div>
338
+ <div class="footer-center">
339
+ <span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
340
+ <span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
341
+ </div>
342
+ <div class="footer-right">
343
+ <a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
344
+ </div>
345
+ </div>
346
+ </footer>
347
+ <!-- Syntax highlighting - Prism.js -->
348
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
349
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
350
+ <!-- Mermaid diagram support -->
351
+ <script type="module">
352
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
353
+
354
+ function getMermaidTheme() {
355
+ const theme = document.documentElement.getAttribute('data-theme');
356
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
357
+ const isDark = theme === 'dark' || (!theme && prefersDark);
358
+ return isDark ? 'dark' : 'neutral';
359
+ }
360
+
361
+ mermaid.initialize({
362
+ startOnLoad: true,
363
+ theme: getMermaidTheme()
364
+ });
365
+
366
+ // Re-render mermaid diagrams when theme changes
367
+ window.reinitMermaid = async function() {
368
+ mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
369
+ const diagrams = document.querySelectorAll('.mermaid');
370
+ for (const el of diagrams) {
371
+ const code = el.getAttribute('data-mermaid-source');
372
+ if (code) {
373
+ el.innerHTML = code;
374
+ el.removeAttribute('data-processed');
375
+ }
376
+ }
377
+ await mermaid.run({ nodes: diagrams });
378
+ };
379
+ </script>
380
+
381
+ <script>
382
+ function toggleTheme() {
383
+ const html = document.documentElement;
384
+ const current = html.getAttribute('data-theme');
385
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
386
+
387
+ let next;
388
+ if (current === 'dark') {
389
+ next = 'light';
390
+ } else if (current === 'light') {
391
+ next = 'dark';
392
+ } else {
393
+ // No explicit theme set, toggle from system preference
394
+ next = prefersDark ? 'light' : 'dark';
395
+ }
396
+
397
+ html.setAttribute('data-theme', next);
398
+ localStorage.setItem('theme', next);
399
+
400
+ // Switch Prism theme
401
+ const prismLight = document.getElementById('prism-light');
402
+ const prismDark = document.getElementById('prism-dark');
403
+ if (next === 'dark') {
404
+ prismLight?.setAttribute('disabled', '');
405
+ prismDark?.removeAttribute('disabled');
406
+ } else {
407
+ prismLight?.removeAttribute('disabled');
408
+ prismDark?.setAttribute('disabled', '');
409
+ }
410
+
411
+ // Re-render mermaid diagrams with new theme
412
+ if (window.reinitMermaid) {
413
+ window.reinitMermaid();
414
+ }
415
+ if (window.reinitCharts) {
416
+ window.reinitCharts();
417
+ }
418
+ }
419
+
420
+ // Slides mode hash routing
421
+ (function initSlidesMode() {
422
+ const shell = document.querySelector('.slides-shell');
423
+ if (!shell) return;
424
+
425
+ const slides = Array.from(document.querySelectorAll('.slide'));
426
+ if (!slides.length) return;
427
+
428
+ const prevBtn = document.querySelector('.slide-prev');
429
+ const nextBtn = document.querySelector('.slide-next');
430
+ const counter = document.querySelector('.slide-counter');
431
+ const progressBar = document.querySelector('.slide-progress-bar');
432
+ const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
433
+ let current = 0;
434
+
435
+ function setActive(n) {
436
+ current = Math.max(0, Math.min(n, slides.length - 1));
437
+ slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
438
+ navLinks.forEach((link) => {
439
+ const active = link.getAttribute('href') === '#' + slides[current].id;
440
+ link.classList.toggle('active', active);
441
+ });
442
+ if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
443
+ if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
444
+ if (prevBtn) prevBtn.disabled = current === 0;
445
+ if (nextBtn) nextBtn.disabled = current === slides.length - 1;
446
+ history.replaceState(null, '', '#' + slides[current].id);
447
+ }
448
+
449
+ function setFromHash() {
450
+ const hash = window.location.hash || '';
451
+ const idx = slides.findIndex((s) => '#' + s.id === hash);
452
+ if (idx >= 0) setActive(idx);
453
+ else setActive(0);
454
+ }
455
+
456
+ prevBtn?.addEventListener('click', () => setActive(current - 1));
457
+ nextBtn?.addEventListener('click', () => setActive(current + 1));
458
+
459
+ document.addEventListener('keydown', (e) => {
460
+ if (e.key === 'ArrowRight' || e.key === ' ') {
461
+ e.preventDefault();
462
+ setActive(current + 1);
463
+ } else if (e.key === 'ArrowLeft') {
464
+ e.preventDefault();
465
+ setActive(current - 1);
466
+ } else if (e.key === 'Home') {
467
+ e.preventDefault();
468
+ setActive(0);
469
+ } else if (e.key === 'End') {
470
+ e.preventDefault();
471
+ setActive(slides.length - 1);
472
+ }
473
+ });
474
+
475
+ window.addEventListener('hashchange', setFromHash);
476
+ setFromHash();
477
+ })();
478
+
479
+ // Copy code button
480
+ document.querySelectorAll('.prose pre code').forEach(block => {
481
+ const button = document.createElement('button');
482
+ button.className = 'copy-button';
483
+ button.textContent = 'Copy';
484
+ button.onclick = async () => {
485
+ await navigator.clipboard.writeText(block.textContent);
486
+ button.textContent = 'Copied!';
487
+ setTimeout(() => button.textContent = 'Copy', 2000);
488
+ };
489
+ block.parentElement.appendChild(button);
490
+ });
491
+
492
+ // Mobile nav toggle
493
+ function toggleNav() {
494
+ document.querySelector('.sidebar').classList.toggle('open');
495
+ }
496
+
497
+ // Close nav when clicking outside on mobile
498
+ document.addEventListener('click', (e) => {
499
+ const sidebar = document.querySelector('.sidebar');
500
+ const toggle = document.querySelector('.nav-toggle');
501
+ if (sidebar.classList.contains('open') &&
502
+ !sidebar.contains(e.target) &&
503
+ !toggle.contains(e.target)) {
504
+ sidebar.classList.remove('open');
505
+ }
506
+ });
507
+ </script>
508
+
509
+ </body>
510
+ </html>