duckfn-docs-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/AGENTS.md +689 -0
  2. package/LICENSE +21 -0
  3. package/README.md +107 -0
  4. package/dist/dom.d.ts +69 -0
  5. package/dist/home/DfkFeatures.d.ts +20 -0
  6. package/dist/home/DfkHero.d.ts +25 -0
  7. package/dist/home/DfkNextSteps.d.ts +16 -0
  8. package/dist/home/styles.d.ts +8 -0
  9. package/dist/index.d.ts +50 -0
  10. package/dist/index.js +2 -0
  11. package/dist/register-DKLiYs-F.js +2324 -0
  12. package/dist/register.d.ts +10 -0
  13. package/dist/remark.d.ts +21 -0
  14. package/dist/remark.js +15 -0
  15. package/dist/runtimeConfig-Bokbb8VH.js +106 -0
  16. package/dist/sql/DfkSql.d.ts +7 -0
  17. package/dist/sql/PreviewTabs.d.ts +37 -0
  18. package/dist/sql/client.d.ts +1 -0
  19. package/dist/sql/client.js +4 -0
  20. package/dist/sql/editor.d.ts +16 -0
  21. package/dist/sql/extensions.d.ts +108 -0
  22. package/dist/sql/extensions.js +198 -0
  23. package/dist/sql/remark.d.ts +88 -0
  24. package/dist/sql/remark.js +69 -0
  25. package/dist/sql/renderers.d.ts +44 -0
  26. package/dist/sql/runtime.d.ts +105 -0
  27. package/dist/sql/runtimeConfig.d.ts +80 -0
  28. package/dist/sql/styles.d.ts +6 -0
  29. package/dist/toc-toggle/TocToggle.d.ts +46 -0
  30. package/dist/toc-toggle/TocToggle.js +69 -0
  31. package/dist/toc-toggle/client.d.ts +1 -0
  32. package/dist/toc-toggle/client.js +9 -0
  33. package/dist/toc-toggle/plugin.d.ts +36 -0
  34. package/dist/toc-toggle/plugin.js +13 -0
  35. package/dist/types.d.ts +42 -0
  36. package/package.json +73 -0
  37. package/src/dom.ts +109 -0
  38. package/src/home/DfkFeatures.ts +78 -0
  39. package/src/home/DfkHero.ts +128 -0
  40. package/src/home/DfkNextSteps.ts +73 -0
  41. package/src/home/home.css +520 -0
  42. package/src/home/styles.ts +28 -0
  43. package/src/index.ts +59 -0
  44. package/src/kit.css +19 -0
  45. package/src/register.ts +39 -0
  46. package/src/remark.ts +60 -0
  47. package/src/sql/DfkSql.css +226 -0
  48. package/src/sql/DfkSql.ts +620 -0
  49. package/src/sql/PreviewTabs.ts +169 -0
  50. package/src/sql/client.ts +16 -0
  51. package/src/sql/editor.ts +75 -0
  52. package/src/sql/extensions.ts +470 -0
  53. package/src/sql/remark.ts +213 -0
  54. package/src/sql/renderers.ts +916 -0
  55. package/src/sql/runtime.ts +348 -0
  56. package/src/sql/runtimeConfig.ts +249 -0
  57. package/src/sql/sql.css +397 -0
  58. package/src/sql/styles.ts +24 -0
  59. package/src/theme/tokens.css +75 -0
  60. package/src/toc-toggle/TocToggle.css +69 -0
  61. package/src/toc-toggle/TocToggle.ts +172 -0
  62. package/src/toc-toggle/client.ts +20 -0
  63. package/src/toc-toggle/plugin.ts +54 -0
  64. package/src/types.ts +47 -0
  65. package/src/vite-env.d.ts +8 -0
@@ -0,0 +1,397 @@
1
+ /* Light-DOM styles for `<dfk-sql>`: the result container. It lives in the
2
+ element's light DOM (not the shadow root) because VTable injects a
3
+ *document-level* stylesheet that a shadow boundary could not host — the same
4
+ light-DOM exception as the TOC toggle (see AGENTS.md rule 9). It is created
5
+ only when a query runs, so the light DOM still starts empty (rule 11). Every
6
+ class carries the `dfk-sql-` prefix so nothing can collide with the host site.
7
+ *
8
+ * The code area — the editor and the buttons floating over it — is in the shadow
9
+ * root instead, and is styled by `DfkSql.css`.
10
+ *
11
+ * Imported through `kit.css`; the consuming site gets it with the one-line
12
+ * `@import 'duckfn-docs-kit/src/kit.css'` it already has. */
13
+
14
+ /* The remark plugin prerenders two children: the loading ghost, and the original
15
+ code node (the class dates from when it was slotted as a read-only preview).
16
+ The code view is the CodeMirror editor now, so that copy must never show —
17
+ hiding it here rather than deleting it in JS also removes the flash between
18
+ parsing the prerendered HTML and the element upgrading.
19
+ The ghost is the one child that *does* show, and only while the element is
20
+ un-upgraded: once it upgrades it is unslotted like the rest and renders
21
+ nowhere, so no JS ever has to remove it. */
22
+ dfk-sql > :not([slot]):not(.dfk-sql-editor-skeleton) {
23
+ display: none;
24
+ }
25
+
26
+ /* --- Loading placeholders ------------------------------------------------- */
27
+
28
+ /* Two heavy libraries arrive through lazy `import()`s — CodeMirror for the
29
+ editor, VTable for a table result — and on a slow link both windows are long
30
+ enough to see. Each therefore gets a placeholder: one bar per line of SQL where
31
+ the editor goes, two rows where the table goes. Both are as tall as what
32
+ replaces them, so neither swap moves the page.
33
+
34
+ The editor ghost is drawn twice: here, for the window *before* the kit's JS has
35
+ defined `<dfk-sql>` at all (the bars are prerendered by `sql/remark.ts`), and
36
+ by `#setEditorPending` inside the shadow tree once it has. This file carries the
37
+ first copy and `DfkSql.css` the second — a shadow tree sees neither the
38
+ document's rules nor its `@keyframes` — so keep the bar and the timing of the
39
+ two in sync.
40
+
41
+ The line count is the SQL's *source* line count, which is a deliberate
42
+ approximation: a line long enough to wrap makes the editor taller than its
43
+ ghost, and knowing the wrapped height would mean laying the text out first —
44
+ measuring the site's fonts and column width — for a placeholder. */
45
+ @keyframes dfk-sql-shimmer {
46
+ from {
47
+ background-position: 0% 0;
48
+ }
49
+ to {
50
+ background-position: 100% 0;
51
+ }
52
+ }
53
+
54
+ /* Before the element upgrades it has no shadow tree, and its prerendered text is
55
+ hidden by the rule above, so without this the block would be an invisible hole
56
+ that pops in and pushes the rest of the page down. `:not(:defined)` paints the
57
+ box the editor will live in; the bars inside come from the plugin. It also
58
+ keeps standing in when no JS ever runs, which is the honest thing to show for a
59
+ block that needs it. */
60
+ dfk-sql:not(:defined) {
61
+ display: block;
62
+ margin: 1rem 0;
63
+ border: 1px solid var(--ifm-global-border-color, #e0e0e0);
64
+ border-radius: var(--ifm-global-radius, 0.4rem);
65
+ background: var(--ifm-background-surface-color, #fff);
66
+ /* The editor's own code font size, so a bar is as tall as a line of code. */
67
+ font-size: var(--ifm-code-font-size, 95%);
68
+ /* Floor of one line for a hand-written block, which carries no prerendered
69
+ ghost to be sized by. `border-box` (the sites' reset) counts the border. */
70
+ min-height: calc(1.6em + 10px);
71
+ }
72
+
73
+ /* The bars themselves: `sql/remark.ts` emits this markup for the pre-upgrade
74
+ window, `#setEditorPending` builds the same nodes inside the shadow tree, and
75
+ `DfkSql.css` carries that second copy of these two rules. */
76
+ .dfk-sql-editor-skeleton {
77
+ /* CodeMirror's own content inset — `.cm-content` carries `padding: 4px 0` and
78
+ this box has none — on the container, so it is paid once, not per line. */
79
+ padding: 4px 0.75rem;
80
+ }
81
+
82
+ .dfk-sql-editor-skeleton-line {
83
+ display: flex;
84
+ align-items: center;
85
+ /* One code line box, exactly: `.cm-scroller`'s `line-height: 1.6`. The half
86
+ leading that centres a line's text is the flex centring here — margins would
87
+ collapse between neighbouring lines and stack the bars closer than the code
88
+ they stand in for. */
89
+ height: 1.6em;
90
+ }
91
+
92
+ .dfk-sql-editor-skeleton-line::before {
93
+ content: '';
94
+ flex: 1;
95
+ height: 0.75em;
96
+ border-radius: 0.25em;
97
+ background-color: var(--ifm-color-emphasis-200, #e6e6e6);
98
+ /* Base colour plus a lighter band travelling through it; `background-size:
99
+ 200%` is what gives `background-position` room to move (at 100% it cannot). */
100
+ background-image: linear-gradient(
101
+ 90deg,
102
+ transparent 0%,
103
+ var(--ifm-color-emphasis-100, #f5f5f5) 50%,
104
+ transparent 100%
105
+ );
106
+ background-size: 200% 100%;
107
+ animation: dfk-sql-shimmer 1.6s linear infinite;
108
+ }
109
+
110
+ /* `.dfk-sql-table` holds these while VTable's bundle is in flight; `mountTable`
111
+ drops them as soon as the canvas is in place — or hands the box to the error
112
+ view if the import failed. The box already has its final height (the renderer
113
+ derives `--dfk-sql-table-height` from the row count), so the rows only have to
114
+ read as "a table is coming"; the row rhythm comes from the renderer's
115
+ `ROW_HEIGHT`. Every row is built the same way, so the columns line up. */
116
+ .dfk-sql-table-skeleton {
117
+ display: flex;
118
+ flex-direction: column;
119
+ /* Clipped to the box: a one-row result is shorter than two skeleton rows. */
120
+ height: 100%;
121
+ overflow: hidden;
122
+ }
123
+
124
+ .dfk-sql-table-skeleton-row {
125
+ display: flex;
126
+ align-items: center;
127
+ gap: 1.5rem;
128
+ height: var(--dfk-sql-skeleton-row-height, 30px);
129
+ padding: 0 0.75rem;
130
+ border-bottom: 1px solid var(--ifm-color-emphasis-100, #f5f5f5);
131
+ }
132
+
133
+ .dfk-sql-table-skeleton-cell {
134
+ flex: 1;
135
+ height: 0.7em;
136
+ border-radius: 0.25em;
137
+ background-color: var(--ifm-color-emphasis-200, #e6e6e6);
138
+ background-image: linear-gradient(
139
+ 90deg,
140
+ transparent 0%,
141
+ var(--ifm-color-emphasis-100, #f5f5f5) 50%,
142
+ transparent 100%
143
+ );
144
+ background-size: 200% 100%;
145
+ animation: dfk-sql-shimmer 1.6s linear infinite;
146
+ }
147
+
148
+ /* A bar that sweeps forever is decoration; readers who asked for less motion get
149
+ the static grey. Mirrored in `DfkSql.css`. */
150
+ @media (prefers-reduced-motion: reduce) {
151
+ .dfk-sql-editor-skeleton-line,
152
+ .dfk-sql-table-skeleton-cell {
153
+ animation: none;
154
+ }
155
+ }
156
+
157
+ .dfk-sql-result {
158
+ border: 1px solid var(--ifm-global-border-color, #e0e0e0);
159
+ border-top: none;
160
+ border-radius: 0 0 var(--ifm-global-radius, 0.4rem)
161
+ var(--ifm-global-radius, 0.4rem);
162
+ overflow: hidden;
163
+ background: var(--ifm-background-surface-color, #fff);
164
+ }
165
+
166
+ .dfk-sql-result:empty {
167
+ display: none;
168
+ }
169
+
170
+ .dfk-sql-text {
171
+ margin: 0;
172
+ padding: 0.5rem 0.75rem;
173
+ font-family: var(--ifm-font-family-monospace, ui-monospace, SFMono-Regular,
174
+ Menlo, monospace);
175
+ font-size: var(--ifm-code-font-size, 95%);
176
+ white-space: pre-wrap;
177
+ word-break: break-word;
178
+ }
179
+
180
+ .dfk-sql-error {
181
+ margin: 0;
182
+ padding: 0.5rem 0.75rem;
183
+ font-family: var(--ifm-font-family-monospace, ui-monospace, SFMono-Regular,
184
+ Menlo, monospace);
185
+ font-size: var(--ifm-code-font-size, 95%);
186
+ white-space: pre-wrap;
187
+ word-break: break-word;
188
+ color: var(--ifm-color-danger-contrast, #fff);
189
+ background: var(--ifm-color-danger, #d32f2f);
190
+ }
191
+
192
+ /* --- Result tabs --------------------------------------------------------- */
193
+
194
+ /* The strip is a row holding the scrolling tab list and the corner button
195
+ (the fullscreen toggle, owned by `<dfk-sql>`). */
196
+ .dfk-sql-tabs {
197
+ display: flex;
198
+ align-items: center;
199
+ padding-right: 0.3rem;
200
+ border-bottom: 1px solid var(--ifm-global-border-color, #e0e0e0);
201
+ background: var(--ifm-color-emphasis-100, #f5f5f5);
202
+ }
203
+
204
+ .dfk-sql-tab-list {
205
+ display: flex;
206
+ flex: 1;
207
+ gap: 0.25rem;
208
+ min-width: 0;
209
+ padding: 0.3rem 0.5rem 0;
210
+ overflow-x: auto;
211
+ }
212
+
213
+ .dfk-sql-tab {
214
+ padding: 0.15rem 0.65rem;
215
+ border: 1px solid transparent;
216
+ border-bottom: none;
217
+ border-radius: var(--ifm-global-radius, 0.4rem)
218
+ var(--ifm-global-radius, 0.4rem) 0 0;
219
+ background: transparent;
220
+ color: var(--ifm-color-emphasis-700, #666);
221
+ font-family: inherit;
222
+ font-size: 0.8rem;
223
+ line-height: 1.5;
224
+ white-space: nowrap;
225
+ cursor: pointer;
226
+ }
227
+
228
+ .dfk-sql-tab:hover {
229
+ color: var(--ifm-color-primary, #14459b);
230
+ }
231
+
232
+ .dfk-sql-tab-active {
233
+ border-color: var(--ifm-global-border-color, #e0e0e0);
234
+ background: var(--ifm-background-surface-color, #fff);
235
+ color: var(--ifm-font-color-base, #181818);
236
+ }
237
+
238
+ /* --- Icon buttons and their tooltips ------------------------------------- */
239
+
240
+ /* The same rules (and the `[data-tip]` tooltip below) are declared in
241
+ `DfkSql.css`, because the buttons are split across the shadow boundary: the
242
+ code-block actions live in the shadow tree, the fullscreen toggle lives in
243
+ this light-DOM tab strip. Keep the two copies in sync. */
244
+ .dfk-sql-icon-button {
245
+ position: relative;
246
+ display: inline-flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ width: 1.6rem;
250
+ height: 1.6rem;
251
+ padding: 0;
252
+ border: none;
253
+ border-radius: var(--ifm-global-radius, 0.25rem);
254
+ background: transparent;
255
+ color: var(--ifm-color-emphasis-700, #666);
256
+ cursor: pointer;
257
+ transition: background 0.12s ease, color 0.12s ease;
258
+ }
259
+
260
+ .dfk-sql-icon-button:hover:not(:disabled),
261
+ .dfk-sql-icon-button:focus-visible {
262
+ background: var(--ifm-color-emphasis-200, #e6e6e6);
263
+ color: var(--ifm-color-primary, #14459b);
264
+ }
265
+
266
+ .dfk-sql-icon-button:disabled {
267
+ opacity: 0.45;
268
+ cursor: progress;
269
+ }
270
+
271
+ .dfk-sql-icon-button[hidden] {
272
+ display: none;
273
+ }
274
+
275
+ /* A sticky state, e.g. the wrap toggle while wrapping is on. */
276
+ .dfk-sql-icon-on {
277
+ color: var(--ifm-color-primary, #14459b);
278
+ }
279
+
280
+ .dfk-sql-icon {
281
+ font-size: 1rem;
282
+ }
283
+
284
+ [data-tip]::after {
285
+ content: attr(data-tip);
286
+ position: absolute;
287
+ top: calc(100% + 0.3rem);
288
+ right: 0;
289
+ z-index: 2;
290
+ padding: 0.15rem 0.4rem;
291
+ border-radius: var(--ifm-global-radius, 0.25rem);
292
+ background: var(--ifm-color-emphasis-800, #333);
293
+ color: var(--ifm-color-emphasis-0, #fff);
294
+ font-size: 0.7rem;
295
+ line-height: 1.4;
296
+ white-space: nowrap;
297
+ opacity: 0;
298
+ pointer-events: none;
299
+ transition: opacity 0.12s ease;
300
+ }
301
+
302
+ [data-tip]:hover::after,
303
+ [data-tip]:focus-visible::after {
304
+ opacity: 1;
305
+ }
306
+
307
+ /* --- Panels -------------------------------------------------------------- */
308
+
309
+ .dfk-sql-panels {
310
+ display: flex;
311
+ flex-direction: column;
312
+ }
313
+
314
+ /* `overscroll-behavior: contain` is what stops a wheel that has reached the
315
+ bottom of the table from continuing into the page scroll (the page lurches
316
+ upward and the example leaves the screen). It is not inherited, so it is
317
+ declared on every scrolling box, including VTable's inner scroll containers
318
+ under `.dfk-sql-table`. */
319
+ .dfk-sql-panel,
320
+ .dfk-sql-table,
321
+ .dfk-sql-table *,
322
+ .dfk-sql-svg {
323
+ overscroll-behavior: contain;
324
+ }
325
+
326
+ .dfk-sql-panel {
327
+ min-height: 0;
328
+ overflow: auto;
329
+ }
330
+
331
+ /* --- Rendered results ---------------------------------------------------- */
332
+
333
+ .dfk-sql-table {
334
+ width: 100%;
335
+ /* VTable measures its container, so the box needs a real height; a taller
336
+ result grows with the rows up to a ceiling. */
337
+ height: var(--dfk-sql-table-height, 240px);
338
+ }
339
+
340
+ .dfk-sql-frame {
341
+ display: block;
342
+ width: var(--dfk-sql-preview-width, 100%);
343
+ height: var(--dfk-sql-preview-height, 360px);
344
+ border: none;
345
+ background: #fff;
346
+ }
347
+
348
+ .dfk-sql-svg {
349
+ width: var(--dfk-sql-preview-width, 100%);
350
+ height: var(--dfk-sql-preview-height, auto);
351
+ padding: 0.5rem;
352
+ overflow: auto;
353
+ }
354
+
355
+ .dfk-sql-svg > svg {
356
+ max-width: 100%;
357
+ height: auto;
358
+ }
359
+
360
+ /* --- Fullscreen ---------------------------------------------------------- */
361
+
362
+ /* Also carries `.dfk-sql-result`, so `overflow` and the radius are re-declared
363
+ here rather than fought with `!important`.
364
+ The background must be the *surface* colour, not `--ifm-background-color`:
365
+ a site may legitimately define the latter as `transparent` (this one does),
366
+ which would leave the overlay see-through with the page showing underneath. */
367
+ .dfk-sql-result-expanded {
368
+ position: fixed;
369
+ inset: 0;
370
+ z-index: 2147483000;
371
+ display: flex;
372
+ flex-direction: column;
373
+ overflow: hidden;
374
+ border: none;
375
+ border-radius: 0;
376
+ background: var(--ifm-background-surface-color, #fff);
377
+ }
378
+
379
+ .dfk-sql-result-expanded .dfk-sql-panels,
380
+ .dfk-sql-result-expanded .dfk-sql-panel {
381
+ flex: 1;
382
+ }
383
+
384
+ .dfk-sql-result-expanded .dfk-sql-table {
385
+ /* The extra height is the point of the fullscreen view. */
386
+ height: 100%;
387
+ }
388
+
389
+ .dfk-sql-result-expanded .dfk-sql-frame {
390
+ height: 100%;
391
+ }
392
+
393
+ /* An inline `<svg>` gets the same treatment as the frame and the table so the
394
+ panel is one full-height scroll area rather than a strip. */
395
+ .dfk-sql-result-expanded .dfk-sql-svg {
396
+ height: 100%;
397
+ }
@@ -0,0 +1,24 @@
1
+ // The `<dfk-sql>` code box's shadow-root CSS as an inline string (Vite `?inline`
2
+ // import), mirroring `home/styles.ts`. It covers the CodeMirror editor and the
3
+ // action cluster, both of which live in this tree. Only the *result* container
4
+ // stays in the light DOM, styled by `sql.css` through `kit.css` instead: VTable
5
+ // injects a document-level stylesheet, which a shadow boundary could not host.
6
+ //
7
+ // The type declaration lives in `vite-env.d.ts`; `tsc` never resolves the
8
+ // `?inline` suffix, so the module graph stays buildable without Vite running.
9
+ import sqlCss from './DfkSql.css?inline';
10
+
11
+ let sheet: CSSStyleSheet | null = null;
12
+
13
+ /**
14
+ * The one parsed stylesheet shared by every `<dfk-sql>` shadow root.
15
+ * Created lazily: `CSSStyleSheet` does not exist during Docusaurus' Node
16
+ * prerender, and only the browser ever calls this.
17
+ */
18
+ export function sqlStyles(): CSSStyleSheet {
19
+ if (!sheet) {
20
+ sheet = new CSSStyleSheet();
21
+ sheet.replaceSync(sqlCss);
22
+ }
23
+ return sheet;
24
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Brand colour tokens for duckfn-family docs sites.
3
+ *
4
+ * The palette comes from the project logo (drawn with a deep blue body,
5
+ * #14459b, and a golden beak, #fcb103). The `--duckfn-*` variables are what the
6
+ * shared components (home.css) pick their tints, glows and borders from, so the
7
+ * brand colour stays defined in exactly one place. They are written out
8
+ * explicitly instead of derived with `color-mix()` to avoid relying on a newer
9
+ * CSS feature for something this central.
10
+ *
11
+ * Contrast, checked against the palettes below:
12
+ * #14459b on white 8.9:1 -> light-mode primary for text and links
13
+ * white on #14459b 8.9:1 -> buttons and badges
14
+ * #4d8fe8 on #1b1b1d 5.2:1 -> dark-mode primary for text and links
15
+ * #fcb103 on #14459b 4.8:1 -> yellow is only legible on brand blue
16
+ * #fcb103 on white 1.8:1 -> NOT a text colour; see the note below.
17
+ *
18
+ * The `--ifm-*` Infima wiring (the seven-step primary ramp) deliberately does
19
+ * NOT live here: each site owns that mapping for its own brand.
20
+ */
21
+ :root {
22
+ --duckfn-blue: #14459b;
23
+ /* Ends of the gradient the homepage headline is painted with. */
24
+ --duckfn-blue-deep: #0e306c;
25
+ --duckfn-blue-bright: #1a5aca;
26
+ /* Tinted surfaces: card icon chips, the code showcase band, hover shadows. */
27
+ --duckfn-blue-soft: #e8eefb;
28
+ --duckfn-tint: #f6f8fc;
29
+ /* Kept faint on purpose: the home page stretches these over a very large
30
+ radius, and a stronger tint shows the gradient's steps as visible bands. */
31
+ --duckfn-glow: rgba(20, 69, 155, 0.12);
32
+ --duckfn-glow-warm: rgba(252, 177, 3, 0.16);
33
+ --duckfn-card-bg: #ffffff;
34
+ --duckfn-card-border: #e3e8f2;
35
+ --duckfn-shadow: 0 8px 24px rgba(20, 69, 155, 0.12);
36
+ --duckfn-yellow: #fcb103;
37
+ /* Home page buttons, kept apart from the primary because the dark mode value
38
+ of `--ifm-color-primary` is too light to carry white text. Measured against
39
+ their own label: white on #14459b is 8.9:1, white on the hover #0e306c is
40
+ 12.7:1. The outline border is decorative — the label above it carries the
41
+ contrast — so it is allowed to stay faint. */
42
+ --duckfn-btn-bg: #14459b;
43
+ --duckfn-btn-bg-hover: #0e306c;
44
+ --duckfn-btn-fg: #ffffff;
45
+ --duckfn-outline-border: #bccbe8;
46
+ }
47
+
48
+ /* For readability concerns, you should choose a lighter palette in dark mode.
49
+ The primary has to move well away from #14459b here: the deep blue only
50
+ reaches about 2.4:1 against #1b1b1d, which is unreadable for text. */
51
+ [data-theme='dark'] {
52
+ --duckfn-blue: #4d8fe8;
53
+ --duckfn-blue-deep: #4d8fe8;
54
+ --duckfn-blue-bright: #9ecbff;
55
+ --duckfn-blue-soft: #17253d;
56
+ --duckfn-tint: #1f2733;
57
+ --duckfn-glow: rgba(77, 143, 232, 0.16);
58
+ --duckfn-glow-warm: rgba(252, 177, 3, 0.12);
59
+ --duckfn-card-bg: #242526;
60
+ --duckfn-card-border: #33405c;
61
+ --duckfn-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
62
+ --duckfn-yellow: #fcb103;
63
+ /* The dark primary is a light blue, so white on it would only reach 3.3:1.
64
+ The button therefore gets its own deeper blue: white on #2f6fd0 is 4.9:1,
65
+ and the one-notch-lighter hover stays at 4.4:1. */
66
+ --duckfn-btn-bg: #2f6fd0;
67
+ --duckfn-btn-bg-hover: #3576dd;
68
+ --duckfn-btn-fg: #ffffff;
69
+ --duckfn-outline-border: #33405c;
70
+ }
71
+
72
+ /* The yellow is an accent, never a text colour: #fcb103 on white is 1.8:1.
73
+ Safe uses (all already at or above 4.5:1): as a chip behind the deep blue,
74
+ as the icon chip background in dark mode, as a decorative underline or
75
+ border, and for small shapes that are not the only carrier of meaning. */
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Table-of-contents toggle styles.
3
+ *
4
+ * Docusaurus has no `hideable` option for the right-hand TOC, so the control
5
+ * and the layout switch are provided by the kit's `toc-toggle` module: the
6
+ * button is injected above the TOC, and the `toc-collapsed` class on `<body>`
7
+ * drives the rules below.
8
+ */
9
+
10
+ .toc-toggle {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+ width: 2rem;
15
+ height: 2rem;
16
+ /* Right edge of the TOC column, next to the list it controls. */
17
+ margin-left: auto;
18
+ margin-bottom: 0.5rem;
19
+ border-radius: 50%;
20
+ background-color: var(--ifm-color-emphasis-200);
21
+ color: var(--ifm-color-emphasis-800);
22
+ transition: background-color var(--ifm-transition-fast)
23
+ var(--ifm-transition-timing-default);
24
+ }
25
+
26
+ .toc-toggle:hover {
27
+ background-color: var(--ifm-color-emphasis-300);
28
+ }
29
+
30
+ .toc-toggle::after {
31
+ content: '';
32
+ width: 1rem;
33
+ height: 1rem;
34
+ background-color: currentColor;
35
+ /* chevron-right: "push the TOC away"; it is flipped when collapsed. */
36
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'/%3E%3C/svg%3E")
37
+ center / contain no-repeat;
38
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'/%3E%3C/svg%3E")
39
+ center / contain no-repeat;
40
+ transition: transform var(--ifm-transition-fast)
41
+ var(--ifm-transition-timing-default);
42
+ }
43
+
44
+ body.toc-collapsed .toc-toggle::after {
45
+ transform: rotate(180deg);
46
+ }
47
+
48
+ /* Collapsed: the column shrinks to a strip that keeps the button reachable, the
49
+ list is hidden, and the article takes the freed width. Infima sizes columns
50
+ through --ifm-col-width, so overriding that one property covers the width.
51
+
52
+ These rules go into the theme's own cascade layer on purpose: `future.v4`
53
+ makes Docusaurus emit layered CSS, and for `!important` declarations layer
54
+ order wins over specificity — an unlayered `!important` loses to a layered
55
+ one, so the `max-width` below would silently do nothing outside this layer. */
56
+ @layer docusaurus.theme-classic {
57
+ body.toc-collapsed .toc-column {
58
+ --ifm-col-width: 2.5rem;
59
+ padding: 0;
60
+ }
61
+
62
+ body.toc-collapsed .toc-column .theme-doc-toc-desktop {
63
+ display: none;
64
+ }
65
+
66
+ body.toc-collapsed [class*='docItemCol'] {
67
+ max-width: 100% !important;
68
+ }
69
+ }