kitfly 0.1.2 → 0.2.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 (194) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +63 -16
  3. package/VERSION +1 -1
  4. package/dist/_raw/content/deployment/preflight.md +134 -0
  5. package/dist/_raw/content/deployment/recipes/aws-s3.md +128 -0
  6. package/dist/_raw/content/deployment/recipes/cloudflare-pages.md +73 -0
  7. package/dist/_raw/content/deployment/recipes/cloudflare-r2.md +156 -0
  8. package/dist/_raw/content/deployment/recipes/fly-io.md +57 -0
  9. package/dist/_raw/content/deployment/recipes/github-pages.md +112 -0
  10. package/dist/_raw/content/deployment/recipes/netlify.md +99 -0
  11. package/dist/_raw/content/deployment/recipes/vercel.md +88 -0
  12. package/dist/_raw/content/deployment/secrets-and-env-vars.md +75 -0
  13. package/dist/_raw/content/deployment.md +128 -0
  14. package/dist/_raw/content/guide/approaches.md +182 -0
  15. package/dist/_raw/content/guide/features.md +121 -0
  16. package/dist/_raw/content/guide/getting-started.md +112 -0
  17. package/dist/_raw/content/guide/kitfly-overview.md +209 -0
  18. package/dist/_raw/content/reference/configuration.md +259 -0
  19. package/dist/_raw/content/reference/design-catalog.md +167 -0
  20. package/dist/_raw/content/reference/environment-variables.md +66 -0
  21. package/dist/_raw/content/reference/glossary.md +92 -0
  22. package/dist/_raw/content/reference/key-concepts.md +118 -0
  23. package/dist/_raw/content/reference/plugins.md +220 -0
  24. package/dist/_raw/content/reference/structure.md +166 -0
  25. package/dist/_raw/content/reference.md +19 -0
  26. package/dist/_raw/content/templates/crucible.md +192 -0
  27. package/dist/_raw/content/templates/handbook.md +83 -0
  28. package/dist/_raw/content/templates/minimal.md +138 -0
  29. package/dist/_raw/content/templates/overview.md +187 -0
  30. package/dist/_raw/content/templates/pipeline.md +151 -0
  31. package/dist/_raw/content/templates/productbook.md +187 -0
  32. package/dist/_raw/content/templates/runbook.md +193 -0
  33. package/dist/_raw/content/templates/servicebook.md +163 -0
  34. package/dist/_raw/docs/decisions/ADR-0001-minimalist-site-code.md +118 -0
  35. package/dist/_raw/docs/decisions/ADR-0002-ai-accessibility.md +153 -0
  36. package/dist/_raw/docs/decisions/ADR-0003-single-file-bundle.md +93 -0
  37. package/dist/_raw/docs/decisions/ADR-0004-bun-runtime.md +98 -0
  38. package/dist/_raw/docs/decisions/ADR-0005-plugin-contract-and-distribution.md +110 -0
  39. package/dist/_raw/docs/decisions/DDR-0001-viewport-locked-layout.md +111 -0
  40. package/dist/_raw/docs/decisions/DDR-0002-theme-system.md +131 -0
  41. package/dist/_raw/docs/decisions/DDR-0003-bounded-logo-slot.md +106 -0
  42. package/dist/_raw/docs/decisions/DDR-0004-slides-rendering-model.md +113 -0
  43. package/dist/_raw/docs/decisions/DDR-0005-deterministic-layout-boundary.md +107 -0
  44. package/dist/_raw/docs/userguide/cli/build.md +85 -0
  45. package/dist/_raw/docs/userguide/cli/bundle.md +81 -0
  46. package/dist/_raw/docs/userguide/cli/dev.md +92 -0
  47. package/dist/_raw/docs/userguide/cli/init.md +116 -0
  48. package/dist/_raw/docs/userguide/cli/servers.md +69 -0
  49. package/dist/_raw/docs/userguide/cli/stop.md +76 -0
  50. package/dist/_raw/docs/userguide/cli/update.md +78 -0
  51. package/dist/_raw/docs/userguide/cli/version.md +65 -0
  52. package/dist/_raw/docs/userguide/cli.md +34 -0
  53. package/dist/_raw/docs/userguide/sharing.md +94 -0
  54. package/dist/_raw/schemas/plugin-schemas-notes.md +71 -0
  55. package/dist/_raw/schemas.md +42 -0
  56. package/dist/assets/brand/kitfly-favicon-32.png +0 -0
  57. package/dist/assets/brand/kitfly-icon-64.png +0 -0
  58. package/dist/assets/brand/kitfly-logo-128.png +0 -0
  59. package/dist/assets/brand/kitfly-logo-512.png +0 -0
  60. package/dist/assets/brand/kitfly-logo.svg +12132 -0
  61. package/dist/assets/brand/kitfly-neon-128.png +0 -0
  62. package/dist/assets/brand/kitfly-neon-192.png +0 -0
  63. package/dist/assets/brand/kitfly-neon-256.png +0 -0
  64. package/dist/assets/brand/kitfly-neon.png +0 -0
  65. package/dist/assets/brand/palette.md +75 -0
  66. package/dist/content/deployment/index.html +11 -0
  67. package/dist/content/deployment/preflight.html +418 -0
  68. package/dist/content/deployment/recipes/aws-s3.html +421 -0
  69. package/dist/content/deployment/recipes/cloudflare-pages.html +372 -0
  70. package/dist/content/deployment/recipes/cloudflare-r2.html +443 -0
  71. package/dist/content/deployment/recipes/fly-io.html +356 -0
  72. package/dist/content/deployment/recipes/github-pages.html +414 -0
  73. package/dist/content/deployment/recipes/index.html +11 -0
  74. package/dist/content/deployment/recipes/netlify.html +394 -0
  75. package/dist/content/deployment/recipes/vercel.html +382 -0
  76. package/dist/content/deployment/secrets-and-env-vars.html +380 -0
  77. package/dist/content/deployment.html +426 -0
  78. package/dist/content/guide/approaches.html +501 -0
  79. package/dist/content/guide/features.html +436 -0
  80. package/dist/content/guide/getting-started.html +403 -0
  81. package/dist/content/guide/index.html +11 -0
  82. package/dist/content/guide/kitfly-overview.html +544 -0
  83. package/dist/content/index.html +11 -0
  84. package/dist/content/reference/configuration.html +580 -0
  85. package/dist/content/reference/design-catalog.html +449 -0
  86. package/dist/content/reference/environment-variables.html +367 -0
  87. package/dist/content/reference/glossary.html +368 -0
  88. package/dist/content/reference/index.html +11 -0
  89. package/dist/content/reference/key-concepts.html +399 -0
  90. package/dist/content/reference/plugins.html +491 -0
  91. package/dist/content/reference/structure.html +463 -0
  92. package/dist/content/reference.html +334 -0
  93. package/dist/content/templates/crucible.html +546 -0
  94. package/dist/content/templates/handbook.html +405 -0
  95. package/dist/content/templates/index.html +11 -0
  96. package/dist/content/templates/minimal.html +447 -0
  97. package/dist/content/templates/overview.html +558 -0
  98. package/dist/content/templates/pipeline.html +494 -0
  99. package/dist/content/templates/productbook.html +540 -0
  100. package/dist/content/templates/runbook.html +543 -0
  101. package/dist/content/templates/servicebook.html +523 -0
  102. package/dist/content-index.json +540 -0
  103. package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +491 -0
  104. package/dist/docs/decisions/ADR-0002-ai-accessibility.html +434 -0
  105. package/dist/docs/decisions/ADR-0003-single-file-bundle.html +412 -0
  106. package/dist/docs/decisions/ADR-0004-bun-runtime.html +409 -0
  107. package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +402 -0
  108. package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +459 -0
  109. package/dist/docs/decisions/DDR-0002-theme-system.html +452 -0
  110. package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +423 -0
  111. package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +399 -0
  112. package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +422 -0
  113. package/dist/docs/decisions/index.html +11 -0
  114. package/dist/docs/userguide/cli/build.html +408 -0
  115. package/dist/docs/userguide/cli/bundle.html +419 -0
  116. package/dist/docs/userguide/cli/dev.html +428 -0
  117. package/dist/docs/userguide/cli/index.html +11 -0
  118. package/dist/docs/userguide/cli/init.html +436 -0
  119. package/dist/docs/userguide/cli/servers.html +393 -0
  120. package/dist/docs/userguide/cli/stop.html +408 -0
  121. package/dist/docs/userguide/cli/update.html +406 -0
  122. package/dist/docs/userguide/cli/version.html +406 -0
  123. package/dist/docs/userguide/cli.html +386 -0
  124. package/dist/docs/userguide/index.html +11 -0
  125. package/dist/docs/userguide/sharing.html +465 -0
  126. package/dist/index.html +387 -0
  127. package/dist/llms.txt +18 -0
  128. package/dist/provenance.json +7 -0
  129. package/dist/schemas/index.html +11 -0
  130. package/dist/schemas/plugin-registry.schema.html +327 -0
  131. package/dist/schemas/plugin-schemas-notes.html +364 -0
  132. package/dist/schemas/plugin.schema.html +327 -0
  133. package/dist/schemas/plugins.schema.html +327 -0
  134. package/dist/schemas/v0/common.schema.html +386 -0
  135. package/dist/schemas/v0/index.html +11 -0
  136. package/dist/schemas/v0/plugin-registry.schema.html +547 -0
  137. package/dist/schemas/v0/plugin.schema.html +497 -0
  138. package/dist/schemas/v0/plugins.schema.html +406 -0
  139. package/dist/schemas/v0/site.schema.html +541 -0
  140. package/dist/schemas/v0/theme.schema.html +615 -0
  141. package/dist/schemas.html +351 -0
  142. package/dist/styles.css +1262 -0
  143. package/package.json +4 -2
  144. package/plugins-dist/callouts.css +32 -0
  145. package/plugins-dist/callouts.js +46 -0
  146. package/plugins-dist/slides-visuals.css +224 -0
  147. package/plugins-dist/slides-visuals.js +598 -0
  148. package/registry/plugins.yaml +35 -0
  149. package/schemas/README.md +10 -0
  150. package/schemas/plugin-registry.schema.json +5 -0
  151. package/schemas/plugin-schemas-notes.md +71 -0
  152. package/schemas/plugin.schema.json +5 -0
  153. package/schemas/plugins.schema.json +5 -0
  154. package/schemas/v0/common.schema.json +64 -0
  155. package/schemas/v0/plugin-registry.schema.json +225 -0
  156. package/schemas/v0/plugin.schema.json +175 -0
  157. package/schemas/v0/plugins.schema.json +84 -0
  158. package/schemas/v0/site.schema.json +56 -9
  159. package/schemas/v0/theme.schema.json +105 -22
  160. package/scripts/build.ts +155 -3
  161. package/scripts/bundle.ts +258 -95
  162. package/scripts/dev.ts +203 -1
  163. package/src/__tests__/build.test.ts +158 -1
  164. package/src/__tests__/bundle.test.ts +31 -0
  165. package/src/__tests__/cli.test.ts +14 -3
  166. package/src/__tests__/fixtures/fences/slides-visuals/invalid/bad-list-indent.md +5 -0
  167. package/src/__tests__/fixtures/fences/slides-visuals/invalid/blank-line.md +5 -0
  168. package/src/__tests__/fixtures/fences/slides-visuals/invalid/compare-object-items.md +9 -0
  169. package/src/__tests__/fixtures/fences/slides-visuals/invalid/indented-fence.md +4 -0
  170. package/src/__tests__/fixtures/fences/slides-visuals/invalid/stat-grid-missing-fields.md +5 -0
  171. package/src/__tests__/fixtures/fences/slides-visuals/invalid/unknown-type.md +3 -0
  172. package/src/__tests__/fixtures/fences/slides-visuals/valid/compare.md +10 -0
  173. package/src/__tests__/fixtures/fences/slides-visuals/valid/comparison-table.md +14 -0
  174. package/src/__tests__/fixtures/fences/slides-visuals/valid/funnel.md +7 -0
  175. package/src/__tests__/fixtures/fences/slides-visuals/valid/kpi.md +5 -0
  176. package/src/__tests__/fixtures/fences/slides-visuals/valid/layer-cake.md +6 -0
  177. package/src/__tests__/fixtures/fences/slides-visuals/valid/pyramid.md +6 -0
  178. package/src/__tests__/fixtures/fences/slides-visuals/valid/quadrant-grid.md +8 -0
  179. package/src/__tests__/fixtures/fences/slides-visuals/valid/scorecard.md +13 -0
  180. package/src/__tests__/fixtures/fences/slides-visuals/valid/stat-grid.md +8 -0
  181. package/src/__tests__/init.test.ts +35 -0
  182. package/src/__tests__/plugin-loader.test.ts +221 -0
  183. package/src/__tests__/shared.test.ts +428 -0
  184. package/src/__tests__/slides-visuals-fence-contract.test.ts +28 -0
  185. package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +114 -0
  186. package/src/__tests__/styles.test.ts +35 -0
  187. package/src/cli.ts +9 -4
  188. package/src/plugin-loader.ts +245 -0
  189. package/src/shared.ts +614 -7
  190. package/src/site/styles.css +331 -0
  191. package/src/site/template.html +66 -5
  192. package/src/templates/deck.ts +186 -0
  193. package/src/templates/driver.ts +11 -1
  194. package/src/templates/minimal.ts +1 -0
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: "ADR-0003: Single-File Bundle Output"
3
+ description: "Kitfly produces a self-contained HTML file as an alternative to static site build"
4
+ author: "deliverylead"
5
+ supervised_by: "@3leapsdave"
6
+ date: "2026-02-09"
7
+ status: "accepted"
8
+ tags: ["adr", "bundle", "architecture", "sharing"]
9
+ ---
10
+
11
+ # ADR-0003: Single-File Bundle Output
12
+
13
+ ## Status
14
+
15
+ Accepted
16
+
17
+ ## Context
18
+
19
+ Documentation is often shared outside of web hosting — via email, Slack uploads, shared drives, or USB handoff. Traditional static site generators produce a directory of HTML, CSS, JS, and images that require a web server or careful relative-path handling to open locally.
20
+
21
+ Kitfly needed a sharing model that works without any infrastructure: one file, any browser, no server.
22
+
23
+ ## Decision
24
+
25
+ Implement `kitfly bundle` as a first-class output mode alongside `kitfly build`. The bundle produces a **single self-contained HTML file** with all dependencies inlined.
26
+
27
+ ### What Gets Inlined
28
+
29
+ | Asset | Inlining Strategy |
30
+ | ------------------------------ | ------------------------------------------- |
31
+ | CSS (styles.css) | `<style>` block in `<head>` |
32
+ | Theme CSS | Generated `<style id="kitfly-theme">` block |
33
+ | Images (PNG, JPG, SVG, GIF) | Base64 data URIs in `<img src>` |
34
+ | Brand logo + favicon | Base64 data URIs |
35
+ | Prism.js (syntax highlighting) | Full JS inlined in `<script>` |
36
+ | Mermaid (diagrams) | Full JS inlined in `<script>` |
37
+ | Dark mode toggle | Inline `<script>` |
38
+ | All page content | Inline HTML sections with `id` anchors |
39
+
40
+ ### Navigation Model
41
+
42
+ The bundle uses **hash-based navigation** rather than page-per-file:
43
+
44
+ - Each content page becomes a `<section id="slug">` within the single document
45
+ - Sidebar links use `href="#slug"` anchors
46
+ - JavaScript shows/hides sections on hash change
47
+ - The sidebar hierarchy uses the same `buildSectionNav()` function as the static build, with a hash-link adapter: `makeHref = (urlPath) => '#' + slugify(urlPath)`
48
+
49
+ This ensures nav structure parity between `build` and `bundle` output.
50
+
51
+ ### Raw Markdown
52
+
53
+ By default, raw `.md` source is embedded in the bundle as hidden `<script type="text/markdown">` blocks, accessible to AI agents. The `--no-raw` flag omits them to reduce file size.
54
+
55
+ ### File Size
56
+
57
+ Typical documentation produces 100KB–1MB bundles. Image-heavy sites are larger due to base64 overhead (~33% per image). The tradeoff is acceptable because the primary use case is sharing, not serving at scale.
58
+
59
+ ## Consequences
60
+
61
+ ### Positive
62
+
63
+ - **Zero-infrastructure sharing**: Email, Slack, Google Drive — recipients open in any browser
64
+ - **Offline by default**: No network requests, no CDN dependencies
65
+ - **Point-in-time snapshot**: Bundle captures exact state of docs at build time
66
+ - **Same navigation as build**: `buildSectionNav()` shared between both output modes
67
+ - **AI-accessible**: Raw markdown available inside the bundle
68
+
69
+ ### Negative
70
+
71
+ - Large bundles with many images (base64 adds ~33% overhead)
72
+ - No incremental loading — entire document loads at once
73
+ - Hash navigation doesn't support deep-linking from external URLs (fine for the sharing use case)
74
+ - Prism + Mermaid JS inlining adds ~500KB to every bundle
75
+
76
+ ### Neutral
77
+
78
+ - Bundle and build share `collectFiles()`, `loadConfig()`, and `buildSectionNav()` from `shared.ts` — shared code stays in one place
79
+ - Bundle is a separate script (`scripts/bundle.ts`) rather than a mode flag on `scripts/build.ts`, keeping each focused
80
+
81
+ ## Alternatives Considered
82
+
83
+ ### PDF export
84
+
85
+ Widely shareable but loses interactivity (dark mode, collapsible nav, syntax highlighting themes). Would require a headless browser dependency (Puppeteer/Playwright). May be added later as a third output mode.
86
+
87
+ ### MHTML / Web Archive
88
+
89
+ Browser-native single-file formats. Rejected because support is inconsistent (Safari doesn't support MHTML, Chrome's implementation has quirks) and generation requires browser automation.
90
+
91
+ ### ZIP of static files
92
+
93
+ Solves the single-artifact problem but requires recipients to extract before viewing. Adds friction compared to double-clicking an HTML file.
@@ -0,0 +1,98 @@
1
+ ---
2
+ title: "ADR-0004: Bun Runtime"
3
+ description: "Kitfly uses Bun as its runtime instead of Node.js"
4
+ author: "deliverylead"
5
+ supervised_by: "@3leapsdave"
6
+ date: "2026-02-09"
7
+ status: "accepted"
8
+ tags: ["adr", "runtime", "architecture", "tooling"]
9
+ ---
10
+
11
+ # ADR-0004: Bun Runtime
12
+
13
+ ## Status
14
+
15
+ Accepted
16
+
17
+ ## Context
18
+
19
+ Kitfly is a TypeScript CLI tool and static site generator. It needs a JavaScript runtime that can:
20
+
21
+ 1. Execute TypeScript directly (no transpilation step)
22
+ 2. Run fast — dev server startup, build times, and test execution should feel instant
23
+ 3. Provide a built-in test runner (reduce dev dependencies)
24
+ 4. Support the Node.js API surface used by kitfly (fs, path, http)
25
+
26
+ The project has a strict minimalism goal (ADR-0001): single production dependency (`marked`), minimal toolchain complexity.
27
+
28
+ ## Decision
29
+
30
+ Use **Bun** as the primary runtime for development, testing, and production execution.
31
+
32
+ ### What Bun Provides
33
+
34
+ | Capability | Replaces |
35
+ | --------------------------- | ---------------------------------------------------------- |
36
+ | Native TypeScript execution | `tsc` compilation step, `ts-node`, `tsx` |
37
+ | Built-in test runner | Separate test framework install (jest/mocha for execution) |
38
+ | Fast startup (~25ms) | Node.js cold start (~100-200ms) |
39
+ | Built-in package manager | npm/yarn/pnpm (for install speed) |
40
+ | `Bun.serve()` HTTP server | Express, Koa, or manual `http.createServer` |
41
+
42
+ ### Runtime Boundary
43
+
44
+ Kitfly's source code uses standard Node.js APIs (`node:fs/promises`, `node:path`, `node:http`) rather than Bun-specific APIs where possible. The main Bun-specific usage is:
45
+
46
+ - `Bun.serve()` in the dev server (`scripts/dev.ts`)
47
+ - `bun run` as the script executor
48
+ - `bun test` via vitest (which uses Bun's runtime)
49
+
50
+ ### User Requirement
51
+
52
+ Users must have Bun installed to run `kitfly`. This is documented in getting-started and enforced at CLI startup. Bun installation is a single command on all major platforms:
53
+
54
+ ```bash
55
+ curl -fsSL https://bun.sh/install | bash
56
+ ```
57
+
58
+ ### Test Runner
59
+
60
+ Tests use **vitest** running on the Bun runtime. This provides:
61
+
62
+ - vitest's assertion API and test organization
63
+ - Bun's fast execution speed
64
+ - Watch mode for development (`bun test:watch`)
65
+
66
+ ## Consequences
67
+
68
+ ### Positive
69
+
70
+ - **No build step**: TypeScript executes directly — `bun run src/cli.ts` just works
71
+ - **Fast iteration**: Dev server starts in under 100ms, full test suite runs in ~500ms (427 tests)
72
+ - **Fewer dependencies**: No need for `ts-node`, `tsx`, or a separate transpiler
73
+ - **Aligned with minimalism**: Bun's built-in capabilities reduce the toolchain surface
74
+
75
+ ### Negative
76
+
77
+ - **Bun is a user prerequisite**: Users who only have Node.js installed need to install Bun first
78
+ - **Ecosystem maturity**: Bun is newer than Node.js — edge cases in compatibility exist (though kitfly's API surface is well-supported)
79
+ - **CI consideration**: CI environments need Bun installed (GitHub Actions: `oven-sh/setup-bun`)
80
+
81
+ ### Neutral
82
+
83
+ - The standard Node.js API usage means a future port to Node.js (with a TypeScript loader) is feasible if needed
84
+ - Bun's package manager is used for `bun install` but `package.json` remains standard — compatible with npm/yarn if users prefer for dependency management
85
+
86
+ ## Alternatives Considered
87
+
88
+ ### Node.js + tsx
89
+
90
+ Node.js with `tsx` for TypeScript execution. Viable but slower startup, requires additional dev dependency, and doesn't provide a built-in test runner or fast HTTP server.
91
+
92
+ ### Deno
93
+
94
+ Native TypeScript support and built-in tooling. Rejected because Deno's module resolution (URL imports, import maps) would complicate the project structure and `kitfly init` copy model. Bun's Node.js compatibility is more aligned with kitfly's design.
95
+
96
+ ### Node.js + esbuild compilation
97
+
98
+ Compile TypeScript to JavaScript, ship compiled output. Rejected because it adds a build step to the development workflow and contradicts the "edit and run" simplicity goal.
@@ -0,0 +1,110 @@
1
+ ---
2
+ title: "ADR-0005: Plugin Contract and Distribution Strategy"
3
+ description: "Defines plugin contract stability, versioning policy, and in-repo-first distribution with extraction path"
4
+ author: "devlead"
5
+ supervised_by: "@3leapsdave"
6
+ date: "2026-02-11"
7
+ status: "proposed"
8
+ tags: ["adr", "plugins", "architecture", "versioning", "supply-chain"]
9
+ ---
10
+
11
+ # ADR-0005: Plugin Contract and Distribution Strategy
12
+
13
+ ## Status
14
+
15
+ Proposed
16
+
17
+ ## Context
18
+
19
+ Kitfly v0.2.0 introduces optional plugin capabilities to support advanced use cases (starting with slides and live-slide extensions) without bloating core site code.
20
+
21
+ As of M1.1, basic slide **shape primitives** remain core CSS. Plugin scope starts at higher-level figures/widgets and engine integrations where deterministic core CSS stops.
22
+
23
+ Two operational choices must be explicit:
24
+
25
+ 1. **Contract stability**: plugin authors and site operators need a clear, versioned interface that remains predictable.
26
+ 2. **Repository strategy**: plugins can live in the main repo or a separate repo; we need a pragmatic default and a low-risk migration path.
27
+
28
+ ## Decision
29
+
30
+ ### 1. Treat plugin contract as versioned API
31
+
32
+ Kitfly will define a versioned plugin contract (`plugin.schema.json` + hook semantics) as a compatibility boundary.
33
+
34
+ Contract rules:
35
+
36
+ - Contract version is explicit (e.g. `contract: "1"`).
37
+ - Breaking changes require a new major contract version.
38
+ - Backward-compatible additions are allowed within a contract major version.
39
+ - Deprecations must include migration guidance before removal.
40
+
41
+ ### 2. Start plugins in-repo for v0.2.0
42
+
43
+ For v0.2.0 delivery speed, first-party plugins are developed in the main kitfly repository under a dedicated plugin path and registry/config structure.
44
+
45
+ Why now:
46
+
47
+ - Faster iteration while contract and hooks are stabilizing.
48
+ - Lower coordination overhead across core + plugin changes.
49
+ - Simpler QA and dogfooding in a single branch/CI surface.
50
+
51
+ ### 3. Keep extraction path explicit
52
+
53
+ In-repo is not permanent by requirement. Plugin layout, manifest format, and loader/registry interfaces must remain portable so plugins can move to `kitfly-plugins` later with minimal disruption.
54
+
55
+ Extraction trigger signals:
56
+
57
+ - plugin maintenance cadence diverges from core,
58
+ - external contribution volume rises,
59
+ - plugin release/security pipeline becomes materially heavier.
60
+
61
+ ### 4. Security and policy baseline
62
+
63
+ Distribution policy applies regardless of repo location:
64
+
65
+ - version pinning required,
66
+ - checksum verification required,
67
+ - SRI required for CDN assets,
68
+ - untrusted third-party plugins denied by default unless explicit opt-in.
69
+
70
+ ## Consequences
71
+
72
+ ### Positive
73
+
74
+ - Contract clarity reduces integration breakage and support ambiguity.
75
+ - In-repo startup reduces delivery friction for v0.2.0.
76
+ - Future repo split remains available without contract rewrite.
77
+
78
+ ### Negative
79
+
80
+ - Core repo grows in scope and review surface in the short term.
81
+ - Requires discipline to prevent plugin internals from leaking into core assumptions.
82
+
83
+ ### Neutral
84
+
85
+ - Repo location is an implementation detail; contract/API stability is the true long-term commitment.
86
+
87
+ ## Compatibility Policy (Initial)
88
+
89
+ - Contract v1 supported throughout v0.2.x.
90
+ - If contract v2 is introduced, v1 support remains for at least one minor release.
91
+ - Compatibility matrix (kitfly version ↔ contract version) must be documented in plugin docs.
92
+
93
+ ## Alternatives Considered
94
+
95
+ ### Separate plugin repo immediately
96
+
97
+ Pros: cleaner ownership and release boundaries from day one.
98
+ Cons: slower early iteration and more cross-repo coordination while contracts are still fluid.
99
+
100
+ Decision: defer split until signals justify it.
101
+
102
+ ### No formal contract versioning
103
+
104
+ Rejected: too much ambiguity and high breakage risk once plugins exist in the wild.
105
+
106
+ ## References
107
+
108
+ - [DDR-0005: Deterministic Layout Boundary](DDR-0005-deterministic-layout-boundary.md)
109
+ - [Design Catalog: Shapes and Figures](../../content/reference/design-catalog.md)
110
+ - [ADR-0001: Minimalist Site Code](ADR-0001-minimalist-site-code.md)
@@ -0,0 +1,111 @@
1
+ ---
2
+ title: "DDR-0001: Viewport-Locked Layout with Fixed Footer Ribbon"
3
+ description: "Defines the kitfly page layout model: fixed chrome (header/footer) with scrollable middle band"
4
+ author: "uxdev"
5
+ supervised_by: "@3leapsdave"
6
+ date: "2026-02-06"
7
+ status: "accepted"
8
+ tags: ["ddr", "layout", "css", "ux"]
9
+ ---
10
+
11
+ # DDR-0001: Viewport-Locked Layout with Fixed Footer Ribbon
12
+
13
+ ## Status
14
+
15
+ Accepted
16
+
17
+ ## Context
18
+
19
+ Kitfly sites use a three-column layout: sidebar navigation, main content, and a table-of-contents panel. The original footer was appended to the content flow with `margin-left` to offset past the sidebar, which caused several problems:
20
+
21
+ - Footer only covered the content pane, not the full viewport width
22
+ - Sidebar navigation extended below the footer, breaking visual hierarchy
23
+ - Footer position depended on content length — short pages had it mid-screen, long pages required scrolling to find it
24
+ - On mobile the footer needed separate margin overrides per breakpoint
25
+
26
+ ## Decision
27
+
28
+ Adopt a **viewport-locked layout** where fixed chrome (footer ribbon, and mobile header) occupies known height at viewport edges, and the middle band (sidebar + content + TOC) fills the remaining space between them.
29
+
30
+ ### Layout Model
31
+
32
+ ```
33
+ ┌──────────────────────────────────────────────────┐
34
+ │ (mobile header — 768px and below only) │
35
+ ├────────────┬─────────────────────────┬───────────┤
36
+ │ │ │ │
37
+ │ SIDEBAR │ CONTENT │ TOC │
38
+ │ fixed │ scrollable │ fixed │
39
+ │ left │ center │ right │
40
+ │ 280px │ flex: 1 │ 200px │
41
+ │ │ │ │
42
+ │ top: 0 │ margin-left: sidebar │ top: 6r │
43
+ │ bottom: │ padding-bottom: │ │
44
+ │ footer │ footer + 1rem │ │
45
+ │ │ │ │
46
+ ├────────────┴─────────────────────────┴───────────┤
47
+ │ FOOTER RIBBON │
48
+ │ position: fixed; bottom: 0; left: 0; right: 0 │
49
+ │ height: var(--footer-height) z-index: 300 │
50
+ │ full-width — spans all columns │
51
+ └──────────────────────────────────────────────────┘
52
+ ```
53
+
54
+ ### CSS Variables
55
+
56
+ | Variable | Value | Purpose |
57
+ | ----------------- | --------- | -------------------- |
58
+ | `--sidebar-width` | `280px` | Sidebar column width |
59
+ | `--footer-height` | `2.25rem` | Footer ribbon height |
60
+
61
+ ### Z-Index Stack
62
+
63
+ | Layer | Z-Index | Element |
64
+ | -------------- | ------- | --------------------------- |
65
+ | Footer ribbon | 300 | `.site-footer` |
66
+ | Mobile header | 200 | `.mobile-header` |
67
+ | Mobile sidebar | 100 | `.sidebar` (mobile overlay) |
68
+
69
+ ### Key Rules
70
+
71
+ 1. **Footer is viewport-fixed**, not content-appended. It uses `position: fixed; bottom: 0` and spans full width.
72
+ 2. **Sidebar stops above footer** via `bottom: var(--footer-height)` — no overlap, sidebar scrolls independently within its band.
73
+ 3. **Content has bottom padding** of `calc(var(--footer-height) + 1rem)` so the last content line is never hidden behind the footer.
74
+ 4. **Layout min-height** is `calc(100vh - var(--footer-height))` to prevent the document from extending behind the footer.
75
+ 5. **Mobile breakpoints** inherit the same `--footer-height` variable — no per-breakpoint footer overrides needed.
76
+ 6. **Print styles** hide the footer entirely (`.site-footer { display: none !important }`).
77
+
78
+ ### Responsive Behavior
79
+
80
+ | Breakpoint | Sidebar | TOC | Footer |
81
+ | ---------- | ------------------ | ----------- | ------------------------ |
82
+ | > 1200px | Fixed left | Fixed right | Fixed bottom, full-width |
83
+ | 769–1200px | Fixed left (240px) | Hidden | Fixed bottom, full-width |
84
+ | ≤ 768px | Overlay (toggle) | Hidden | Fixed bottom, full-width |
85
+
86
+ The footer ribbon is consistent across all breakpoints — it never changes position or width.
87
+
88
+ ## Consequences
89
+
90
+ ### Positive
91
+
92
+ - Footer is always visible as a status/provenance ribbon without scrolling
93
+ - Sidebar navigation never extends below footer
94
+ - Single `--footer-height` variable controls all spacing — easy to adjust
95
+ - No per-breakpoint footer margin overrides
96
+ - Clean visual hierarchy: chrome frames content
97
+
98
+ ### Negative
99
+
100
+ - Content needs `padding-bottom` to avoid being hidden — if `--footer-height` changes, padding must match (mitigated by using the CSS variable)
101
+ - Fixed footer consumes ~36px of viewport permanently — acceptable for the information density it provides (version, date, copyright, brand link)
102
+
103
+ ## Alternatives Considered
104
+
105
+ ### Sticky footer (content-appended)
106
+
107
+ The original approach. Footer stays in document flow but sticks to bottom on short pages. Rejected because it doesn't span the sidebar and creates layout inconsistencies across page lengths.
108
+
109
+ ### CSS Grid viewport layout
110
+
111
+ Use `grid-template-rows: 1fr auto` on the body. Would work but requires restructuring the HTML template (moving sidebar inside a grid container). More invasive than necessary for the current fix.
@@ -0,0 +1,131 @@
1
+ ---
2
+ title: "DDR-0002: Two-Layer Theme System"
3
+ description: "Defines the kitfly theming model: theme.yaml semantic tokens → generated CSS variables → styles.css fallbacks"
4
+ author: "deliverylead"
5
+ supervised_by: "@3leapsdave"
6
+ date: "2026-02-09"
7
+ status: "accepted"
8
+ tags: ["ddr", "theming", "css", "design"]
9
+ ---
10
+
11
+ # DDR-0002: Two-Layer Theme System
12
+
13
+ ## Status
14
+
15
+ Accepted
16
+
17
+ ## Context
18
+
19
+ Kitfly needs a theming system that lets users customize colors and typography without editing CSS. At the same time, the shipped CSS must render correctly even without a theme file — cold-start works must work. The system must support light/dark modes, syntax highlighting themes, and user-supplied theme files alongside bundled presets.
20
+
21
+ ## Decision
22
+
23
+ Adopt a **two-layer theming model** where a YAML configuration layer defines semantic design tokens and a CSS layer consumes them as custom properties, with hardcoded fallbacks.
24
+
25
+ ### Layer 1: theme.yaml → CSS Variables
26
+
27
+ `theme.yaml` defines semantic color tokens and typography settings:
28
+
29
+ ```yaml
30
+ colors:
31
+ light:
32
+ background: "#ffffff"
33
+ surface: "#f5f7f8"
34
+ text: "#374151"
35
+ heading: "#152F46"
36
+ primary: "#007182"
37
+ accent: "#D17059"
38
+ border: "#e5e7eb"
39
+ dark:
40
+ background: "#0d1117"
41
+ surface: "#152F46"
42
+ # ...
43
+
44
+ typography:
45
+ body: "system"
46
+ headings: "system"
47
+ code: "mono"
48
+ baseSize: "16px"
49
+ scale: "1.25"
50
+
51
+ code:
52
+ light: "default"
53
+ dark: "okaidia"
54
+ ```
55
+
56
+ `src/theme.ts` loads this file, deep-merges with `DEFAULT_THEME`, and calls `generateThemeCSS()` to produce a `<style id="kitfly-theme">` block that sets CSS custom properties on `:root`.
57
+
58
+ ### Layer 2: styles.css Fallbacks
59
+
60
+ `src/site/styles.css` contains hardcoded fallback values in its `:root` block:
61
+
62
+ ```css
63
+ :root {
64
+ --color-bg: #ffffff;
65
+ --color-bg-sidebar: #f8f9fa;
66
+ --color-text: #1a1a1a;
67
+ /* ... */
68
+ }
69
+ ```
70
+
71
+ These fallbacks ensure the site renders correctly if `theme.yaml` is missing or `generateThemeCSS()` is not injected. When the theme `<style>` block is present, it overrides the fallbacks because it appears later in the document `<head>`.
72
+
73
+ ### Token Mapping
74
+
75
+ | theme.yaml token | CSS variable | CSS usage |
76
+ | --------------------------- | -------------------- | ------------------------------- |
77
+ | `colors.light.background` | `--color-bg` | Page background |
78
+ | `colors.light.surface` | `--color-bg-sidebar` | Sidebar, code block backgrounds |
79
+ | `colors.light.text` | `--color-text` | Body text |
80
+ | `colors.light.textMuted` | `--color-text-muted` | Secondary text, metadata |
81
+ | `colors.light.heading` | `--color-accent` | Headings, logo color |
82
+ | `colors.light.primary` | `--color-link` | Links, active nav |
83
+ | `colors.light.primaryHover` | `--color-link-hover` | Link hover state |
84
+ | `colors.light.border` | `--color-border` | Borders, dividers |
85
+
86
+ Dark mode uses the same mapping under `@media (prefers-color-scheme: dark)` and `[data-theme="dark"]`.
87
+
88
+ ### Dark Mode Strategy
89
+
90
+ Three selectors handle dark mode:
91
+
92
+ 1. `@media (prefers-color-scheme: dark)` — OS-level automatic
93
+ 2. `[data-theme="dark"]` — explicit user toggle
94
+ 3. `:root:not([data-theme="light"])` — prevents OS dark overriding an explicit light choice
95
+
96
+ ### Theme Presets
97
+
98
+ Bundled themes in `themes/` (`github.yaml`, `paper.yaml`, `terminal.yaml`) can be copied and customized. Users place their `theme.yaml` in the project root alongside `site.yaml`.
99
+
100
+ ## Consequences
101
+
102
+ ### Positive
103
+
104
+ - Users customize appearance via YAML without touching CSS
105
+ - CSS works standalone — no build step required to see something
106
+ - Dark mode works automatically via OS preference with manual override
107
+ - Theme presets provide starting points for customization
108
+ - Syntax highlighting themes are decoupled (Prism CDN URLs per light/dark)
109
+
110
+ ### Negative
111
+
112
+ - CSS fallback values can drift from `DEFAULT_THEME` if one is updated without the other (mitigated: both live in the codebase and tests can catch drift)
113
+ - Token mapping is implicit — `surface` → `--color-bg-sidebar` isn't obvious without reading theme.ts
114
+
115
+ ### Neutral
116
+
117
+ - Typography uses named presets (`system`, `serif`, `readable`) rather than raw font stacks — simple but limits advanced control
118
+
119
+ ## Alternatives Considered
120
+
121
+ ### CSS-only theming (no YAML)
122
+
123
+ Users edit CSS directly. Rejected because YAML is more accessible for non-developers and integrates with the existing `site.yaml` configuration model.
124
+
125
+ ### CSS Modules / PostCSS pipeline
126
+
127
+ Build-time CSS transformation. Rejected because it contradicts ADR-0001 (minimalist site code) and adds build dependencies.
128
+
129
+ ### Single source of truth (no CSS fallbacks)
130
+
131
+ Only generate CSS from theme.ts, no hardcoded `:root`. Rejected because it breaks cold-start: opening `styles.css` directly or loading before JS runs would show unstyled content.
@@ -0,0 +1,106 @@
1
+ ---
2
+ title: "DDR-0003: Bounded Logo Slot"
3
+ description: "Logo rendering uses a bounded bounding-box model supporting both square icons and wide wordmarks"
4
+ author: "deliverylead"
5
+ supervised_by: "@3leapsdave"
6
+ date: "2026-02-09"
7
+ status: "accepted"
8
+ tags: ["ddr", "logo", "css", "branding"]
9
+ ---
10
+
11
+ # DDR-0003: Bounded Logo Slot
12
+
13
+ ## Status
14
+
15
+ Accepted
16
+
17
+ ## Context
18
+
19
+ Kitfly sites display a brand logo in the sidebar header. Logos come in two aspect ratios:
20
+
21
+ - **Square icons** (1:1 or near-square) — app icons, monograms, favicons scaled up
22
+ - **Wide wordmarks** (3:1 or wider) — full brand names, logotypes with text
23
+
24
+ The previous implementation used a fixed pixel height for the logo image, which worked for one shape but not the other. A square icon at 64px rendered fine; the same 64px height on a wide wordmark meant it consumed most of the sidebar width and pushed navigation down. Conversely, constraining width for wordmarks made square icons tiny.
25
+
26
+ ## Decision
27
+
28
+ Use a **bounded bounding-box model** where the logo slot defines maximum dimensions per breakpoint, and the image fills the box while preserving its native aspect ratio.
29
+
30
+ ### Slot Dimensions
31
+
32
+ | Breakpoint | Max Height | Max Width |
33
+ | ------------------ | ---------- | --------- |
34
+ | Desktop (> 1024px) | 64px | 180px |
35
+ | Tablet (≤ 1024px) | 56px | 150px |
36
+ | Mobile (≤ 768px) | 48px | 130px |
37
+
38
+ ### CSS Implementation
39
+
40
+ ```css
41
+ .logo-icon {
42
+ height: 64px;
43
+ width: auto;
44
+ max-width: 180px;
45
+ }
46
+ ```
47
+
48
+ The key properties:
49
+
50
+ - `height` sets the box height (the dominant constraint for square icons)
51
+ - `width: auto` preserves aspect ratio
52
+ - `max-width` prevents wide wordmarks from overflowing the sidebar
53
+
54
+ For wordmark logos, the `.logo-wordmark` class switches the constraint axis:
55
+
56
+ ```css
57
+ .logo.logo-wordmark .logo-img {
58
+ max-width: 180px;
59
+ height: auto;
60
+ max-height: 64px;
61
+ }
62
+ ```
63
+
64
+ ### logoType Configuration
65
+
66
+ `site.yaml` supports `brand.logoType` to select the rendering mode:
67
+
68
+ | Value | Behavior |
69
+ | ------------------ | ---------------------------------------------- |
70
+ | `"icon"` (default) | Height-dominant constraint, square/near-square |
71
+ | `"wordmark"` | Width-dominant constraint, wide aspect ratio |
72
+
73
+ The bundle system propagates `logoType` via `buildBundleSidebarHeader()` to ensure parity between build and bundle output.
74
+
75
+ ### Supported Formats
76
+
77
+ - **PNG**: Recommended for icons. Use `kitfly-logo-128.png` at 128px for 2x retina clarity in the 64px slot.
78
+ - **SVG**: Supported but requires a tightly-cropped `viewBox`. An oversized canvas (e.g., A4 page `viewBox="0 0 210 297"`) will render the artwork too small within the bounded slot.
79
+
80
+ ## Consequences
81
+
82
+ ### Positive
83
+
84
+ - Both icon and wordmark logos render cleanly without CSS customization
85
+ - Responsive scaling is automatic — one set of breakpoints handles both shapes
86
+ - No logo distortion — aspect ratio is always preserved
87
+ - Bundle output matches static build (logoType propagation)
88
+
89
+ ### Negative
90
+
91
+ - SVG logos with oversized viewBox render poorly — users must crop the viewBox to artwork bounds (documented in configuration reference)
92
+ - Two rendering modes (`icon` vs `wordmark`) add a configuration choice — mitigated by defaulting to `icon` which handles most cases
93
+
94
+ ## Alternatives Considered
95
+
96
+ ### Fixed pixel dimensions
97
+
98
+ Single `width: 120px; height: 40px` for all logos. Rejected because it distorts non-matching aspect ratios and doesn't adapt to breakpoints.
99
+
100
+ ### Percentage-based sizing
101
+
102
+ `width: 60%` of sidebar. Rejected because it couples logo size to sidebar width and produces inconsistent results across breakpoints (280px desktop vs overlay mobile).
103
+
104
+ ### User-specified dimensions in site.yaml
105
+
106
+ Let users set `logo.width` and `logo.height` in config. Rejected as over-engineering — the bounded box handles the common cases, and users can override via custom CSS if needed.