@takazudo/zudo-doc 4.2.1 → 4.4.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 (164) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +3 -3
  3. package/bin/zudo-doc.mjs +54 -13
  4. package/dist/auto-logo/index.d.ts +18 -0
  5. package/dist/auto-logo/index.js +33 -0
  6. package/dist/auto-logo/shapes.d.ts +32 -0
  7. package/dist/auto-logo/shapes.js +183 -0
  8. package/dist/auto-logo/standalone.d.ts +7 -0
  9. package/dist/auto-logo/standalone.js +35 -0
  10. package/dist/color-scheme-utils.d.ts +4 -3
  11. package/dist/color-scheme-utils.js +1 -1
  12. package/dist/config/component-tokens.js +2 -2
  13. package/dist/config.d.ts +23 -1
  14. package/dist/config.js +3 -0
  15. package/dist/content.css +4 -3
  16. package/dist/doc-content-header/index.js +1 -1
  17. package/dist/doc-history/index.js +1 -1
  18. package/dist/doc-history-area/index.d.ts +1 -0
  19. package/dist/doc-history-area/index.js +3 -0
  20. package/dist/doc-page-shell/index.js +1 -1
  21. package/dist/doc-pager/index.js +2 -2
  22. package/dist/eject-logo/config-parse.d.ts +25 -0
  23. package/dist/eject-logo/config-parse.js +68 -0
  24. package/dist/eject-logo/config-rewriter.d.ts +22 -0
  25. package/dist/eject-logo/config-rewriter.js +68 -0
  26. package/dist/eject-logo/eject.d.ts +18 -0
  27. package/dist/eject-logo/eject.js +66 -0
  28. package/dist/eject-logo/index.d.ts +4 -0
  29. package/dist/eject-logo/index.js +16 -0
  30. package/dist/eject-logo/site-name.d.ts +21 -0
  31. package/dist/eject-logo/site-name.js +31 -0
  32. package/dist/features.css +8 -29
  33. package/dist/header/header.js +14 -6
  34. package/dist/header/nav-class-tokens.d.ts +12 -0
  35. package/dist/header/nav-class-tokens.js +80 -0
  36. package/dist/header/nav-overflow-script.js +33 -16
  37. package/dist/home-page/index.d.ts +34 -5
  38. package/dist/home-page/index.js +95 -38
  39. package/dist/home-page/prepare-home-data.d.ts +9 -0
  40. package/dist/home-page/prepare-home-data.js +10 -3
  41. package/dist/html-preview-wrapper/highlight-runtime.d.ts +40 -0
  42. package/dist/html-preview-wrapper/highlight-runtime.js +58 -0
  43. package/dist/html-preview-wrapper/highlighted-code.d.ts +3 -2
  44. package/dist/html-preview-wrapper/highlighted-code.js +11 -30
  45. package/dist/i18n-defaults/index.js +3 -0
  46. package/dist/metainfo/doc-metainfo.js +35 -22
  47. package/dist/plugins/doc-history.js +6 -2
  48. package/dist/plugins/internal/doc-history/index.d.ts +2 -0
  49. package/dist/plugins/internal/doc-history/index.js +5 -0
  50. package/dist/plugins/internal/doc-history/pre-build.d.ts +2 -0
  51. package/dist/plugins/internal/doc-history/pre-build.js +3 -0
  52. package/dist/preset.d.ts +1 -0
  53. package/dist/preset.js +2 -1
  54. package/dist/routes/index.js +13 -3
  55. package/dist/routes/locale-index.js +12 -2
  56. package/dist/safelist.css +1 -1
  57. package/dist/settings.d.ts +15 -0
  58. package/dist/sidebar-tree-island/index.js +3 -3
  59. package/dist/theme-cli/config-scanner.js +47 -0
  60. package/dist/theme-packs/academia/fonts/CourierPrime-400-latin.woff2 +0 -0
  61. package/dist/theme-packs/academia/fonts/CourierPrime-700-latin.woff2 +0 -0
  62. package/dist/theme-packs/academia/fonts/OFL.txt +94 -0
  63. package/dist/theme-packs/academia/fonts/SourceSerif4-italic-latin.woff2 +0 -0
  64. package/dist/theme-packs/academia/fonts/SourceSerif4-latin.woff2 +0 -0
  65. package/dist/theme-packs/academia/meta.json +37 -0
  66. package/dist/theme-packs/academia/pack.css +505 -0
  67. package/dist/theme-packs/bauhaus/fonts/ArchivoBlack-latin.woff2 +0 -0
  68. package/dist/theme-packs/bauhaus/fonts/Jost-latin.woff2 +0 -0
  69. package/dist/theme-packs/bauhaus/fonts/OFL.txt +95 -0
  70. package/dist/theme-packs/bauhaus/fonts/SpaceMono-400-latin.woff2 +0 -0
  71. package/dist/theme-packs/bauhaus/fonts/SpaceMono-700-latin.woff2 +0 -0
  72. package/dist/theme-packs/bauhaus/meta.json +38 -0
  73. package/dist/theme-packs/bauhaus/pack.css +614 -0
  74. package/dist/theme-packs/blueprint/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  75. package/dist/theme-packs/blueprint/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  76. package/dist/theme-packs/blueprint/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  77. package/dist/theme-packs/blueprint/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  78. package/dist/theme-packs/blueprint/fonts/Inter-latin-ext.woff2 +0 -0
  79. package/dist/theme-packs/blueprint/fonts/Inter-latin.woff2 +0 -0
  80. package/dist/theme-packs/blueprint/fonts/JosefinSans-latin.woff2 +0 -0
  81. package/dist/theme-packs/blueprint/fonts/OFL.txt +95 -0
  82. package/dist/theme-packs/blueprint/meta.json +38 -0
  83. package/dist/theme-packs/blueprint/pack.css +577 -0
  84. package/dist/theme-packs/botanica/fonts/CormorantGaramond-latin.woff2 +0 -0
  85. package/dist/theme-packs/botanica/fonts/CourierPrime-400-latin-ext.woff2 +0 -0
  86. package/dist/theme-packs/botanica/fonts/CourierPrime-400-latin.woff2 +0 -0
  87. package/dist/theme-packs/botanica/fonts/CourierPrime-700-latin-ext.woff2 +0 -0
  88. package/dist/theme-packs/botanica/fonts/CourierPrime-700-latin.woff2 +0 -0
  89. package/dist/theme-packs/botanica/fonts/CrimsonPro-latin-italic.woff2 +0 -0
  90. package/dist/theme-packs/botanica/fonts/CrimsonPro-latin.woff2 +0 -0
  91. package/dist/theme-packs/botanica/fonts/OFL.txt +95 -0
  92. package/dist/theme-packs/botanica/meta.json +38 -0
  93. package/dist/theme-packs/botanica/pack.css +546 -0
  94. package/dist/theme-packs/brutalist/meta.json +1 -1
  95. package/dist/theme-packs/brutalist/pack.css +10 -9
  96. package/dist/theme-packs/eink/fonts/CourierPrime-400-latin-ext.woff2 +0 -0
  97. package/dist/theme-packs/eink/fonts/CourierPrime-400-latin.woff2 +0 -0
  98. package/dist/theme-packs/eink/fonts/CourierPrime-700-latin-ext.woff2 +0 -0
  99. package/dist/theme-packs/eink/fonts/CourierPrime-700-latin.woff2 +0 -0
  100. package/dist/theme-packs/eink/fonts/EBGaramond-latin-italic.woff2 +0 -0
  101. package/dist/theme-packs/eink/fonts/EBGaramond-latin.woff2 +0 -0
  102. package/dist/theme-packs/eink/fonts/OFL.txt +94 -0
  103. package/dist/theme-packs/eink/meta.json +37 -0
  104. package/dist/theme-packs/eink/pack.css +490 -0
  105. package/dist/theme-packs/foundry/meta.json +1 -1
  106. package/dist/theme-packs/foundry/pack.css +2 -2
  107. package/dist/theme-packs/hearth/meta.json +1 -1
  108. package/dist/theme-packs/hearth/pack.css +15 -4
  109. package/dist/theme-packs/nocturne/pack.css +4 -0
  110. package/dist/theme-packs/observatory/meta.json +1 -1
  111. package/dist/theme-packs/observatory/pack.css +4 -1
  112. package/dist/theme-packs/onyx/pack.css +6 -0
  113. package/dist/theme-packs/phosphor/pack.css +4 -0
  114. package/dist/theme-packs/riso/fonts/ArchivoBlack-latin.woff2 +0 -0
  115. package/dist/theme-packs/riso/fonts/OFL.txt +95 -0
  116. package/dist/theme-packs/riso/fonts/SpaceGrotesk-latin.woff2 +0 -0
  117. package/dist/theme-packs/riso/fonts/SpaceMono-400-italic-latin.woff2 +0 -0
  118. package/dist/theme-packs/riso/fonts/SpaceMono-400-latin.woff2 +0 -0
  119. package/dist/theme-packs/riso/fonts/SpaceMono-700-latin.woff2 +0 -0
  120. package/dist/theme-packs/riso/meta.json +38 -0
  121. package/dist/theme-packs/riso/pack.css +602 -0
  122. package/dist/theme-packs/sakura/fonts/MPlus1Code-400-latin.woff2 +0 -0
  123. package/dist/theme-packs/sakura/fonts/MPlus1Code-500-latin.woff2 +0 -0
  124. package/dist/theme-packs/sakura/fonts/Nunito-latin.woff2 +0 -0
  125. package/dist/theme-packs/sakura/fonts/OFL.txt +95 -0
  126. package/dist/theme-packs/sakura/fonts/ShipporiMincho-600-latin.woff2 +0 -0
  127. package/dist/theme-packs/sakura/fonts/ShipporiMincho-700-latin.woff2 +0 -0
  128. package/dist/theme-packs/sakura/meta.json +38 -0
  129. package/dist/theme-packs/sakura/pack.css +473 -0
  130. package/dist/theme-packs/scandi/fonts/Manrope-latin.woff2 +0 -0
  131. package/dist/theme-packs/scandi/fonts/OFL.txt +94 -0
  132. package/dist/theme-packs/scandi/fonts/SplineSansMono-latin.woff2 +0 -0
  133. package/dist/theme-packs/scandi/meta.json +37 -0
  134. package/dist/theme-packs/scandi/pack.css +370 -0
  135. package/dist/theme-packs/sumi/meta.json +1 -1
  136. package/dist/theme-packs/sumi/pack.css +4 -1
  137. package/dist/theme-packs/swissgrid/pack.css +4 -0
  138. package/dist/theme-packs/tidepool/fonts/JetBrainsMono-latin-ext.woff2 +0 -0
  139. package/dist/theme-packs/tidepool/fonts/JetBrainsMono-latin.woff2 +0 -0
  140. package/dist/theme-packs/tidepool/fonts/OFL.txt +94 -0
  141. package/dist/theme-packs/tidepool/fonts/SpaceGrotesk-latin.woff2 +0 -0
  142. package/dist/theme-packs/tidepool/meta.json +37 -0
  143. package/dist/theme-packs/tidepool/pack.css +425 -0
  144. package/dist/theme-packs/timberline/fonts/Fraunces-italic-latin.woff2 +0 -0
  145. package/dist/theme-packs/timberline/fonts/Fraunces-latin.woff2 +0 -0
  146. package/dist/theme-packs/timberline/fonts/JetBrainsMono-latin.woff2 +0 -0
  147. package/dist/theme-packs/timberline/fonts/OFL.txt +95 -0
  148. package/dist/theme-packs/timberline/fonts/SourceSans3-italic-latin.woff2 +0 -0
  149. package/dist/theme-packs/timberline/fonts/SourceSans3-latin.woff2 +0 -0
  150. package/dist/theme-packs/timberline/meta.json +38 -0
  151. package/dist/theme-packs/timberline/pack.css +509 -0
  152. package/dist/theme-packs/washi/meta.json +1 -1
  153. package/dist/theme-packs/washi/pack.css +6 -26
  154. package/dist/theme-packs-registry/validator.js +24 -1
  155. package/dist/theme.css +1 -1
  156. package/eject/doc-history/index.tsx +1 -1
  157. package/eject/doc-pager/index.tsx +2 -2
  158. package/eject/header/header.tsx +16 -8
  159. package/eject/header/nav-class-tokens.ts +115 -0
  160. package/eject/header/nav-overflow-script.ts +49 -16
  161. package/eject/sidebar-tree-island/index.tsx +4 -4
  162. package/package.json +16 -13
  163. package/routes-src/index.tsx +10 -3
  164. package/routes-src/locale-index.tsx +9 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,49 @@ All notable changes to `@takazudo/zudo-doc` are documented in this file.
4
4
 
5
5
  The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
6
6
 
7
+ ## [4.4.0] - 2026-07-22
8
+
9
+ ### Features
10
+
11
+ - Theme Batch 5 adds 10 new theme packs — academia, bauhaus, blueprint, botanica, eink, riso, sakura, scandi, tidepool, and timberline — bringing the catalog to 31 packs, each with full admonition/code/sidebar coverage and a rendered WCAG contrast audit (29bb3497f, 12665cdf1)
12
+ - A new `logo` setting renders a generated "auto" home-hero logo by default, alongside a `zudo-doc eject logo` CLI subcommand for taking full ownership of the logo markup (a2ba5188a, 3cedbee04)
13
+ - `scripts/theme-a11y-audit.ts` adds a rendered per-pack contrast audit tool, now wired into the nightly exam and documented for theme authors (4c969cd4a, 8ed913f9d)
14
+ - The top-page Tags section now renders tag chips, and the home hero gained a `heroLink` prop with an inline extras row (55be502c2, fc865e6be)
15
+ - Header, sidebar, overflow-dropdown, and doc-pager non-active nav elements now consistently turn accent color on hover/focus, with a shared class-token source of truth for the header nav (fafecc154, 274a8641f)
16
+
17
+ ### Bug Fixes
18
+
19
+ - Resolved 10 rendered-a11y audit findings across 5 existing theme packs, plus follow-up contrast fixes for blueprint/sakura/tidepool and five additional deep-review findings (b694708dc, 2c2dcc858, 12f7964f9)
20
+ - Theme-CLI config scanning now correctly decodes `\uXXXX`, `\u{...}`, and `\xXX` escapes (2e58e3dc0)
21
+ - The default title-rule border is reset only where a theme pack owns its own title styling, keeping custom pack titles from double-bordering (9043b9439)
22
+ - Doc-history anchors now point at the actual generated heading slugs (9e3e62655)
23
+
24
+ ### Other Changes
25
+
26
+ - Re-baselined the route-injection parity test's normalized-HTML sha256 snapshots twice this cycle, attributing every changed byte to its originating merged PR (5e46831d4, 4fd642c3b)
27
+ - Bumped the `@takazudo/zfb` family to `0.1.0-next.90`
28
+ - Expanded English and Japanese documentation for the logo/eject-logo feature, the Theme Gallery (31 packs), and the hover-accent interactive color rule
29
+
30
+ ## [4.3.0] - 2026-07-19
31
+
32
+ ### Features
33
+
34
+ - Syntax highlighting is now driven by semantic design tokens across document code fences and `HtmlPreview`. Browser previews lazily load the public zfb WASM highlighter, existing color schemes inherit compatible semantic aliases, and project-owned Shiki dependencies and theme-name configuration are no longer required (178f23321)
35
+ - Home pages can opt into the wide content layout with `home.wide`; the setting applies consistently to package-owned root and localized home routes (d068ac346)
36
+ - Doc history can exclude selected pages through configuration, with matching behavior threaded through rendering, history generation, package settings, and generated-project configuration (e18c2e528)
37
+ - The Hearth theme now provides complete admonition icon coverage (736c6c5e1)
38
+
39
+ ### Bug Fixes
40
+
41
+ - Frontmatter status pills now meet WCAG contrast requirements across every shipped theme pack, light/dark mode, and semantic role while preserving their tinted backgrounds (2986d87c4)
42
+ - The showcase now consumes the package-owned theme contract correctly and emits literal semantic z-index utilities (2ca79ca6b, 8fb517df6)
43
+ - Theme-pack font validation is stricter, and the generated setup skill resolves main-worktree paths reliably (f9dd94d53, 4c2a508bb)
44
+
45
+ ### Other Changes
46
+
47
+ - Expanded English and Japanese documentation for syntax-token migration, wide home layouts, doc-history exclusions, theme development, and network exposure caveats
48
+ - Added browser-level highlighting coverage, exact pill contrast checks, settings parity tests, and compatibility-contract guards
49
+
7
50
  ## [4.2.1] - 2026-07-19
8
51
 
9
52
  ### Other Changes
package/README.md CHANGED
@@ -18,12 +18,12 @@ This package provides the missing-by-design framework concerns:
18
18
  - **Head injection** (`./head`) — canonical, og:\*, twitter:\*, robots, preload hints, RSS link, sitemap link, and theme-color output.
19
19
  - **SSR-skip wrappers** (`./ssr-skip`) — `<AiChatModalIsland>`, `<ImageEnlargeIsland>`, `<DesignTokenTweakPanelIsland>`, `<MockInitIsland>` — wrap zfb's `<Island ssrFallback>` with the right fallback markup so doc pages don't have to re-implement the SSR-skip pattern.
20
20
 
21
- ## Optional peer dependency: shiki
21
+ ## Optional peer dependency: `@takazudo/zfb-md-wasm`
22
22
 
23
- `./html-preview-wrapper`'s `<HighlightedCode>` lazily `import("shiki")`s at runtime for client-side syntax highlighting. `shiki` is declared as an **optional peerDependency** — install it in your project if you use that subpath:
23
+ `./html-preview-wrapper`'s `<HighlightedCode>` lazily imports the package root and calls `highlightCode()` for client-side semantic syntax highlighting. `@takazudo/zfb-md-wasm` is declared as an **optional peerDependency** — install the same prerelease version as the rest of your zfb packages if you use that subpath:
24
24
 
25
25
  ```sh
26
- pnpm add shiki
26
+ pnpm add @takazudo/zfb-md-wasm
27
27
  ```
28
28
 
29
29
  Projects scaffolded by `create-zudo-doc` already include it. If you never render `<HtmlPreview>` / `<HighlightedCode>`, you can omit it.
package/bin/zudo-doc.mjs CHANGED
@@ -3,21 +3,28 @@
3
3
  //
4
4
  // Package bin: `zudo-doc eject <component>` swizzle CLI, plus
5
5
  // `zudo-doc theme list|apply <slug>` (issue #2824; ADR
6
- // docs/adr/theme-packs.md).
6
+ // docs/adr/theme-packs.md), plus `zudo-doc eject logo` (issue #3050; epic
7
+ // #3047).
7
8
  //
8
- // Self-contained ESM — runs on plain `node` with NO `tsx` requirement. Both
9
- // the eject and theme-cli logic are imported from the package's COMPILED
10
- // `../dist/eject/index.js` / `../dist/theme-cli/index.js`, and the only
11
- // runtime deps are `minimist` + `picocolors` (declared deps of this package,
12
- // so they are present transitively in any consumer's node_modules). This is
13
- // the key difference from the tsx-runner pattern used by `bin/tags-audit.mjs`:
14
- // tags-audit must load the *project's* TypeScript config files at runtime (hence
15
- // tsx), whereas eject/theme-cli only copy files / rewrite text — no TS eval
16
- // needed so both work in a default generated project that never installed
17
- // tsx (#2367).
9
+ // Self-contained ESM — runs on plain `node` with NO `tsx` requirement. The
10
+ // eject, theme-cli, and eject-logo logic are imported from the package's
11
+ // COMPILED `../dist/eject/index.js` / `../dist/theme-cli/index.js` /
12
+ // `../dist/eject-logo/index.js`, and the only runtime deps are `minimist` +
13
+ // `picocolors` (declared deps of this package, so they are present
14
+ // transitively in any consumer's node_modules). This is the key difference
15
+ // from the tsx-runner pattern used by `bin/tags-audit.mjs`: tags-audit must
16
+ // load the *project's* TypeScript config files at runtime (hence tsx),
17
+ // whereas eject/theme-cli/eject-logo only copy files / rewrite text no TS
18
+ // eval needed — so all three work in a default generated project that never
19
+ // installed tsx (#2367).
20
+ //
21
+ // `eject logo` is special-cased below, BEFORE the EJECTABLE lookup — it is
22
+ // NOT a source-copy swizzle (EJECTABLE's contract), it renders a fresh SVG
23
+ // and rewrites a config field, so it does not belong in that map.
18
24
  //
19
25
  // Usage:
20
26
  // zudo-doc eject <component> # eject a component's TS source into the project
27
+ // zudo-doc eject logo # render public/img/logo.svg + rewrite the logo field
21
28
  // zudo-doc theme list # list installed theme packs + the active one
22
29
  // zudo-doc theme apply <slug> # rewrite zfb.config.ts's themePack field
23
30
  // zudo-doc --help # show help
@@ -25,10 +32,12 @@
25
32
  import minimist from "minimist";
26
33
  import pc from "picocolors";
27
34
  import { eject, EJECTABLE } from "../dist/eject/index.js";
35
+ import { ejectLogo } from "../dist/eject-logo/index.js";
28
36
  import { applyThemePack, formatThemeList, listThemePacks } from "../dist/theme-cli/index.js";
29
37
 
30
38
  const argv = minimist(process.argv.slice(2), {
31
- boolean: ["help"],
39
+ string: ["seed"],
40
+ boolean: ["help", "force"],
32
41
  alias: { h: "help" },
33
42
  });
34
43
 
@@ -40,6 +49,9 @@ ${pc.bold("Usage:")} zudo-doc <subcommand> [options]
40
49
  ${pc.bold("Subcommands:")}
41
50
  eject <component> Copy a component's TS source into your project and
42
51
  rewrite imports so it resolves locally.
52
+ eject logo [--seed <name>] [--force]
53
+ Render public/img/logo.svg and rewrite zfb.config.ts's
54
+ logo field to point at it.
43
55
  theme list List the installed theme packs and which one is active.
44
56
  theme apply <slug> Rewrite zfb.config.ts's themePack field to <slug>.
45
57
 
@@ -47,7 +59,9 @@ ${pc.bold("Ejectable components:")}
47
59
  ${validNames}
48
60
 
49
61
  ${pc.bold("Options:")}
50
- -h, --help Show this help message
62
+ -h, --help Show this help message
63
+ --seed <name> (eject logo) Seed for the generated logo, overriding siteName
64
+ --force (eject logo) Overwrite an existing public/img/logo.svg
51
65
 
52
66
  ${pc.bold("Examples:")}
53
67
  ${pc.dim("# Eject the header component")}
@@ -56,6 +70,9 @@ ${pc.bold("Examples:")}
56
70
  ${pc.dim("# Eject the theme-toggle component")}
57
71
  zudo-doc eject theme-toggle
58
72
 
73
+ ${pc.dim("# Eject the logo as a standalone SVG")}
74
+ zudo-doc eject logo
75
+
59
76
  ${pc.dim("# List installed theme packs")}
60
77
  zudo-doc theme list
61
78
 
@@ -77,6 +94,29 @@ async function runEject(componentArg) {
77
94
  await eject(componentArg, { cwd: process.cwd() });
78
95
  }
79
96
 
97
+ async function runEjectLogo() {
98
+ const hasSeedFlag = Object.prototype.hasOwnProperty.call(argv, "seed");
99
+ if (hasSeedFlag && argv.seed.length === 0) {
100
+ console.error(
101
+ pc.red(`Missing value for --seed.`) +
102
+ `\nUsage: zudo-doc eject logo [--seed <name>] [--force]`,
103
+ );
104
+ process.exit(1);
105
+ }
106
+
107
+ const result = await ejectLogo({
108
+ cwd: process.cwd(),
109
+ seed: hasSeedFlag ? argv.seed : undefined,
110
+ force: argv.force,
111
+ });
112
+
113
+ if (!result.ok) {
114
+ console.error(pc.red(result.message));
115
+ process.exit(1);
116
+ }
117
+ console.log(pc.green(result.message));
118
+ }
119
+
80
120
  async function runThemeList() {
81
121
  const result = await listThemePacks({ cwd: process.cwd() });
82
122
  console.log(formatThemeList(result));
@@ -120,6 +160,7 @@ async function main() {
120
160
 
121
161
  const [subcommand, ...rest] = argv._;
122
162
 
163
+ if (subcommand === "eject" && rest[0] === "logo") return runEjectLogo();
123
164
  if (subcommand === "eject") return runEject(rest[0]);
124
165
  if (subcommand === "theme") return runTheme(rest);
125
166
 
@@ -0,0 +1,18 @@
1
+ /** @jsxRuntime automatic */
2
+ /** @jsxImportSource preact */
3
+ import type { JSX } from "preact";
4
+ import { pickGlyphName } from "./shapes.js";
5
+ export { pickGlyphName };
6
+ export { renderAutoLogoStandaloneSvg } from "./standalone.js";
7
+ /** Props for {@link AutoLogo}. */
8
+ export interface AutoLogoProps {
9
+ /** Deterministic seed — pass `settings.siteName`. */
10
+ seed: string;
11
+ /** Class list for the root `<svg>` (sizing/color, e.g. `w-[320px] text-fg`). */
12
+ class?: string;
13
+ }
14
+ /**
15
+ * Generated "decorated plate" logo — plate + frame + corner rays + disc +
16
+ * seeded line-art glyph. Server-rendered, deterministic per seed.
17
+ */
18
+ export declare function AutoLogo({ seed, class: className }: AutoLogoProps): JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { jsx, jsxs } from "preact/jsx-runtime";
2
+ import { h } from "preact";
3
+ import {
4
+ W,
5
+ H,
6
+ PLATE_SHAPE,
7
+ INNER_FRAME_SHAPE,
8
+ RAY_SHAPES,
9
+ DISC_SHAPE,
10
+ GLYPH_SHAPES,
11
+ GLYPH_TRANSFORM,
12
+ pickGlyphName
13
+ } from "./shapes.js";
14
+ import { renderAutoLogoStandaloneSvg } from "./standalone.js";
15
+ function renderShape({ el, attrs }, key) {
16
+ return h(el, key !== void 0 ? { ...attrs, key } : attrs);
17
+ }
18
+ function AutoLogo({ seed, class: className }) {
19
+ const glyphName = pickGlyphName(seed);
20
+ const glyphShapes = GLYPH_SHAPES[glyphName];
21
+ return /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${W} ${H}`, fill: "currentColor", class: className, "data-auto-logo": glyphName, "aria-hidden": "true", children: [
22
+ renderShape(PLATE_SHAPE),
23
+ renderShape(INNER_FRAME_SHAPE),
24
+ RAY_SHAPES.map((ray, i) => renderShape(ray, i)),
25
+ renderShape(DISC_SHAPE),
26
+ /* @__PURE__ */ jsx("g", { transform: GLYPH_TRANSFORM, children: glyphShapes.map((shape, i) => renderShape(shape, i)) })
27
+ ] });
28
+ }
29
+ export {
30
+ AutoLogo,
31
+ pickGlyphName,
32
+ renderAutoLogoStandaloneSvg
33
+ };
@@ -0,0 +1,32 @@
1
+ /** One SVG primitive as plain data: element name + its attributes. */
2
+ export interface ShapePrimitive {
3
+ el: "path" | "line" | "circle" | "rect";
4
+ attrs: Record<string, string | number>;
5
+ }
6
+ export declare const W = 200;
7
+ export declare const H = 105;
8
+ export declare const INSET = 6;
9
+ export declare const CX: number;
10
+ export declare const CY: number;
11
+ export declare const DISC_R = 34;
12
+ /** Full-bleed plate rect. */
13
+ export declare const PLATE_SHAPE: ShapePrimitive;
14
+ /** Thin inner frame stroke, knocked out against the page background. */
15
+ export declare const INNER_FRAME_SHAPE: ShapePrimitive;
16
+ /** 4 diagonal corner rays, each stopping just short of the disc edge. */
17
+ export declare const RAY_SHAPES: ShapePrimitive[];
18
+ /** Centered disc, knocked out against the page background. */
19
+ export declare const DISC_SHAPE: ShapePrimitive;
20
+ /** Scale + translate that centers a glyph's 100×100 box onto the disc
21
+ * (slight overscan reads better). */
22
+ export declare const GLYPH_SCALE: number;
23
+ export declare const GLYPH_TRANSFORM = "translate(64.3, 16.799999999999997) scale(0.7140000000000001)";
24
+ /** Line-art glyphs drawn in a 100×100 box centered at (50,50), as plain
25
+ * shape data. The original JSX wrapped each glyph (and some repeated
26
+ * groups) in a `<g>` that carried no attributes of its own — only React
27
+ * `key`s for list rendering — so flattening into a single primitive array
28
+ * per glyph loses no visual information. */
29
+ export declare const GLYPH_SHAPES: Record<string, ShapePrimitive[]>;
30
+ export declare const GLYPH_NAMES: string[];
31
+ /** Deterministic glyph name for a seed. */
32
+ export declare function pickGlyphName(seed: string): string;
@@ -0,0 +1,183 @@
1
+ const W = 200;
2
+ const H = 105;
3
+ const INSET = 6;
4
+ const CX = W / 2;
5
+ const CY = H / 2;
6
+ const DISC_R = 34;
7
+ const KO = "var(--color-bg)";
8
+ const PLATE_SHAPE = {
9
+ el: "rect",
10
+ attrs: { x: 0, y: 0, width: W, height: H, fill: "currentColor" }
11
+ };
12
+ const INNER_FRAME_SHAPE = {
13
+ el: "rect",
14
+ attrs: {
15
+ x: INSET,
16
+ y: INSET,
17
+ width: W - INSET * 2,
18
+ height: H - INSET * 2,
19
+ fill: "none",
20
+ stroke: KO,
21
+ "stroke-width": 1.6
22
+ }
23
+ };
24
+ const RAY_SHAPES = [
25
+ [INSET, INSET],
26
+ [W - INSET, INSET],
27
+ [INSET, H - INSET],
28
+ [W - INSET, H - INSET]
29
+ ].map(([x, y]) => {
30
+ const dx = CX - x;
31
+ const dy = CY - y;
32
+ const len = Math.hypot(dx, dy);
33
+ const stop = (len - DISC_R - 7) / len;
34
+ return {
35
+ el: "line",
36
+ attrs: { x1: x, y1: y, x2: x + dx * stop, y2: y + dy * stop, stroke: KO, "stroke-width": 1.4 }
37
+ };
38
+ });
39
+ const DISC_SHAPE = {
40
+ el: "circle",
41
+ attrs: { cx: CX, cy: CY, r: DISC_R, fill: KO }
42
+ };
43
+ const GLYPH_SCALE = DISC_R * 2 / 100 * 1.05;
44
+ const GLYPH_TRANSFORM = `translate(${CX - 50 * GLYPH_SCALE}, ${CY - 50 * GLYPH_SCALE}) scale(${GLYPH_SCALE})`;
45
+ const GLYPH_SHAPES = {
46
+ book: [
47
+ {
48
+ el: "path",
49
+ attrs: {
50
+ d: "M50 30 C42 24, 30 23, 22 26 L22 68 C30 65, 42 66, 50 72 C58 66, 70 65, 78 68 L78 26 C70 23, 58 24, 50 30 Z",
51
+ fill: "none",
52
+ stroke: "currentColor",
53
+ "stroke-width": 6,
54
+ "stroke-linejoin": "round"
55
+ }
56
+ },
57
+ { el: "line", attrs: { x1: 50, y1: 30, x2: 50, y2: 72, stroke: "currentColor", "stroke-width": 5 } },
58
+ ...[0, 1, 2].flatMap(
59
+ (i) => [
60
+ {
61
+ el: "line",
62
+ attrs: {
63
+ x1: 29,
64
+ y1: 38 + i * 10,
65
+ x2: 43,
66
+ y2: 40 + i * 10,
67
+ stroke: "currentColor",
68
+ "stroke-width": 4,
69
+ "stroke-linecap": "round"
70
+ }
71
+ },
72
+ {
73
+ el: "line",
74
+ attrs: {
75
+ x1: 57,
76
+ y1: 40 + i * 10,
77
+ x2: 71,
78
+ y2: 38 + i * 10,
79
+ stroke: "currentColor",
80
+ "stroke-width": 4,
81
+ "stroke-linecap": "round"
82
+ }
83
+ }
84
+ ]
85
+ )
86
+ ],
87
+ doc: [
88
+ {
89
+ el: "path",
90
+ attrs: { d: "M32 22 L60 22 L70 32 L70 78 L32 78 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" }
91
+ },
92
+ {
93
+ el: "path",
94
+ attrs: { d: "M60 22 L60 32 L70 32", fill: "none", stroke: "currentColor", "stroke-width": 5, "stroke-linejoin": "round" }
95
+ },
96
+ ...[0, 1, 2].map(
97
+ (i) => ({
98
+ el: "line",
99
+ attrs: { x1: 40, y1: 44 + i * 11, x2: 62, y2: 44 + i * 11, stroke: "currentColor", "stroke-width": 4, "stroke-linecap": "round" }
100
+ })
101
+ )
102
+ ],
103
+ bookmark: [
104
+ {
105
+ el: "path",
106
+ attrs: { d: "M34 22 L66 22 L66 78 L50 64 L34 78 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" }
107
+ },
108
+ { el: "line", attrs: { x1: 42, y1: 36, x2: 58, y2: 36, stroke: "currentColor", "stroke-width": 4, "stroke-linecap": "round" } }
109
+ ],
110
+ compass: [
111
+ { el: "circle", attrs: { cx: 50, cy: 50, r: 26, fill: "none", stroke: "currentColor", "stroke-width": 6 } },
112
+ { el: "path", attrs: { d: "M60 40 L54 56 L40 60 L46 44 Z", fill: "currentColor" } }
113
+ ],
114
+ terminal: [
115
+ { el: "rect", attrs: { x: 24, y: 30, width: 52, height: 40, rx: 5, fill: "none", stroke: "currentColor", "stroke-width": 6 } },
116
+ {
117
+ el: "path",
118
+ attrs: { d: "M33 42 L41 50 L33 58", fill: "none", stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round", "stroke-linejoin": "round" }
119
+ },
120
+ { el: "line", attrs: { x1: 47, y1: 58, x2: 60, y2: 58, stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round" } }
121
+ ],
122
+ bulb: [
123
+ {
124
+ el: "path",
125
+ attrs: {
126
+ d: "M50 22 C37 22, 30 32, 30 41 C30 49, 35 53, 39 58 L39 64 L61 64 L61 58 C65 53, 70 49, 70 41 C70 32, 63 22, 50 22 Z",
127
+ fill: "none",
128
+ stroke: "currentColor",
129
+ "stroke-width": 6,
130
+ "stroke-linejoin": "round"
131
+ }
132
+ },
133
+ { el: "line", attrs: { x1: 42, y1: 71, x2: 58, y2: 71, stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round" } },
134
+ { el: "line", attrs: { x1: 45, y1: 78, x2: 55, y2: 78, stroke: "currentColor", "stroke-width": 5, "stroke-linecap": "round" } }
135
+ ],
136
+ chat: [
137
+ {
138
+ el: "path",
139
+ attrs: { d: "M26 28 L74 28 L74 62 L48 62 L36 74 L36 62 L26 62 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" }
140
+ },
141
+ ...[0, 1, 2].map((i) => ({ el: "circle", attrs: { cx: 38 + i * 12, cy: 45, r: 3, fill: "currentColor" } }))
142
+ ],
143
+ layers: [
144
+ { el: "path", attrs: { d: "M50 22 L78 36 L50 50 L22 36 Z", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linejoin": "round" } },
145
+ {
146
+ el: "path",
147
+ attrs: { d: "M25 47 L50 60 L75 47", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linecap": "round", "stroke-linejoin": "round" }
148
+ },
149
+ {
150
+ el: "path",
151
+ attrs: { d: "M25 58 L50 71 L75 58", fill: "none", stroke: "currentColor", "stroke-width": 6, "stroke-linecap": "round", "stroke-linejoin": "round" }
152
+ }
153
+ ]
154
+ };
155
+ const GLYPH_NAMES = Object.keys(GLYPH_SHAPES);
156
+ function hashString(str) {
157
+ let h = 2166136261 >>> 0;
158
+ for (let i = 0; i < str.length; i++) {
159
+ h ^= str.charCodeAt(i);
160
+ h = Math.imul(h, 16777619);
161
+ }
162
+ return h >>> 0;
163
+ }
164
+ function pickGlyphName(seed) {
165
+ return GLYPH_NAMES[hashString(seed) % GLYPH_NAMES.length];
166
+ }
167
+ export {
168
+ CX,
169
+ CY,
170
+ DISC_R,
171
+ DISC_SHAPE,
172
+ GLYPH_NAMES,
173
+ GLYPH_SCALE,
174
+ GLYPH_SHAPES,
175
+ GLYPH_TRANSFORM,
176
+ H,
177
+ INNER_FRAME_SHAPE,
178
+ INSET,
179
+ PLATE_SHAPE,
180
+ RAY_SHAPES,
181
+ W,
182
+ pickGlyphName
183
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Render the deterministic AutoLogo design as a standalone, mask-friendly
3
+ * SVG string. Same seed always produces byte-identical output; glyph
4
+ * selection reuses {@link pickGlyphName} so an ejected file shows the same
5
+ * glyph as `logo: "auto"` mode.
6
+ */
7
+ export declare function renderAutoLogoStandaloneSvg(seed: string): string;
@@ -0,0 +1,35 @@
1
+ import {
2
+ W,
3
+ H,
4
+ PLATE_SHAPE,
5
+ INNER_FRAME_SHAPE,
6
+ RAY_SHAPES,
7
+ DISC_SHAPE,
8
+ GLYPH_SHAPES,
9
+ GLYPH_TRANSFORM,
10
+ pickGlyphName
11
+ } from "./shapes.js";
12
+ const MASK_ID = "auto-logo-mask";
13
+ const MASK_COLORS = {
14
+ currentColor: "#fff",
15
+ "var(--color-bg)": "#000"
16
+ };
17
+ function escapeAttr(value) {
18
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
19
+ }
20
+ function renderShape(shape) {
21
+ const attrs = Object.entries(shape.attrs).map(([key, value]) => {
22
+ const resolved = typeof value === "string" && value in MASK_COLORS ? MASK_COLORS[value] : value;
23
+ return `${key}="${escapeAttr(String(resolved))}"`;
24
+ }).join(" ");
25
+ return `<${shape.el} ${attrs} />`;
26
+ }
27
+ function renderAutoLogoStandaloneSvg(seed) {
28
+ const glyphName = pickGlyphName(seed);
29
+ const glyphShapes = GLYPH_SHAPES[glyphName];
30
+ const maskContent = renderShape(PLATE_SHAPE) + renderShape(INNER_FRAME_SHAPE) + RAY_SHAPES.map(renderShape).join("") + renderShape(DISC_SHAPE) + `<g transform="${escapeAttr(GLYPH_TRANSFORM)}">${glyphShapes.map(renderShape).join("")}</g>`;
31
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" data-auto-logo="${glyphName}"><mask id="${MASK_ID}" mask-type="luminance" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="${W}" height="${H}">` + maskContent + `</mask><rect x="0" y="0" width="${W}" height="${H}" fill="#000" mask="url(#${MASK_ID})" /></svg>`;
32
+ }
33
+ export {
34
+ renderAutoLogoStandaloneSvg
35
+ };
@@ -74,9 +74,10 @@ export interface ModeMap {
74
74
  selectionBg: RampRef;
75
75
  selectionFg: RampRef;
76
76
  semantic: Record<SemanticKey, RampRef>;
77
- /** Syntax-specific overrides. The map is required, while omitted roles
78
- * inherit the aliases in `SYNTAX_SEMANTIC_ALIASES`. */
79
- syntax: Partial<Record<SyntaxSemanticKey, RampRef>>;
77
+ /** Optional syntax-specific overrides. An absent map and omitted roles
78
+ * inherit the aliases in `SYNTAX_SEMANTIC_ALIASES`, preserving schemes
79
+ * authored before syntax tokens existed. */
80
+ syntax?: Partial<Record<SyntaxSemanticKey, RampRef>>;
80
81
  }
81
82
  /** A complete color scheme — shared Tier-1 ramps + per-mode Tier-2 wiring. */
82
83
  export interface ColorScheme {
@@ -207,7 +207,7 @@ function resolveSyntaxPalette(scheme) {
207
207
  );
208
208
  const emergencyColor = tryResolveRampRef(emergencyRef, ramps) ?? "currentColor";
209
209
  for (const key of SYNTAX_SEMANTIC_KEYS) {
210
- const explicitRef = map.syntax[key];
210
+ const explicitRef = map.syntax?.[key];
211
211
  const explicitColor = tryResolveRampRef(explicitRef, ramps);
212
212
  const inheritedRef = map.semantic[SYNTAX_SEMANTIC_ALIASES[key]];
213
213
  const inheritedColor = tryResolveRampRef(inheritedRef, ramps);
@@ -343,7 +343,7 @@ const COMPONENT_TOKENS = [
343
343
  component: "sidebar",
344
344
  surface: "chrome",
345
345
  category: "typography",
346
- description: "Font family of the navigation sidebar \u2014 covers BOTH the desktop `#desktop-sidebar` rail and the mobile drawer, so one override styles both viewports and neither drifts onto a different face. Defaults to the `--zdc-chrome-font` seam (a no-op until redefined)."
346
+ description: "Font family of the navigation sidebar \u2014 covers BOTH the desktop `#desktop-sidebar` rail and the mobile drawer, so one override styles both viewports and neither drifts onto a different face. Defaults to the `--zdc-chrome-font` seam (a no-op until redefined). Verify granular MOBILE chrome-font overrides with `zfb build`/preview, not `pnpm dev`: the current zfb dev server strips island-root `data-*` attributes, so the mobile selector silently does not match."
347
347
  },
348
348
  {
349
349
  cssVar: "--zdc-toc-font",
@@ -353,7 +353,7 @@ const COMPONENT_TOKENS = [
353
353
  component: "toc",
354
354
  surface: "chrome",
355
355
  category: "typography",
356
- description: "Font family of the table of contents \u2014 covers BOTH the desktop right rail and the mobile collapsible TOC, so one override styles both viewports. Defaults to the `--zdc-chrome-font` seam (a no-op until redefined), which also keeps the mobile TOC on the chrome font rather than the prose font of the `.zd-content` it renders inside."
356
+ description: "Font family of the table of contents \u2014 covers BOTH the desktop right rail and the mobile collapsible TOC, so one override styles both viewports. Defaults to the `--zdc-chrome-font` seam (a no-op until redefined), which also keeps the mobile TOC on the chrome font rather than the prose font of the `.zd-content` it renders inside. Verify granular MOBILE chrome-font overrides with `zfb build`/preview, not `pnpm dev`: the current zfb dev server strips island-root `data-*` attributes, so the mobile selector silently does not match."
357
357
  }
358
358
  ];
359
359
  export {
package/dist/config.d.ts CHANGED
@@ -62,7 +62,7 @@ import type { ZfbConfig, BundleConfig } from "@takazudo/zfb/config";
62
62
  import type { ZodType } from "zod";
63
63
  import type { DirectiveVocabulary, PresetTranslations, PresetTagVocabularyEntry } from "./preset.js";
64
64
  import type { ColorScheme } from "./color-scheme-utils.js";
65
- import type { Settings, ColorModeConfig, LocaleConfig, MetaTagsConfig, SiteHeadConfig, TagPlacement, TagGovernanceMode, VersionConfig, FooterConfig, HeaderNavItem, HeaderRightItem, FrontmatterPreviewConfig, BodyFootUtilAreaConfig, HtmlPreviewConfig } from "./settings.js";
65
+ import type { Settings, ColorModeConfig, LocaleConfig, MetaTagsConfig, SiteHeadConfig, TagPlacement, TagGovernanceMode, VersionConfig, FooterConfig, HeaderNavItem, HeaderRightItem, HomeConfig, FrontmatterPreviewConfig, BodyFootUtilAreaConfig, HtmlPreviewConfig } from "./settings.js";
66
66
  /** The `settings.claudeResources` block (or `false` when disabled). */
67
67
  type ClaudeResourcesConfig = {
68
68
  claudeDir: string;
@@ -92,6 +92,14 @@ export interface ZudoDocConfig {
92
92
  * @default ""
93
93
  */
94
94
  siteDescription?: string;
95
+ /**
96
+ * Home-hero logo. `"auto"` renders a generated deterministic SVG seeded by
97
+ * `siteName` (no asset needed); a path string (e.g. `"/img/logo.svg"`)
98
+ * renders that asset as a theme-adaptive CSS mask; `false` hides the logo
99
+ * block entirely.
100
+ * @default "auto"
101
+ */
102
+ logo?: string | false;
95
103
  /**
96
104
  * Public URL sub-path prefix mounted in front of every absolute asset URL
97
105
  * (e.g. `"/pj/my-site/"`). `"/"` = root-mounted.
@@ -103,6 +111,12 @@ export interface ZudoDocConfig {
103
111
  * @default false
104
112
  */
105
113
  trailingSlash?: boolean;
114
+ /**
115
+ * Package-owned home-page layout. Set `wide: true` to let the category grid
116
+ * fill most of the viewport on both `/` and locale home routes.
117
+ * @default { wide: false }
118
+ */
119
+ home?: HomeConfig;
106
120
  /**
107
121
  * Minify production HTML output from `zfb build`.
108
122
  * @default true
@@ -300,6 +314,14 @@ export interface ZudoDocConfig {
300
314
  * @default false
301
315
  */
302
316
  docHistory?: boolean;
317
+ /**
318
+ * Glob patterns matched against the doc slug (path minus extension, `/index`
319
+ * stripped, root = `index`) that exclude matching pages from git-history
320
+ * capture entirely. Excluded pages have no dropdown JSON or
321
+ * Created/Updated/Author block; matching is locale/version-independent.
322
+ * @default []
323
+ */
324
+ docHistoryExclude?: string[];
303
325
  /**
304
326
  * Body-foot utility area (doc-history / view-source), or `false` to disable.
305
327
  * @default false
package/dist/config.js CHANGED
@@ -13,8 +13,10 @@ const DEFAULT_SETTINGS = {
13
13
  },
14
14
  siteName: "Docs",
15
15
  siteDescription: "",
16
+ logo: "auto",
16
17
  base: "/",
17
18
  trailingSlash: false,
19
+ home: { wide: false },
18
20
  minifyHtml: true,
19
21
  docsDir: "src/content/docs",
20
22
  defaultLocale: "en",
@@ -58,6 +60,7 @@ const DEFAULT_SETTINGS = {
58
60
  dynamicPageTransition: false,
59
61
  frontmatterPreview: false,
60
62
  docHistory: false,
63
+ docHistoryExclude: [],
61
64
  bodyFootUtilArea: false,
62
65
  htmlPreview: void 0,
63
66
  versions: false,
package/dist/content.css CHANGED
@@ -144,12 +144,13 @@
144
144
  /* ── Links ── */
145
145
 
146
146
  /* Link reset inside site-nav (component can't detect ancestor context) */
147
- .zd-content [data-site-nav] a {
147
+ /* Reset must stay specificity-0 (:where()) so island utility classes like hover:text-accent control color */
148
+ :where(.zd-content [data-site-nav] a) {
148
149
  color: inherit;
149
150
  text-decoration: none;
150
151
  }
151
- .zd-content [data-site-nav] a:hover,
152
- .zd-content [data-site-nav] a:focus-visible {
152
+ :where(.zd-content [data-site-nav] a):hover,
153
+ :where(.zd-content [data-site-nav] a):focus-visible {
153
154
  text-decoration: underline;
154
155
  }
155
156
 
@@ -19,7 +19,7 @@ function createDocContentHeader(ctx) {
19
19
  version
20
20
  }) {
21
21
  return /* @__PURE__ */ jsxs(Fragment, { children: [
22
- /* @__PURE__ */ jsx("h1", { class: "text-heading font-bold mb-vsp-xs", children: entry.data.title }),
22
+ /* @__PURE__ */ jsx("h1", { class: "text-heading font-bold border-b border-fg pb-vsp-xs mb-vsp-xs", children: entry.data.title }),
23
23
  docContentHeaderExtras?.({
24
24
  entry,
25
25
  slug,
@@ -339,7 +339,7 @@ function DocHistory({ slug, locale, basePath = "/" }) {
339
339
  {
340
340
  type: "button",
341
341
  onClick: handleOpen,
342
- className: "doc-history-trigger flex items-center gap-hsp-xs px-hsp-md py-vsp-xs rounded-lg bg-surface border border-muted text-muted hover:text-fg hover:border-fg transition-colors",
342
+ className: "doc-history-trigger flex items-center gap-hsp-xs px-hsp-md py-vsp-xs rounded-lg bg-surface border border-muted text-muted hover:text-accent hover:border-accent focus-visible:text-accent focus-visible:border-accent transition-colors",
343
343
  "aria-label": "View document history",
344
344
  children: [
345
345
  /* @__PURE__ */ jsx(History, { className: "h-icon-md w-icon-md" }),