@dogsbay/format-astro 0.2.0-beta.15 → 0.2.0-beta.150

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 (67) hide show
  1. package/dist/api-markdown.d.ts +7 -0
  2. package/dist/api-markdown.js +49 -0
  3. package/dist/base-path.d.ts +93 -8
  4. package/dist/base-path.js +122 -9
  5. package/dist/blog.d.ts +133 -0
  6. package/dist/blog.js +325 -0
  7. package/dist/browser.d.ts +13 -0
  8. package/dist/browser.js +13 -0
  9. package/dist/canonical.d.ts +35 -0
  10. package/dist/canonical.js +110 -0
  11. package/dist/capabilities.d.ts +28 -0
  12. package/dist/capabilities.js +94 -0
  13. package/dist/cli.d.ts +0 -1
  14. package/dist/cli.js +3 -1
  15. package/dist/diff-decoration.d.ts +35 -0
  16. package/dist/diff-decoration.js +101 -0
  17. package/dist/granularity.d.ts +82 -0
  18. package/dist/granularity.js +246 -0
  19. package/dist/head-metadata.d.ts +25 -0
  20. package/dist/head-metadata.js +55 -0
  21. package/dist/index.d.ts +26 -6
  22. package/dist/index.js +34 -5
  23. package/dist/lead.d.ts +1 -39
  24. package/dist/lead.js +3 -38
  25. package/dist/llms-txt.d.ts +41 -11
  26. package/dist/llms-txt.js +82 -274
  27. package/dist/plugins.d.ts +0 -1
  28. package/dist/plugins.js +4 -3
  29. package/dist/project.d.ts +461 -13
  30. package/dist/project.js +2343 -649
  31. package/dist/redirects.d.ts +117 -0
  32. package/dist/redirects.js +223 -0
  33. package/dist/serialize.d.ts +14 -1
  34. package/dist/serialize.js +857 -255
  35. package/dist/site-chrome.d.ts +60 -0
  36. package/dist/site-chrome.js +56 -0
  37. package/dist/sitemap.d.ts +62 -4
  38. package/dist/sitemap.js +63 -56
  39. package/dist/taxonomy.d.ts +48 -2
  40. package/dist/taxonomy.js +50 -12
  41. package/dist/theme-css.d.ts +33 -0
  42. package/dist/theme-css.js +683 -0
  43. package/dist/tone-classes.d.ts +52 -0
  44. package/dist/tone-classes.js +84 -0
  45. package/dist/write-if-changed.d.ts +20 -0
  46. package/dist/write-if-changed.js +57 -0
  47. package/package.json +17 -7
  48. package/dist/base-path.d.ts.map +0 -1
  49. package/dist/base-path.js.map +0 -1
  50. package/dist/cli.d.ts.map +0 -1
  51. package/dist/cli.js.map +0 -1
  52. package/dist/index.d.ts.map +0 -1
  53. package/dist/index.js.map +0 -1
  54. package/dist/lead.d.ts.map +0 -1
  55. package/dist/lead.js.map +0 -1
  56. package/dist/llms-txt.d.ts.map +0 -1
  57. package/dist/llms-txt.js.map +0 -1
  58. package/dist/plugins.d.ts.map +0 -1
  59. package/dist/plugins.js.map +0 -1
  60. package/dist/project.d.ts.map +0 -1
  61. package/dist/project.js.map +0 -1
  62. package/dist/serialize.d.ts.map +0 -1
  63. package/dist/serialize.js.map +0 -1
  64. package/dist/sitemap.d.ts.map +0 -1
  65. package/dist/sitemap.js.map +0 -1
  66. package/dist/taxonomy.d.ts.map +0 -1
  67. package/dist/taxonomy.js.map +0 -1
@@ -0,0 +1,683 @@
1
+ /**
2
+ * The stylesheets a generated Astro site starts from: global.css
3
+ * (generateGlobalCss: element display rules, .docs-prose, the tone safelist)
4
+ * and theme.css (themeCss: the Tailwind theme mapping plus a preset's tokens).
5
+ *
6
+ * Pure (no I/O; it imports only the tone palette) so other packages can use
7
+ * the same strings: @dogsbay/render-html bundles this module to write its
8
+ * css/ (packages/render-html/scripts/sources.mjs), which dogsbay-ssg and the
9
+ * single-page renderer use.
10
+ */
11
+ import { TONE_CLASSES, CARD_TONE_CLASSES, TAB_VARIANTS, THEME_SCOPES } from "./tone-classes.js";
12
+ // ── CSS generation (ported from import-mkdocs.ts) ───────
13
+ /**
14
+ * Build the `@source inline("...")` directive that pins the
15
+ * grid-tone palette into the generated stylesheet.
16
+ *
17
+ * Why we need it: tone classes like `bg-primary/10` only appear in
18
+ * `.astro` pages emitted by Dogsbay's grid-item serializer. When
19
+ * Tailwind's content scanner doesn't pick them up — because the
20
+ * page lives outside the default scan globs, or because a class
21
+ * is composed at the boundary of an interpolation — they get
22
+ * purged. Result observed in dogsbay-docs-markdown audit: half
23
+ * the grid demo cells render with no background. Pinning forces
24
+ * generation regardless of scanner reach.
25
+ *
26
+ * Single source of truth: derived from TONE_CLASSES so any new
27
+ * tone added to the palette is automatically safelisted.
28
+ */
29
+ export function buildToneSafelist() {
30
+ const seen = new Set();
31
+ for (const classes of Object.values(TONE_CLASSES)) {
32
+ for (const cls of classes.split(/\s+/)) {
33
+ if (cls)
34
+ seen.add(cls);
35
+ }
36
+ }
37
+ return [...seen].sort().join(" ");
38
+ }
39
+ /**
40
+ * The same pin for the variant, tone and theme-scope tables
41
+ * (plans/dita-variants-themes.md). A line of its own, marked ASTRO-ONLY:
42
+ * render-html copies this stylesheet, and a renderer that compiles from the
43
+ * page's own classes (render-html, dogsbay-ssg) needs no safelist — there it
44
+ * put every variant's classes on every page (scripts/sources.mjs drops it).
45
+ */
46
+ export function buildVariantSafelist() {
47
+ const seen = new Set();
48
+ const sources = [
49
+ ...Object.values(CARD_TONE_CLASSES),
50
+ ...Object.values(TAB_VARIANTS).flatMap((v) => [v.list, v.trigger]),
51
+ ...Object.values(THEME_SCOPES),
52
+ ];
53
+ for (const classes of sources)
54
+ for (const cls of classes.split(/\s+/))
55
+ if (cls)
56
+ seen.add(cls);
57
+ return [...seen].sort().join(" ");
58
+ }
59
+ export function generateGlobalCss() {
60
+ return `@import "tailwindcss";
61
+ @import "./theme.css";
62
+
63
+ /* Custom elements have no user-agent style, so an unstyled one is
64
+ display:inline — on which w-full does nothing and a border paints
65
+ as detached fragments rather than around the content. @dogsbay/elements
66
+ injects these same defaults at runtime, which is what reaches EXISTING
67
+ sites (global.css is scaffolded once and never rewritten). Having them
68
+ here too means a new site is correct at CSS time, with no flash before
69
+ the module script runs.
70
+
71
+ :where() keeps specificity at zero, so any utility class wins. */
72
+ :where(db-tabs, db-accordion, db-collapsible, db-card, db-steps, db-code-block) {
73
+ display: block;
74
+ }
75
+ :where(db-link-button) {
76
+ display: inline-block;
77
+ }
78
+
79
+ /* Scan @dogsbay packages for Tailwind classes */
80
+ @source "../../node_modules/@dogsbay/ui/src";
81
+ @source "../../node_modules/@dogsbay/docs-layout/src";
82
+
83
+ /* Pin the grid-tone palette. These classes are emitted into
84
+ markdown-generated .astro pages by the grid-item serializer
85
+ (TONE_CLASSES in @dogsbay/format-astro). Without inlining,
86
+ opacity-modified utilities like bg-primary/10 get purged when
87
+ Tailwind doesn't see them in the scanned globs, leaving grid
88
+ demo cells with no visible background. */
89
+ @source inline("${buildToneSafelist()}");
90
+ @source inline("${buildVariantSafelist()}"); /* ASTRO-ONLY: variants, tones, theme scopes */
91
+
92
+ /* Prose typography for rendered content.
93
+ Every element rule is guarded with :not(.dba-api *): API-reference
94
+ regions (ApiLayout root carries the dba-api marker) compose their own
95
+ typography from utility classes, and unguarded .docs-prose rules would
96
+ out-specify them. The guard lets mixed prose+endpoint pages (MDX
97
+ imports) keep full typography outside the cards. */
98
+ .docs-prose {
99
+ line-height: 1.7;
100
+
101
+ /* A heading that is part of a component (a card title: h3.not-prose) keeps the component look */
102
+ & h1:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 2rem; font-weight: 700; margin-top: 0; margin-bottom: 1rem; line-height: 1.2; letter-spacing: -0.025em; }
103
+ & h2:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 1.5rem; font-weight: 600; margin-top: 2.5rem; margin-bottom: 0.75rem; line-height: 1.3; border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; letter-spacing: -0.015em; }
104
+ & h3:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 1.25rem; font-weight: 600; margin-top: 2rem; margin-bottom: 0.5rem; line-height: 1.4; }
105
+ & h4:not(.dba-api *):not(.not-prose) { font-family: var(--font-heading); font-size: 1.1rem; font-weight: 600; margin-top: 1.5rem; margin-bottom: 0.5rem; }
106
+
107
+ & p:not(.dba-api *) { margin-top: 0.75rem; margin-bottom: 0.75rem; }
108
+
109
+ /* The :not([data-variant]) guard exempts anchors that ARE components. A
110
+ link-button renders as an <a> carrying bg-primary + text-primary-foreground
111
+ and a data-variant attribute, and this rule repainted its text with
112
+ var(--primary) — primary on primary: an unreadable solid block in light
113
+ mode, a blank one in dark. The secondary variant survived only because its
114
+ background is light enough to read primary-coloured text, which is why it
115
+ looked like a colour bug in one button rather than a cascade bug in both.
116
+ (No backticks in this comment: the whole stylesheet is a template
117
+ literal.) */
118
+ /* Not a whole-card link (LinkCard, the video card: \`a.not-prose\`): its underline ran under every line of the card */
119
+ & a:not(.dba-api *):not([data-variant]):not(.not-prose) { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
120
+ & a:hover:not(.dba-api *):not([data-variant]):not(.not-prose) { opacity: 0.8; }
121
+
122
+ & strong:not(.dba-api *) { font-weight: 600; }
123
+ & code:not(.dba-api *) { font-size: 0.875em; padding: 0.15em 0.35em; border-radius: 0.25rem; background: var(--muted); font-family: var(--font-code, ui-monospace, monospace); }
124
+ & pre code:not(.dba-api *) { padding: 0; background: none; font-size: 1em; }
125
+
126
+ /* Spacing between consecutive block elements (code blocks, alerts, etc.) */
127
+ & > * + * { margin-top: 0.75rem; }
128
+ & li > * + *:not(.dba-api *) { margin-top: 0.75rem; }
129
+
130
+ & ul:not(.dba-api *) { list-style: disc; padding-left: 1.5rem; margin: 0.75rem 0; }
131
+ /* Not a step list, nor a list inside one: Steps.astro draws their numbers as circles, and this
132
+ rule (one class and an element) outranked its .steps-list { list-style: none }, so each step
133
+ showed its "2." beside its circle */
134
+ & ol:not(.dba-api *):not(.steps-list, .sub-steps-list, .steps-list ol) { list-style: decimal; padding-left: 1.5rem; margin: 0.75rem 0; }
135
+ & li:not(.dba-api *) { margin: 0.25rem 0; }
136
+ & li > ul:not(.dba-api *), & li > ol:not(.dba-api *) { margin: 0.25rem 0; }
137
+ /* A file tree draws its own folder and file icons (files/markup.ts): the list rules above gave it bullets too */
138
+ & [data-component="files"] ul:not(.dba-api *) { list-style: none; padding-left: 0; }
139
+ & [data-component="files"] li > ul:not(.dba-api *) { padding-left: 1rem; }
140
+
141
+ & blockquote:not(.dba-api *) { border-left: 4px solid var(--border); padding-left: 1rem; color: var(--muted-foreground); font-style: italic; margin: 1rem 0; }
142
+
143
+ & hr:not(.dba-api *) { border: none; border-top: 1px solid var(--border); margin: 2rem 0; }
144
+
145
+ & table:not(.dba-api *) { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.875rem; }
146
+ & th:not(.dba-api *) { text-align: left; vertical-align: bottom; font-weight: 600; padding: 0.5rem; border-bottom: 2px solid var(--border); }
147
+ & td:not(.dba-api *) { padding: 0.5rem; vertical-align: top; border-bottom: 1px solid var(--border); }
148
+ /* A table's caption is its title, above it; the same for Table components and raw HTML tables */
149
+ & caption:not(.dba-api *) { caption-side: top; text-align: left; font-weight: 500; color: var(--foreground); padding-bottom: 0.5rem; }
150
+
151
+ /* Tailwind Preflight sets display: block on every img, which turns an
152
+ INLINE icon into its own line and splits the sentence around it —
153
+ AsciiDoc UI macros render mid-step and were breaking every procedure
154
+ that used one. inline-block restores the flow and leaves a standalone
155
+ image (the only child of its paragraph) looking exactly as before. */
156
+ & img:not(.dba-api *) { display: inline-block; max-width: 100%; border-radius: 0.5rem; }
157
+ /* A standalone image — the only child of its paragraph — keeps block
158
+ layout, so the vertical rhythm and the absence of baseline descender
159
+ space are exactly what they were before inline icons were fixed. */
160
+ & p > img:only-child:not(.dba-api *) { display: block; }
161
+
162
+ & .heading-anchor { text-decoration: none; opacity: 0; margin-right: 0.25rem; transition: opacity 0.2s; }
163
+ & h1:hover .heading-anchor, & h2:hover .heading-anchor, & h3:hover .heading-anchor, & h4:hover .heading-anchor { opacity: 0.4; }
164
+
165
+ /* Raw HTML <details> only. CollapsibleAlert (:::details, collapsible
166
+ callouts) is a <details> too, with its own border and spacing; it
167
+ carries data-variant, and without the exclusion it got a second border
168
+ and 1rem of padding around its title bar. */
169
+ & details:not(.dba-api *):not([data-variant]) { border: 1px solid var(--border); border-radius: 0.5rem; padding: 1rem; margin: 1rem 0; }
170
+ & summary:not(.dba-api *) { cursor: pointer; font-weight: 600; }
171
+
172
+ /* mark is emitted by the highlight directive AND can arrive as raw HTML
173
+ from an importer. Without a rule here it renders as the browser default
174
+ yellow, which clashes in light mode and is unreadable on a dark page
175
+ (nothing declares color-scheme). Token-based so it follows the theme. */
176
+ & mark:not(.dba-api *) { background: var(--dsb-mark-bg, rgb(254 240 138 / 0.6)); color: inherit; border-radius: 0.125rem; padding: 0 0.125rem; }
177
+ &:where(.dark *) mark:not(.dba-api *), .dark & mark:not(.dba-api *) { background: var(--dsb-mark-bg-dark, rgb(234 179 8 / 0.3)); }
178
+
179
+ & dl:not(.dba-api *) { margin: 1rem 0; }
180
+ & dt:not(.dba-api *) { font-weight: 600; margin-top: 0.75rem; }
181
+ & dd:not(.dba-api *) { margin-left: 1.5rem; color: var(--muted-foreground); }
182
+
183
+ /* In-cell admonitions. Inside a raw HTML table cell nothing
184
+ markdown-native works, so the AsciiDoc engine emits a neutral
185
+ dl.db-admonition shape (DD-010) and this rule renders it as a compact
186
+ callout box — same contract the Obsidian plugin styles. Per-type
187
+ accent from theme tokens; overrides the generic dl/dt/dd rules above. */
188
+ & dl.db-admonition {
189
+ margin: 0.75rem 0;
190
+ padding: 0.5rem 0.75rem;
191
+ border-left: 3px solid var(--info);
192
+ border-radius: 0.25rem;
193
+ background: color-mix(in oklab, var(--info) 8%, transparent);
194
+ }
195
+ /* The LABEL blends the accent toward the foreground; the border and the
196
+ tint keep the accent at full strength, so the callout still reads as
197
+ its colour while the text stays legible.
198
+
199
+ Measured from painted pixels on a real page, light mode, 13px/600 —
200
+ the accent used raw is a genuine AA failure, not one of axe's oklch
201
+ misreads:
202
+
203
+ --info raw 2.18:1 mixed 5.66:1
204
+ --warning raw 2.25:1 mixed 5.74:1
205
+ --destructive raw 5.97:1 mixed 10.98:1
206
+
207
+ Mixed with var(--foreground), not black, on purpose. Black fixes
208
+ light and BREAKS dark: 70% black measures 5.21:1 light but 3.25:1
209
+ dark, because the tint it sits on is dark too. --foreground is
210
+ near-black in light and near-white in dark, so one rule serves both
211
+ (dark measures 10.96 / 10.87 / 9.79). A fixed colour used in both
212
+ themes is a bug waiting. */
213
+ & dl.db-admonition > dt {
214
+ margin-top: 0;
215
+ font-weight: 600;
216
+ font-size: 0.8125rem;
217
+ text-transform: uppercase;
218
+ letter-spacing: 0.03em;
219
+ color: color-mix(in oklab, var(--info) 60%, var(--foreground));
220
+ }
221
+ & dl.db-admonition > dd { margin-left: 0; margin-top: 0.25rem; color: var(--foreground); }
222
+ & dl.db-admonition-warning, & dl.db-admonition-caution {
223
+ border-left-color: var(--warning);
224
+ background: color-mix(in oklab, var(--warning) 8%, transparent);
225
+ }
226
+ & dl.db-admonition-warning > dt, & dl.db-admonition-caution > dt { color: color-mix(in oklab, var(--warning) 60%, var(--foreground)); }
227
+ & dl.db-admonition-important {
228
+ border-left-color: var(--destructive);
229
+ background: color-mix(in oklab, var(--destructive) 8%, transparent);
230
+ }
231
+ & dl.db-admonition-important > dt { color: color-mix(in oklab, var(--destructive) 60%, var(--foreground)); }
232
+
233
+ /* Granularity book-view affordances (plans/granularity-views.md):
234
+ "Read as single page →" on topics, "Open as page ↗" on book sections.
235
+ Deliberately subtle/muted — the book has one per section. */
236
+ & .dsb-read-single-page {
237
+ display: inline-block;
238
+ margin: 0 0 1.25rem;
239
+ font-size: 0.8125rem;
240
+ color: var(--muted-foreground);
241
+ text-decoration: none;
242
+ }
243
+ & .dsb-read-single-page:hover { color: var(--foreground); text-decoration: underline; }
244
+ & .dsb-open-as-page { margin: -0.25rem 0 0.5rem; font-size: 0.75rem; }
245
+ & .dsb-open-as-page a { color: var(--muted-foreground); text-decoration: none; }
246
+ & .dsb-open-as-page a:hover { color: var(--foreground); text-decoration: underline; }
247
+
248
+ /* AsciiDoc block roles ([role="x"] / [.x]) preserved as classes (#384).
249
+ _abstract marks a module's lead/intro paragraph; .lead/.small are
250
+ Asciidoctor built-ins; _additional-resources marks the trailing
251
+ "Additional resources" section heading. */
252
+ & ._abstract, & .abstract, & .lead {
253
+ font-size: 1.05rem;
254
+ line-height: 1.6;
255
+ color: var(--muted-foreground);
256
+ }
257
+ & .small { font-size: 0.875rem; }
258
+ & h2._additional-resources, & h3._additional-resources {
259
+ font-size: 1.15rem;
260
+ margin-top: 2rem;
261
+ }
262
+
263
+ /* Related resources (#384 R3): [role="_additional-resources"] + .Title +
264
+ list folds to a <section class="related-resources"> (→ DITA related-links). */
265
+ & .related-resources {
266
+ margin-top: 2rem;
267
+ padding-top: 1rem;
268
+ border-top: 1px solid var(--border);
269
+ }
270
+ & .related-resources-title {
271
+ font-weight: 600;
272
+ margin: 0 0 0.5rem;
273
+ }
274
+ }
275
+
276
+ @utility scrollbar-none {
277
+ -ms-overflow-style: none;
278
+ scrollbar-width: none;
279
+ &::-webkit-scrollbar {
280
+ display: none;
281
+ }
282
+ }
283
+ `;
284
+ }
285
+ // ── Theme presets ───────────────────────────────────────
286
+ const THEME_DEFAULT = {
287
+ light: `:root {
288
+ --background: oklch(1 0 0);
289
+ --foreground: oklch(0.145 0.014 285.823);
290
+ --card: oklch(1 0 0);
291
+ --card-foreground: oklch(0.145 0.014 285.823);
292
+ --popover: oklch(1 0 0);
293
+ --popover-foreground: oklch(0.145 0.014 285.823);
294
+ --primary: oklch(0.205 0.014 285.823);
295
+ --primary-foreground: oklch(0.985 0 0);
296
+ --secondary: oklch(0.965 0.001 286.375);
297
+ --secondary-foreground: oklch(0.205 0.014 285.823);
298
+ --muted: oklch(0.965 0.001 286.375);
299
+ --muted-foreground: oklch(0.45 0.014 285.938);
300
+ --accent: oklch(0.965 0.001 286.375);
301
+ --accent-foreground: oklch(0.205 0.014 285.823);
302
+ --destructive: oklch(0.45 0.245 27.325);
303
+ --destructive-foreground: oklch(0.985 0 0);
304
+ --border: oklch(0.922 0.004 286.32);
305
+ --input: oklch(0.922 0.004 286.32);
306
+ --ring: oklch(0.708 0.014 285.823);
307
+ --radius: 0.625rem;
308
+ --note: oklch(0.637 0.174 259.126);
309
+ --note-foreground: var(--foreground);
310
+ --abstract: oklch(0.691 0.148 221.723);
311
+ --info: oklch(0.722 0.128 207.084);
312
+ --tip: oklch(0.731 0.112 178.627);
313
+ --success: oklch(0.754 0.175 149.577);
314
+ --question: oklch(0.809 0.19 126.526);
315
+ --warning: oklch(0.74 0.175 62.778);
316
+ --failure: oklch(0.614 0.194 21.602);
317
+ --danger: oklch(0.577 0.245 16.439);
318
+ --bug: oklch(0.548 0.258 349.761);
319
+ --example: oklch(0.534 0.222 286.033);
320
+ --quote: oklch(0.65 0 0);
321
+ --sidebar: oklch(0.985 0 0);
322
+ --sidebar-foreground: oklch(0.145 0.014 285.823);
323
+ --sidebar-primary: oklch(0.205 0.014 285.823);
324
+ --sidebar-primary-foreground: oklch(0.985 0 0);
325
+ --sidebar-accent: oklch(0.965 0.001 286.375);
326
+ --sidebar-accent-foreground: oklch(0.205 0.014 285.823);
327
+ --sidebar-border: oklch(0.922 0.004 286.32);
328
+ --sidebar-ring: oklch(0.708 0.014 285.823);
329
+ --code-background: oklch(0.965 0.004 264);
330
+ --code-foreground: oklch(0.3 0.014 253.833);
331
+
332
+ /* API reference — always-dark code panel */
333
+ --api-panel-bg: oklch(0.145 0.014 253.833);
334
+ --api-panel-fg: oklch(0.926 0.013 253.833);
335
+
336
+ /* API semantic colors — HTTP methods, types, status codes */
337
+ /* Light mode: darkened for WCAG AA on the 20%/10% tints. See .dark. */
338
+ --api-get: oklch(0.435 0.095 162);
339
+ --api-post: oklch(0.45 0.174 259);
340
+ --api-put: oklch(0.455 0.105 63);
341
+ --api-patch: oklch(0.455 0.13 45);
342
+ --api-delete: oklch(0.45 0.179 22);
343
+ --api-head: oklch(0.47 0.222 286);
344
+ --api-required: oklch(0.45 0.179 22);
345
+ --api-deprecated: oklch(0.455 0.105 63);
346
+ /* A field name in an operation's description (\`id\`, \`name\`): the JSON
347
+ key colour of the highlighted examples (github-light-default #0550ae). */
348
+ --api-field: oklch(0.47 0.16 254);
349
+ --api-type-string: oklch(0.435 0.095 162);
350
+ --api-type-number: oklch(0.45 0.174 259);
351
+ --api-type-boolean: oklch(0.455 0.105 63);
352
+ --api-type-object: oklch(0.47 0.222 286);
353
+ --api-type-array: oklch(0.455 0.13 45);
354
+ --api-status-2xx: oklch(0.435 0.095 162);
355
+ --api-status-3xx: oklch(0.45 0.174 259);
356
+ --api-status-4xx: oklch(0.455 0.105 63);
357
+ --api-status-5xx: oklch(0.45 0.179 22);
358
+
359
+ --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
360
+ --font-heading: var(--font-sans);
361
+ --font-code: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
362
+ }`,
363
+ dark: `.dark {
364
+ --background: oklch(0.145 0.014 285.823);
365
+ --foreground: oklch(0.985 0 0);
366
+ --card: oklch(0.145 0.014 285.823);
367
+ --card-foreground: oklch(0.985 0 0);
368
+ --popover: oklch(0.145 0.014 285.823);
369
+ --popover-foreground: oklch(0.985 0 0);
370
+ --primary: oklch(0.985 0 0);
371
+ --primary-foreground: oklch(0.205 0.014 285.823);
372
+ --secondary: oklch(0.269 0.007 286.033);
373
+ --secondary-foreground: oklch(0.985 0 0);
374
+ --muted: oklch(0.269 0.007 286.033);
375
+ --muted-foreground: oklch(0.708 0.014 285.823);
376
+ --accent: oklch(0.269 0.007 286.033);
377
+ --accent-foreground: oklch(0.985 0 0);
378
+ --destructive: oklch(0.704 0.191 22.216);
379
+ --destructive-foreground: oklch(0.145 0.014 285.823);
380
+ --border: oklch(0.269 0.007 286.033);
381
+ --input: oklch(0.269 0.007 286.033);
382
+ --ring: oklch(0.439 0.01 286.375);
383
+ --note-foreground: var(--foreground);
384
+ --sidebar: oklch(0.205 0.014 285.823);
385
+ --sidebar-foreground: oklch(0.985 0 0);
386
+ --sidebar-primary: oklch(0.488 0.243 264.376);
387
+ --sidebar-primary-foreground: oklch(0.985 0 0);
388
+ --sidebar-accent: oklch(0.269 0.007 286.033);
389
+ --sidebar-accent-foreground: oklch(0.985 0 0);
390
+ --sidebar-border: oklch(0.269 0.007 286.033);
391
+ --sidebar-ring: oklch(0.439 0.01 286.375);
392
+ --code-background: oklch(0.18 0.014 253.833);
393
+ --code-foreground: oklch(0.926 0.013 253.833);
394
+
395
+ /* API semantic colors — lightened for dark mode (AA on 20%/10% tints). */
396
+ --api-get: oklch(0.720 0.135 162);
397
+ --api-post: oklch(0.755 0.174 259);
398
+ --api-put: oklch(0.745 0.15 63);
399
+ --api-patch: oklch(0.725 0.18 45);
400
+ --api-delete: oklch(0.805 0.194 22);
401
+ --api-head: oklch(0.740 0.182 286);
402
+ --api-required: oklch(0.805 0.194 22);
403
+ --api-deprecated: oklch(0.745 0.15 63);
404
+ --api-field: oklch(0.79 0.11 245); /* github-dark-default #79c0ff */
405
+ --api-type-string: oklch(0.690 0.135 162);
406
+ --api-type-number: oklch(0.725 0.174 259);
407
+ --api-type-boolean: oklch(0.720 0.15 63);
408
+ --api-type-object: oklch(0.740 0.182 286);
409
+ --api-type-array: oklch(0.725 0.18 45);
410
+ --api-status-2xx: oklch(0.720 0.135 162);
411
+ --api-status-3xx: oklch(0.755 0.174 259);
412
+ --api-status-4xx: oklch(0.770 0.15 63);
413
+ --api-status-5xx: oklch(0.805 0.194 22);
414
+ }`,
415
+ };
416
+ /**
417
+ * Mintlify-inspired theme — clean, neutral palette matching modern doc sites.
418
+ *
419
+ * Extracted from Claude Code docs (Mintlify):
420
+ * - White background, near-black text
421
+ * - Serif display font for H1, sans-serif body
422
+ * - Soft colored callout backgrounds (blue note, green tip, amber warning)
423
+ * - Rounded callout borders (16px radius)
424
+ * - Light gray code background, JetBrains Mono
425
+ */
426
+ const THEME_MINTLIFY = {
427
+ light: `:root {
428
+ --background: oklch(0.993 0.005 95);
429
+ --foreground: oklch(0.15 0.01 260);
430
+ --card: oklch(0.993 0.005 95);
431
+ --card-foreground: oklch(0.15 0.01 260);
432
+ --popover: oklch(0.993 0.005 95);
433
+ --popover-foreground: oklch(0.15 0.01 260);
434
+ --primary: oklch(0.15 0.01 260);
435
+ --primary-foreground: oklch(0.985 0 0);
436
+ --secondary: oklch(0.97 0.002 260);
437
+ --secondary-foreground: oklch(0.15 0.01 260);
438
+ --muted: oklch(0.97 0.002 260);
439
+ --muted-foreground: oklch(0.45 0.01 260);
440
+ --accent: oklch(0.97 0.002 260);
441
+ --accent-foreground: oklch(0.15 0.01 260);
442
+ --destructive: oklch(0.55 0.22 25);
443
+ --destructive-foreground: oklch(0.985 0 0);
444
+ --border: oklch(0.91 0.004 260);
445
+ --input: oklch(0.91 0.004 260);
446
+ --ring: oklch(0.7 0.01 260);
447
+ --radius: 1rem;
448
+ /* Callout colors — soft pastel backgrounds matching Mintlify */
449
+ --note: oklch(0.62 0.18 260);
450
+ --note-foreground: var(--foreground);
451
+ --abstract: oklch(0.65 0.14 220);
452
+ --info: oklch(0.65 0.14 230);
453
+ --tip: oklch(0.65 0.16 155);
454
+ --success: oklch(0.65 0.16 155);
455
+ --question: oklch(0.7 0.15 270);
456
+ --warning: oklch(0.7 0.15 75);
457
+ --failure: oklch(0.6 0.2 25);
458
+ --danger: oklch(0.55 0.22 25);
459
+ --bug: oklch(0.55 0.22 350);
460
+ --example: oklch(0.55 0.2 285);
461
+ --quote: oklch(0.55 0 0);
462
+ /* Sidebar */
463
+ --sidebar: oklch(0.993 0.005 95);
464
+ --sidebar-foreground: oklch(0.15 0.01 260);
465
+ --sidebar-primary: oklch(0.15 0.01 260);
466
+ --sidebar-primary-foreground: oklch(0.985 0 0);
467
+ --sidebar-accent: oklch(0.97 0.002 260);
468
+ --sidebar-accent-foreground: oklch(0.15 0.01 260);
469
+ --sidebar-border: oklch(0.93 0.004 260);
470
+ --sidebar-ring: oklch(0.7 0.01 260);
471
+ /* Code — light gray background, matching Mintlify's inline code */
472
+ --code-background: oklch(0.96 0.003 260);
473
+ --code-foreground: oklch(0.25 0.01 260);
474
+
475
+ /* API reference — always-dark code panel */
476
+ --api-panel-bg: oklch(0.145 0.014 253.833);
477
+ --api-panel-fg: oklch(0.926 0.013 253.833);
478
+
479
+ /* API semantic colors — HTTP methods, types, status codes */
480
+ /* Light mode: darkened for WCAG AA on the 20%/10% tints. See .dark. */
481
+ --api-get: oklch(0.435 0.095 162);
482
+ --api-post: oklch(0.45 0.174 259);
483
+ --api-put: oklch(0.455 0.105 63);
484
+ --api-patch: oklch(0.455 0.13 45);
485
+ --api-delete: oklch(0.45 0.179 22);
486
+ --api-head: oklch(0.47 0.222 286);
487
+ --api-required: oklch(0.45 0.179 22);
488
+ --api-deprecated: oklch(0.455 0.105 63);
489
+ /* A field name in an operation's description (\`id\`, \`name\`): the JSON
490
+ key colour of the highlighted examples (github-light-default #0550ae). */
491
+ --api-field: oklch(0.47 0.16 254);
492
+ --api-type-string: oklch(0.435 0.095 162);
493
+ --api-type-number: oklch(0.45 0.174 259);
494
+ --api-type-boolean: oklch(0.455 0.105 63);
495
+ --api-type-object: oklch(0.47 0.222 286);
496
+ --api-type-array: oklch(0.455 0.13 45);
497
+ --api-status-2xx: oklch(0.435 0.095 162);
498
+ --api-status-3xx: oklch(0.45 0.174 259);
499
+ --api-status-4xx: oklch(0.455 0.105 63);
500
+ --api-status-5xx: oklch(0.45 0.179 22);
501
+
502
+ /* Fonts — system sans for body, JetBrains Mono for code */
503
+ --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
504
+ --font-heading: var(--font-sans);
505
+ --font-code: "JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
506
+ }`,
507
+ dark: `.dark {
508
+ --background: oklch(0.16 0.01 260);
509
+ --foreground: oklch(0.93 0.005 260);
510
+ --card: oklch(0.18 0.01 260);
511
+ --card-foreground: oklch(0.93 0.005 260);
512
+ --popover: oklch(0.18 0.01 260);
513
+ --popover-foreground: oklch(0.93 0.005 260);
514
+ --primary: oklch(0.93 0.005 260);
515
+ --primary-foreground: oklch(0.16 0.01 260);
516
+ --secondary: oklch(0.22 0.008 260);
517
+ --secondary-foreground: oklch(0.93 0.005 260);
518
+ --muted: oklch(0.22 0.008 260);
519
+ --muted-foreground: oklch(0.65 0.01 260);
520
+ --accent: oklch(0.22 0.008 260);
521
+ --accent-foreground: oklch(0.93 0.005 260);
522
+ --destructive: oklch(0.65 0.2 25);
523
+ --destructive-foreground: oklch(0.16 0.01 260);
524
+ --border: oklch(0.28 0.008 260);
525
+ --input: oklch(0.28 0.008 260);
526
+ --ring: oklch(0.45 0.01 260);
527
+ --note-foreground: var(--foreground);
528
+ --sidebar: oklch(0.16 0.01 260);
529
+ --sidebar-foreground: oklch(0.93 0.005 260);
530
+ --sidebar-primary: oklch(0.55 0.2 260);
531
+ --sidebar-primary-foreground: oklch(0.93 0.005 260);
532
+ --sidebar-accent: oklch(0.22 0.008 260);
533
+ --sidebar-accent-foreground: oklch(0.93 0.005 260);
534
+ --sidebar-border: oklch(0.28 0.008 260);
535
+ --sidebar-ring: oklch(0.45 0.01 260);
536
+ --code-background: oklch(0.2 0.01 260);
537
+ --code-foreground: oklch(0.9 0.01 260);
538
+
539
+ /* API semantic colors — lightened for dark mode (AA on 20%/10% tints). */
540
+ --api-get: oklch(0.720 0.135 162);
541
+ --api-post: oklch(0.755 0.174 259);
542
+ --api-put: oklch(0.745 0.15 63);
543
+ --api-patch: oklch(0.725 0.18 45);
544
+ --api-delete: oklch(0.805 0.194 22);
545
+ --api-head: oklch(0.740 0.182 286);
546
+ --api-required: oklch(0.805 0.194 22);
547
+ --api-deprecated: oklch(0.745 0.15 63);
548
+ --api-field: oklch(0.79 0.11 245); /* github-dark-default #79c0ff */
549
+ --api-type-string: oklch(0.690 0.135 162);
550
+ --api-type-number: oklch(0.725 0.174 259);
551
+ --api-type-boolean: oklch(0.720 0.15 63);
552
+ --api-type-object: oklch(0.740 0.182 286);
553
+ --api-type-array: oklch(0.725 0.18 45);
554
+ --api-status-2xx: oklch(0.720 0.135 162);
555
+ --api-status-3xx: oklch(0.755 0.174 259);
556
+ --api-status-4xx: oklch(0.770 0.15 63);
557
+ --api-status-5xx: oklch(0.805 0.194 22);
558
+ }`,
559
+ };
560
+ /** The theme presets by name. A name not here falls back to "default", as writeThemeFile always did. */
561
+ export const THEMES = { default: THEME_DEFAULT, mintlify: THEME_MINTLIFY };
562
+ /** A site's src/styles/theme.css for a preset: the Tailwind theme mapping plus the preset's tokens. */
563
+ export function themeCss(themeName) {
564
+ const tokens = themeName === "mintlify" ? THEME_MINTLIFY : THEME_DEFAULT;
565
+ return `@custom-variant dark (&:where(.dark, .dark *));
566
+
567
+ @theme inline {
568
+ --color-background: var(--background);
569
+ --color-foreground: var(--foreground);
570
+ --color-card: var(--card);
571
+ --color-card-foreground: var(--card-foreground);
572
+ --color-popover: var(--popover);
573
+ --color-popover-foreground: var(--popover-foreground);
574
+ --color-primary: var(--primary);
575
+ --color-primary-foreground: var(--primary-foreground);
576
+ --color-secondary: var(--secondary);
577
+ --color-secondary-foreground: var(--secondary-foreground);
578
+ --color-muted: var(--muted);
579
+ --color-muted-foreground: var(--muted-foreground);
580
+ --color-accent: var(--accent);
581
+ --color-accent-foreground: var(--accent-foreground);
582
+ --color-destructive: var(--destructive);
583
+ --color-destructive-foreground: var(--destructive-foreground);
584
+ --color-border: var(--border);
585
+ --color-input: var(--input);
586
+ --color-ring: var(--ring);
587
+ --radius-sm: calc(var(--radius) - 4px);
588
+ --radius-md: calc(var(--radius) - 2px);
589
+ --radius-lg: var(--radius);
590
+ --radius-xl: calc(var(--radius) + 4px);
591
+
592
+ /* Sidebar */
593
+ --color-sidebar: var(--sidebar);
594
+ --color-sidebar-foreground: var(--sidebar-foreground);
595
+ --color-sidebar-primary: var(--sidebar-primary);
596
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
597
+ --color-sidebar-accent: var(--sidebar-accent);
598
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
599
+ --color-sidebar-border: var(--sidebar-border);
600
+ --color-sidebar-ring: var(--sidebar-ring);
601
+
602
+ /* Admonition colors */
603
+ --color-note: var(--note);
604
+ --color-note-foreground: var(--note-foreground);
605
+ --color-abstract: var(--abstract);
606
+ --color-info: var(--info);
607
+ --color-tip: var(--tip);
608
+ --color-success: var(--success);
609
+ --color-question: var(--question);
610
+ --color-warning: var(--warning);
611
+ --color-failure: var(--failure);
612
+ --color-danger: var(--danger);
613
+ --color-bug: var(--bug);
614
+ --color-example: var(--example);
615
+ --color-quote: var(--quote);
616
+
617
+ /* Code */
618
+ --color-code-bg: var(--code-background);
619
+ --color-code-fg: var(--code-foreground);
620
+
621
+ /* API reference — HTTP method, type, status, role colors */
622
+ --color-api-field: var(--api-field);
623
+ --color-api-get: var(--api-get);
624
+ --color-api-post: var(--api-post);
625
+ --color-api-put: var(--api-put);
626
+ --color-api-patch: var(--api-patch);
627
+ --color-api-delete: var(--api-delete);
628
+ --color-api-head: var(--api-head);
629
+ --color-api-required: var(--api-required);
630
+ --color-api-deprecated: var(--api-deprecated);
631
+ --color-api-type-string: var(--api-type-string);
632
+ --color-api-type-number: var(--api-type-number);
633
+ --color-api-type-boolean: var(--api-type-boolean);
634
+ --color-api-type-object: var(--api-type-object);
635
+ --color-api-type-array: var(--api-type-array);
636
+ --color-api-status-2xx: var(--api-status-2xx);
637
+ --color-api-status-3xx: var(--api-status-3xx);
638
+ --color-api-status-4xx: var(--api-status-4xx);
639
+ --color-api-status-5xx: var(--api-status-5xx);
640
+
641
+ /* Fonts */
642
+ --font-sans: var(--font-sans);
643
+ --font-mono: var(--font-code);
644
+ --font-family-code: var(--font-code);
645
+ }
646
+
647
+ ${tokens.light}
648
+
649
+ ${tokens.dark}
650
+
651
+ @layer base {
652
+ * { @apply border-border outline-ring/50; }
653
+ body { @apply bg-background text-foreground font-sans; }
654
+ }
655
+
656
+ /* Shiki dual-theme: light by default, dark when .dark class is active */
657
+ .shiki,
658
+ .shiki span,
659
+ .astro-code,
660
+ .astro-code span {
661
+ color: var(--shiki-light) !important;
662
+ }
663
+ .shiki,
664
+ .astro-code {
665
+ background-color: var(--shiki-light-bg) !important;
666
+ }
667
+ .dark .shiki,
668
+ .dark .shiki span,
669
+ .dark .astro-code,
670
+ .dark .astro-code span {
671
+ color: var(--shiki-dark) !important;
672
+ }
673
+ .dark .shiki,
674
+ .dark .astro-code {
675
+ background-color: var(--shiki-dark-bg) !important;
676
+ }
677
+ /* Fix comment color contrast for WCAG AA */
678
+ .shiki span[style*="--shiki-light:#6E7781"],
679
+ .astro-code span[style*="--shiki-light:#6E7781"] {
680
+ --shiki-light: #576069 !important;
681
+ }
682
+ `;
683
+ }