@veluai/velu 0.2.13 → 0.2.15

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 (109) hide show
  1. package/README.md +80 -80
  2. package/dist/cli.js +37 -37
  3. package/package.json +64 -64
  4. package/runtime/velu-ui/base.css +320 -320
  5. package/runtime/velu-ui/components/Accordion.jsx +64 -64
  6. package/runtime/velu-ui/components/ApiClient.jsx +207 -207
  7. package/runtime/velu-ui/components/ApiField.jsx +87 -87
  8. package/runtime/velu-ui/components/ApiPath.jsx +63 -63
  9. package/runtime/velu-ui/components/ApiReferencePage.jsx +384 -384
  10. package/runtime/velu-ui/components/ApiSamples.jsx +36 -36
  11. package/runtime/velu-ui/components/ApiSidebar.jsx +122 -122
  12. package/runtime/velu-ui/components/AskBar.jsx +71 -71
  13. package/runtime/velu-ui/components/Callout.jsx +114 -114
  14. package/runtime/velu-ui/components/Card.jsx +131 -131
  15. package/runtime/velu-ui/components/Chatbot.jsx +885 -885
  16. package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
  17. package/runtime/velu-ui/components/Columns.jsx +56 -56
  18. package/runtime/velu-ui/components/ContextMenu.jsx +298 -273
  19. package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
  20. package/runtime/velu-ui/components/Field.jsx +81 -81
  21. package/runtime/velu-ui/components/Image.jsx +163 -163
  22. package/runtime/velu-ui/components/Logo.jsx +31 -31
  23. package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
  24. package/runtime/velu-ui/components/NavSelect.jsx +108 -108
  25. package/runtime/velu-ui/components/NotFound.jsx +63 -63
  26. package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
  27. package/runtime/velu-ui/components/PageFooter.jsx +145 -145
  28. package/runtime/velu-ui/components/PageHeader.jsx +422 -422
  29. package/runtime/velu-ui/components/PageNav.jsx +77 -77
  30. package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
  31. package/runtime/velu-ui/components/Prompt.jsx +115 -115
  32. package/runtime/velu-ui/components/Search.jsx +460 -460
  33. package/runtime/velu-ui/components/Sidebar.jsx +254 -254
  34. package/runtime/velu-ui/components/SocialLinks.jsx +90 -90
  35. package/runtime/velu-ui/components/Steps.jsx +65 -65
  36. package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
  37. package/runtime/velu-ui/components/Toc.jsx +537 -537
  38. package/runtime/velu-ui/components/TocBar.jsx +195 -195
  39. package/runtime/velu-ui/components/Tree.jsx +87 -87
  40. package/runtime/velu-ui/components/TryItBar.jsx +102 -102
  41. package/runtime/velu-ui/components/accordion.css +92 -92
  42. package/runtime/velu-ui/components/api-page.css +208 -208
  43. package/runtime/velu-ui/components/api.css +635 -635
  44. package/runtime/velu-ui/components/ask-bar.css +94 -94
  45. package/runtime/velu-ui/components/card.css +105 -105
  46. package/runtime/velu-ui/components/chatbot.css +622 -622
  47. package/runtime/velu-ui/components/code-block.css +263 -263
  48. package/runtime/velu-ui/components/context-menu.css +173 -173
  49. package/runtime/velu-ui/components/docs-layout.css +822 -822
  50. package/runtime/velu-ui/components/field.css +82 -82
  51. package/runtime/velu-ui/components/image.css +237 -237
  52. package/runtime/velu-ui/components/nav-select.css +157 -157
  53. package/runtime/velu-ui/components/not-found.css +94 -94
  54. package/runtime/velu-ui/components/page-feedback.css +241 -241
  55. package/runtime/velu-ui/components/page-footer.css +130 -130
  56. package/runtime/velu-ui/components/page-header.css +558 -558
  57. package/runtime/velu-ui/components/page-nav.css +50 -50
  58. package/runtime/velu-ui/components/powered-by.css +92 -92
  59. package/runtime/velu-ui/components/prompt.css +99 -99
  60. package/runtime/velu-ui/components/search.css +307 -307
  61. package/runtime/velu-ui/components/sidebar.css +205 -205
  62. package/runtime/velu-ui/components/steps.css +77 -77
  63. package/runtime/velu-ui/components/theme-toggle.css +102 -102
  64. package/runtime/velu-ui/components/toc-bar.css +234 -234
  65. package/runtime/velu-ui/components/tree.css +49 -49
  66. package/runtime/velu-ui/index.js +54 -54
  67. package/runtime/velu-ui/lib/api-send.js +92 -92
  68. package/runtime/velu-ui/lib/brand-icons.jsx +103 -103
  69. package/runtime/velu-ui/lib/component-schemas.js +100 -100
  70. package/runtime/velu-ui/lib/copyText.js +64 -64
  71. package/runtime/velu-ui/lib/docs-assistant.js +250 -250
  72. package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
  73. package/runtime/velu-ui/lib/pagefind.js +113 -113
  74. package/runtime/velu-ui/lib/prism-langs.js +957 -957
  75. package/runtime/velu-ui/lib/prism-loader.js +74 -74
  76. package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
  77. package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
  78. package/runtime/velu-ui/mdx-components.jsx +105 -105
  79. package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
  80. package/runtime/velu-ui/primitives/Stack.jsx +63 -63
  81. package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
  82. package/runtime/velu-ui/primitives/stack.css +3 -3
  83. package/runtime/velu-ui/primitives/switcher.css +25 -25
  84. package/runtime/velu-ui/styles.css +46 -46
  85. package/runtime/velu-ui/tokens.css +4 -4
  86. package/schema/velu.schema.json +423 -423
  87. package/src/lib/extract-mdx-error.js +170 -170
  88. package/src/lib/issues.js +159 -159
  89. package/src/lib/known-components.js +34 -34
  90. package/src/navigation.js +443 -443
  91. package/src/runtime/App.jsx +1669 -1668
  92. package/src/runtime/ErrorBoundary.jsx +54 -54
  93. package/src/runtime/client-entry.jsx +22 -22
  94. package/src/runtime/server-entry.jsx +16 -16
  95. package/src/template.html +48 -48
  96. package/templates/starter/ai-tools/claude-code.mdx +26 -26
  97. package/templates/starter/ai-tools/cursor.mdx +17 -17
  98. package/templates/starter/api-reference/introduction.mdx +43 -43
  99. package/templates/starter/development.mdx +19 -19
  100. package/templates/starter/essentials/code.mdx +29 -29
  101. package/templates/starter/essentials/images.mdx +29 -29
  102. package/templates/starter/essentials/markdown.mdx +25 -25
  103. package/templates/starter/essentials/navigation.mdx +39 -39
  104. package/templates/starter/essentials/settings.mdx +30 -30
  105. package/templates/starter/favicon.svg +6 -6
  106. package/templates/starter/index.mdx +31 -31
  107. package/templates/starter/openapi.json +160 -160
  108. package/templates/starter/quickstart.mdx +31 -31
  109. package/templates/starter/velu.json +41 -41
@@ -1,320 +1,320 @@
1
- /* Fonts are loaded via <link rel="preconnect"/"stylesheet"> in template.html
2
- (faster than an @import inside critical CSS — non-blocking, parallel). */
3
- :root {
4
- --ratio: 1.25;
5
- --s-6: calc(var(--s-5) / var(--ratio));
6
- --s-5: calc(var(--s-4) / var(--ratio));
7
- --s-4: calc(var(--s-3) / var(--ratio));
8
- --s-3: calc(var(--s-2) / var(--ratio));
9
- --s-2: calc(var(--s-1) / var(--ratio));
10
- --s-1: calc(var(--s0) / var(--ratio));
11
- --s0: 1rem;
12
- --s1: calc(var(--s0) * var(--ratio));
13
- --s2: calc(var(--s1) * var(--ratio));
14
- --s3: calc(var(--s2) * var(--ratio));
15
- --s4: calc(var(--s3) * var(--ratio));
16
- --s5: calc(var(--s4) * var(--ratio));
17
- }
18
-
19
- /* Semantic color tokens — verbatim from Figma (1:1 names, no translation).
20
- Components reference ONLY these (never raw hex), so theme switching =
21
- swapping this block (zero component changes).
22
- --page-bg is PROVISIONAL: Figma has no explicit page-background token yet
23
- (see note to Aravind) — replace once defined. */
24
- :root {
25
- --page-bg: #ffffff;
26
-
27
- --border-color: #dcdcde;
28
- --text-color: #000000;
29
- --accent-color: #dc143c;
30
- --muted-color: #7a7a7a;
31
- --surface-color: #f2f3f4;
32
-
33
- --note-bg-color: #f2f3f4;
34
- --note-stroke-color: #6b7280;
35
- --warning-bg-color: #fef3e2;
36
- --warning-stroke-color: #d97706;
37
- --info-bg-color: #e8f4fd;
38
- --info-stroke-color: #3b82f6;
39
- --tip-bg-color: #e8f5e9;
40
- --tip-stroke-color: #16a34a;
41
- --check-bg-color: #e6f9ed;
42
- --check-stroke-color: #22c55e;
43
- --danger-bg-color: #fde8e8;
44
- --danger-stroke-color: #dc2626;
45
- --callout-bg-color: #f3e8fd;
46
- --callout-stroke-color: #8b5cf6;
47
-
48
- --post-pill-color: #e8f4fd;
49
- --post-pill-stroke-color: #91bbf9;
50
- --post-cta-color: #3064e3;
51
- --get-pill-color: #e6f9ed;
52
- --get-pill-stroke-color: #22c55e;
53
- --get-cta-color: #22c55e;
54
- --put-pill-color: #fef3e2;
55
- --put-pill-stroke-color: #d97706;
56
- --put-cta-color: #d97706;
57
- --delete-pill-color: #fde8e8;
58
- --delete-pill-stroke-color: #dc2626;
59
- --delete-cta-color: #dc2626;
60
- --patch-pill-color: #f3e8fd;
61
- --patch-pill-stroke-color: #8b5cf6;
62
- --patch-cta-color: #8b5cf6;
63
- }
64
- :root[data-theme="dark"] {
65
- --page-bg: #0D0E10;
66
-
67
- --border-color: #262827;
68
- --text-color: #eeeff1;
69
- --accent-color: #dc143c;
70
- --muted-color: #7a7a7a;
71
- --surface-color: #323334;
72
-
73
- --note-bg-color: #1a1a1c;
74
- --note-stroke-color: #eeeff1;
75
- --warning-bg-color: #2a1f0d;
76
- --warning-stroke-color: #7a5a1f;
77
- --info-bg-color: #0d1f2a;
78
- --info-stroke-color: #1f5a7a;
79
- --tip-bg-color: #0d2a1a;
80
- --tip-stroke-color: #1f7a4a;
81
- --check-bg-color: #0d2a1a;
82
- --check-stroke-color: #1f7a4a;
83
- --danger-bg-color: #2a0d0d;
84
- --danger-stroke-color: #7a1f1f;
85
- --callout-bg-color: #1f0d2a;
86
- --callout-stroke-color: #5a1f7a;
87
-
88
- --post-pill-color: #1e3a8a;
89
- --post-pill-stroke-color: #3b82f6;
90
- --post-cta-color: #2563eb;
91
- --get-pill-color: #065f46;
92
- --get-pill-stroke-color: #10b981;
93
- --get-cta-color: #059669;
94
- --put-pill-color: #78350f;
95
- --put-pill-stroke-color: #f59e0b;
96
- --put-cta-color: #d97706;
97
- --delete-pill-color: #7f1d1d;
98
- --delete-pill-stroke-color: #ef4444;
99
- --delete-cta-color: #dc2626;
100
- --patch-pill-color: #4c1d95;
101
- --patch-pill-stroke-color: #8b5cf6;
102
- --patch-cta-color: #7c3aed;
103
- }
104
-
105
- :root {
106
- --measure: 66ch;
107
- --font-sans: "Google Sans Flex", sans-serif;
108
- --font-mono: "Google Sans Code", ui-monospace, SFMono-Regular, monospace;
109
- --font-brand: "Outfit", var(--font-sans);
110
- font-family: var(--font-sans);
111
-
112
- /* Approx height of the docs PageHeader (used as the offset for sticky
113
- sidebars / TOC so they stick BELOW the header, not behind it). */
114
- --velu-header-height: 7rem;
115
-
116
- /* Fluid font sizes — 375px → 1440px viewport */
117
- --f-body: 1rem; /* 16 → 16 */
118
- --f-h7: calc(var(--f-h6) / var(--ratio)); /* micro UI text — one modular step below h6 (chips, captions, kbd) */
119
- --f-h6: clamp(0.75rem, 0.188vw + 0.68rem, 0.875rem); /* 12 → 14 (reversed: mobile 14 → desktop 12, see note) */
120
- --f-h5: 1rem; /* 16 → 16 */
121
- --f-h4: clamp(1.125rem, 0.235vw + 1.07rem, 1.375rem); /* 18 → 22 */
122
- --f-h3: clamp(1.3125rem,0.423vw + 1.214rem, 1.75rem); /* 21 → 28 */
123
- --f-h2: clamp(1.5rem, 0.704vw + 1.335rem, 2.25rem); /* 24 → 36 */
124
- --f-h1: clamp(1.6875rem,1.972vw + 1.226rem, 3rem); /* 27 → 48 */
125
-
126
- /* Line-height tokens — unitless (height ÷ font-size from Figma). The
127
- single source of truth for leading; components reference these, never
128
- a hardcoded ratio. */
129
- --lh-body: 1.75;
130
- --lh-h1: 1.75;
131
- --lh-h2: 1.78;
132
- --lh-h3: 1.71;
133
- --lh-h4: 1.82;
134
- --lh-h5: 1.75;
135
- --lh-h6: 1.71;
136
- --lh-h7: 1.71;
137
-
138
- /* Hairline width — single source of truth for every box border, divider
139
- and rail. (Sub-pixel: crisp on hi-dpi; some 1x displays round it.) */
140
- --border-width: 0.5px;
141
-
142
- /* Icon stroke weight — single source of truth for every lucide icon. */
143
- --icon-stroke: 1.5;
144
-
145
- /* Icon size scale — em-relative so an icon next to text inherits the
146
- text size automatically. `--icon-size` matches surrounding text;
147
- `--icon-size-lg` is one modular step larger (for nav lists / trees
148
- where the icon should slightly outweigh the label). */
149
- --icon-size: 1em;
150
- --icon-size-lg: calc(1em * var(--ratio));
151
-
152
- /* Corner-radius scale, derived from --s0 (16px) — no raw literals.
153
- sm 8 / md 16 / lg 32. Components pick the right step. */
154
- --radius-sm: calc(var(--s0) / 2);
155
- --radius-md: var(--s0);
156
- --radius-lg: calc(var(--s0) * 2);
157
-
158
- /* Font-weight scale — single source of truth so components never
159
- write raw 300/400/500/600/700. Matches Google Sans Flex's
160
- variable-font axis stops. */
161
- --weight-light: 300;
162
- --weight-normal: 400;
163
- --weight-medium: 500;
164
- --weight-semibold: 600;
165
- --weight-bold: 700;
166
- }
167
-
168
- /* lucide-react adds class="lucide …" to every icon svg. This one rule
169
- tokenizes stroke weight app-wide (CSS overrides the svg's stroke-width
170
- attribute), so no component sets it per-icon. */
171
- .lucide {
172
- stroke-width: var(--icon-stroke);
173
- }
174
-
175
- /* Prose rhythm — wraps MDX content. MDX compiles to a flat list of
176
- sibling block elements (h1, p, the velu-ui components, etc.).
177
- Spacing is scaled by HEADING LEVEL so the document's structure is
178
- readable at a glance:
179
- - default sibling pair (paragraph rhythm): --s1
180
- - before any heading (section break): scaled by level
181
- - after a heading (heading → first child): one step under default
182
- The rules use adjacent-sibling selectors so every same-level heading
183
- gets the same lead-in regardless of what came before it (sibling
184
- equality), and the larger before-heading gap reads as the section
185
- break in every case. The owl rule sets the default; per-heading
186
- rules below override it via source order (same specificity). */
187
-
188
- /* Scroll-anchor offset for in-page links: when a TOC click lands on
189
- a heading, the browser scrolls so the heading sits at the viewport
190
- top — which would otherwise put it BEHIND the sticky chrome
191
- (header + TocBar). `scroll-margin-block-start` tells the scroll
192
- algorithm to leave that much space above the target.
193
-
194
- --velu-scroll-offset is published by App.jsx as
195
- `headerHeight + tocbarHeight + 1rem`, measured live via
196
- ResizeObserver. The scroll-spy reads the SAME variable as its
197
- trigger line, so scrollIntoView lands the heading exactly where
198
- the spy promotes it — no magic numbers, no drift. */
199
- .velu-hero h1,
200
- .velu-prose :is(h1, h2, h3, h4, h5, h6) {
201
- scroll-margin-block-start: var(--velu-scroll-offset, 5rem);
202
- }
203
-
204
- /* Default prose rhythm. */
205
- .velu-prose > * + * {
206
- margin-block-start: var(--s1);
207
- }
208
-
209
- /* Before-heading: progressively larger gap above higher-level headings.
210
- Same level → same gap → siblings read at equal intervals. */
211
- .velu-prose > * + h2 {
212
- margin-block-start: var(--s4);
213
- }
214
- .velu-prose > * + h3 {
215
- margin-block-start: var(--s3);
216
- }
217
- .velu-prose > * + h4 {
218
- margin-block-start: var(--s2);
219
- }
220
-
221
- /* After-heading: tighter, so the heading reads as "introducing" what
222
- follows rather than floating alone above a chasm. */
223
- .velu-prose > h1 + *,
224
- .velu-prose > h2 + *,
225
- .velu-prose > h3 + *,
226
- .velu-prose > h4 + *,
227
- .velu-prose > h5 + *,
228
- .velu-prose > h6 + * {
229
- margin-block-start: var(--s0);
230
- }
231
-
232
- /* Page hero — the frontmatter (title + description) is wrapped in
233
- `.velu-hero` so the title and description sit tight as a single
234
- unit, and a larger break separates the hero from the prose body. */
235
- .velu-hero > h1 + p {
236
- margin-block-start: var(--s-2);
237
- }
238
- /* hero → body is an h1 → h2 transition (level-down, not peer-to-peer),
239
- so the break is one modular step LARGER than the regular section
240
- break above an h2 (--s4). Keeps the scale strictly monotonic:
241
- level-down break > peer break. */
242
- .velu-hero + .velu-prose {
243
- margin-block-start: var(--s5);
244
- }
245
-
246
- /* Discreet thin scrollbar with a tokenised colour in every state — applied
247
- GLOBALLY (the `*` selector) so every scrollable element inherits it by
248
- default: the chatbot, dialogs, and any future component, with no
249
- per-component opt-in. Component-specific scrollbar rules win via
250
- specificity (e.g. the nav/TOC reveal-on-hover overrides below).
251
- `.velu-hide-scrollbar` stays as an explicit alias for existing markup.
252
-
253
- No transitions: Firefox doesn't honour CSS transitions on `scrollbar-color`
254
- consistently, and the in-between interpolation + its auto-derived
255
- hover/active colours can flash a yellow/amber accent (an OS / system accent
256
- when interpolating). Hard switches avoid that. Same reason every WebKit
257
- thumb pseudo-state is set explicitly — otherwise Chromium can fall back to
258
- the Windows 11 system accent on hover. */
259
- *,
260
- .velu-hide-scrollbar {
261
- scrollbar-width: thin;
262
- /* Single colour both at rest and on hover. Firefox auto-darkens on
263
- interaction, but starting from a defined light grey keeps the
264
- derived shade in the same family (no yellow flash). */
265
- scrollbar-color: var(--border-color) transparent;
266
- }
267
- *::-webkit-scrollbar,
268
- .velu-hide-scrollbar::-webkit-scrollbar {
269
- inline-size: 6px;
270
- block-size: 6px;
271
- }
272
- *::-webkit-scrollbar-track,
273
- .velu-hide-scrollbar::-webkit-scrollbar-track {
274
- background: transparent;
275
- }
276
- *::-webkit-scrollbar-thumb,
277
- .velu-hide-scrollbar::-webkit-scrollbar-thumb {
278
- background: var(--border-color);
279
- border-radius: 999px;
280
- }
281
- *::-webkit-scrollbar-thumb:hover,
282
- *::-webkit-scrollbar-thumb:active,
283
- .velu-hide-scrollbar::-webkit-scrollbar-thumb:hover,
284
- .velu-hide-scrollbar::-webkit-scrollbar-thumb:active {
285
- background: var(--muted-color);
286
- }
287
-
288
- /* Unitless line-height ratios (height ÷ font-size from Figma) */
289
- body {
290
- font-size: var(--f-body);
291
- line-height: var(--lh-body);
292
- font-weight: var(--weight-normal);
293
- background: var(--page-bg);
294
- color: var(--text-color);
295
- }
296
- h1 { font-size: var(--f-h1); line-height: var(--lh-h1); font-weight: var(--weight-semibold); }
297
- h2 { font-size: var(--f-h2); line-height: var(--lh-h2); font-weight: var(--weight-medium); }
298
- h3 { font-size: var(--f-h3); line-height: var(--lh-h3); font-weight: var(--weight-medium); }
299
- h4 { font-size: var(--f-h4); line-height: var(--lh-h4); font-weight: var(--weight-normal); }
300
- h5 { font-size: var(--f-h5); line-height: var(--lh-h5); font-weight: var(--weight-normal); }
301
- h6 { font-size: var(--f-h6); line-height: var(--lh-h6); font-weight: var(--weight-normal); }
302
-
303
- * {
304
- max-width: var(--measure);
305
- box-sizing: border-box;
306
- }
307
-
308
- html,
309
- body,
310
- div,
311
- header,
312
- nav,
313
- main,
314
- footer,
315
- pre,
316
- code,
317
- span,
318
- button {
319
- max-width: none;
320
- }
1
+ /* Fonts are loaded via <link rel="preconnect"/"stylesheet"> in template.html
2
+ (faster than an @import inside critical CSS — non-blocking, parallel). */
3
+ :root {
4
+ --ratio: 1.25;
5
+ --s-6: calc(var(--s-5) / var(--ratio));
6
+ --s-5: calc(var(--s-4) / var(--ratio));
7
+ --s-4: calc(var(--s-3) / var(--ratio));
8
+ --s-3: calc(var(--s-2) / var(--ratio));
9
+ --s-2: calc(var(--s-1) / var(--ratio));
10
+ --s-1: calc(var(--s0) / var(--ratio));
11
+ --s0: 1rem;
12
+ --s1: calc(var(--s0) * var(--ratio));
13
+ --s2: calc(var(--s1) * var(--ratio));
14
+ --s3: calc(var(--s2) * var(--ratio));
15
+ --s4: calc(var(--s3) * var(--ratio));
16
+ --s5: calc(var(--s4) * var(--ratio));
17
+ }
18
+
19
+ /* Semantic color tokens — verbatim from Figma (1:1 names, no translation).
20
+ Components reference ONLY these (never raw hex), so theme switching =
21
+ swapping this block (zero component changes).
22
+ --page-bg is PROVISIONAL: Figma has no explicit page-background token yet
23
+ (see note to Aravind) — replace once defined. */
24
+ :root {
25
+ --page-bg: #ffffff;
26
+
27
+ --border-color: #dcdcde;
28
+ --text-color: #000000;
29
+ --accent-color: #dc143c;
30
+ --muted-color: #7a7a7a;
31
+ --surface-color: #f2f3f4;
32
+
33
+ --note-bg-color: #f2f3f4;
34
+ --note-stroke-color: #6b7280;
35
+ --warning-bg-color: #fef3e2;
36
+ --warning-stroke-color: #d97706;
37
+ --info-bg-color: #e8f4fd;
38
+ --info-stroke-color: #3b82f6;
39
+ --tip-bg-color: #e8f5e9;
40
+ --tip-stroke-color: #16a34a;
41
+ --check-bg-color: #e6f9ed;
42
+ --check-stroke-color: #22c55e;
43
+ --danger-bg-color: #fde8e8;
44
+ --danger-stroke-color: #dc2626;
45
+ --callout-bg-color: #f3e8fd;
46
+ --callout-stroke-color: #8b5cf6;
47
+
48
+ --post-pill-color: #e8f4fd;
49
+ --post-pill-stroke-color: #91bbf9;
50
+ --post-cta-color: #3064e3;
51
+ --get-pill-color: #e6f9ed;
52
+ --get-pill-stroke-color: #22c55e;
53
+ --get-cta-color: #22c55e;
54
+ --put-pill-color: #fef3e2;
55
+ --put-pill-stroke-color: #d97706;
56
+ --put-cta-color: #d97706;
57
+ --delete-pill-color: #fde8e8;
58
+ --delete-pill-stroke-color: #dc2626;
59
+ --delete-cta-color: #dc2626;
60
+ --patch-pill-color: #f3e8fd;
61
+ --patch-pill-stroke-color: #8b5cf6;
62
+ --patch-cta-color: #8b5cf6;
63
+ }
64
+ :root[data-theme="dark"] {
65
+ --page-bg: #0D0E10;
66
+
67
+ --border-color: #262827;
68
+ --text-color: #eeeff1;
69
+ --accent-color: #dc143c;
70
+ --muted-color: #7a7a7a;
71
+ --surface-color: #323334;
72
+
73
+ --note-bg-color: #1a1a1c;
74
+ --note-stroke-color: #eeeff1;
75
+ --warning-bg-color: #2a1f0d;
76
+ --warning-stroke-color: #7a5a1f;
77
+ --info-bg-color: #0d1f2a;
78
+ --info-stroke-color: #1f5a7a;
79
+ --tip-bg-color: #0d2a1a;
80
+ --tip-stroke-color: #1f7a4a;
81
+ --check-bg-color: #0d2a1a;
82
+ --check-stroke-color: #1f7a4a;
83
+ --danger-bg-color: #2a0d0d;
84
+ --danger-stroke-color: #7a1f1f;
85
+ --callout-bg-color: #1f0d2a;
86
+ --callout-stroke-color: #5a1f7a;
87
+
88
+ --post-pill-color: #1e3a8a;
89
+ --post-pill-stroke-color: #3b82f6;
90
+ --post-cta-color: #2563eb;
91
+ --get-pill-color: #065f46;
92
+ --get-pill-stroke-color: #10b981;
93
+ --get-cta-color: #059669;
94
+ --put-pill-color: #78350f;
95
+ --put-pill-stroke-color: #f59e0b;
96
+ --put-cta-color: #d97706;
97
+ --delete-pill-color: #7f1d1d;
98
+ --delete-pill-stroke-color: #ef4444;
99
+ --delete-cta-color: #dc2626;
100
+ --patch-pill-color: #4c1d95;
101
+ --patch-pill-stroke-color: #8b5cf6;
102
+ --patch-cta-color: #7c3aed;
103
+ }
104
+
105
+ :root {
106
+ --measure: 66ch;
107
+ --font-sans: "Google Sans Flex", sans-serif;
108
+ --font-mono: "Google Sans Code", ui-monospace, SFMono-Regular, monospace;
109
+ --font-brand: "Outfit", var(--font-sans);
110
+ font-family: var(--font-sans);
111
+
112
+ /* Approx height of the docs PageHeader (used as the offset for sticky
113
+ sidebars / TOC so they stick BELOW the header, not behind it). */
114
+ --velu-header-height: 7rem;
115
+
116
+ /* Fluid font sizes — 375px → 1440px viewport */
117
+ --f-body: 1rem; /* 16 → 16 */
118
+ --f-h7: calc(var(--f-h6) / var(--ratio)); /* micro UI text — one modular step below h6 (chips, captions, kbd) */
119
+ --f-h6: clamp(0.75rem, 0.188vw + 0.68rem, 0.875rem); /* 12 → 14 (reversed: mobile 14 → desktop 12, see note) */
120
+ --f-h5: 1rem; /* 16 → 16 */
121
+ --f-h4: clamp(1.125rem, 0.235vw + 1.07rem, 1.375rem); /* 18 → 22 */
122
+ --f-h3: clamp(1.3125rem,0.423vw + 1.214rem, 1.75rem); /* 21 → 28 */
123
+ --f-h2: clamp(1.5rem, 0.704vw + 1.335rem, 2.25rem); /* 24 → 36 */
124
+ --f-h1: clamp(1.6875rem,1.972vw + 1.226rem, 3rem); /* 27 → 48 */
125
+
126
+ /* Line-height tokens — unitless (height ÷ font-size from Figma). The
127
+ single source of truth for leading; components reference these, never
128
+ a hardcoded ratio. */
129
+ --lh-body: 1.75;
130
+ --lh-h1: 1.75;
131
+ --lh-h2: 1.78;
132
+ --lh-h3: 1.71;
133
+ --lh-h4: 1.82;
134
+ --lh-h5: 1.75;
135
+ --lh-h6: 1.71;
136
+ --lh-h7: 1.71;
137
+
138
+ /* Hairline width — single source of truth for every box border, divider
139
+ and rail. (Sub-pixel: crisp on hi-dpi; some 1x displays round it.) */
140
+ --border-width: 0.5px;
141
+
142
+ /* Icon stroke weight — single source of truth for every lucide icon. */
143
+ --icon-stroke: 1.5;
144
+
145
+ /* Icon size scale — em-relative so an icon next to text inherits the
146
+ text size automatically. `--icon-size` matches surrounding text;
147
+ `--icon-size-lg` is one modular step larger (for nav lists / trees
148
+ where the icon should slightly outweigh the label). */
149
+ --icon-size: 1em;
150
+ --icon-size-lg: calc(1em * var(--ratio));
151
+
152
+ /* Corner-radius scale, derived from --s0 (16px) — no raw literals.
153
+ sm 8 / md 16 / lg 32. Components pick the right step. */
154
+ --radius-sm: calc(var(--s0) / 2);
155
+ --radius-md: var(--s0);
156
+ --radius-lg: calc(var(--s0) * 2);
157
+
158
+ /* Font-weight scale — single source of truth so components never
159
+ write raw 300/400/500/600/700. Matches Google Sans Flex's
160
+ variable-font axis stops. */
161
+ --weight-light: 300;
162
+ --weight-normal: 400;
163
+ --weight-medium: 500;
164
+ --weight-semibold: 600;
165
+ --weight-bold: 700;
166
+ }
167
+
168
+ /* lucide-react adds class="lucide …" to every icon svg. This one rule
169
+ tokenizes stroke weight app-wide (CSS overrides the svg's stroke-width
170
+ attribute), so no component sets it per-icon. */
171
+ .lucide {
172
+ stroke-width: var(--icon-stroke);
173
+ }
174
+
175
+ /* Prose rhythm — wraps MDX content. MDX compiles to a flat list of
176
+ sibling block elements (h1, p, the velu-ui components, etc.).
177
+ Spacing is scaled by HEADING LEVEL so the document's structure is
178
+ readable at a glance:
179
+ - default sibling pair (paragraph rhythm): --s1
180
+ - before any heading (section break): scaled by level
181
+ - after a heading (heading → first child): one step under default
182
+ The rules use adjacent-sibling selectors so every same-level heading
183
+ gets the same lead-in regardless of what came before it (sibling
184
+ equality), and the larger before-heading gap reads as the section
185
+ break in every case. The owl rule sets the default; per-heading
186
+ rules below override it via source order (same specificity). */
187
+
188
+ /* Scroll-anchor offset for in-page links: when a TOC click lands on
189
+ a heading, the browser scrolls so the heading sits at the viewport
190
+ top — which would otherwise put it BEHIND the sticky chrome
191
+ (header + TocBar). `scroll-margin-block-start` tells the scroll
192
+ algorithm to leave that much space above the target.
193
+
194
+ --velu-scroll-offset is published by App.jsx as
195
+ `headerHeight + tocbarHeight + 1rem`, measured live via
196
+ ResizeObserver. The scroll-spy reads the SAME variable as its
197
+ trigger line, so scrollIntoView lands the heading exactly where
198
+ the spy promotes it — no magic numbers, no drift. */
199
+ .velu-hero h1,
200
+ .velu-prose :is(h1, h2, h3, h4, h5, h6) {
201
+ scroll-margin-block-start: var(--velu-scroll-offset, 5rem);
202
+ }
203
+
204
+ /* Default prose rhythm. */
205
+ .velu-prose > * + * {
206
+ margin-block-start: var(--s1);
207
+ }
208
+
209
+ /* Before-heading: progressively larger gap above higher-level headings.
210
+ Same level → same gap → siblings read at equal intervals. */
211
+ .velu-prose > * + h2 {
212
+ margin-block-start: var(--s4);
213
+ }
214
+ .velu-prose > * + h3 {
215
+ margin-block-start: var(--s3);
216
+ }
217
+ .velu-prose > * + h4 {
218
+ margin-block-start: var(--s2);
219
+ }
220
+
221
+ /* After-heading: tighter, so the heading reads as "introducing" what
222
+ follows rather than floating alone above a chasm. */
223
+ .velu-prose > h1 + *,
224
+ .velu-prose > h2 + *,
225
+ .velu-prose > h3 + *,
226
+ .velu-prose > h4 + *,
227
+ .velu-prose > h5 + *,
228
+ .velu-prose > h6 + * {
229
+ margin-block-start: var(--s0);
230
+ }
231
+
232
+ /* Page hero — the frontmatter (title + description) is wrapped in
233
+ `.velu-hero` so the title and description sit tight as a single
234
+ unit, and a larger break separates the hero from the prose body. */
235
+ .velu-hero > h1 + p {
236
+ margin-block-start: var(--s-2);
237
+ }
238
+ /* hero → body is an h1 → h2 transition (level-down, not peer-to-peer),
239
+ so the break is one modular step LARGER than the regular section
240
+ break above an h2 (--s4). Keeps the scale strictly monotonic:
241
+ level-down break > peer break. */
242
+ .velu-hero + .velu-prose {
243
+ margin-block-start: var(--s5);
244
+ }
245
+
246
+ /* Discreet thin scrollbar with a tokenised colour in every state — applied
247
+ GLOBALLY (the `*` selector) so every scrollable element inherits it by
248
+ default: the chatbot, dialogs, and any future component, with no
249
+ per-component opt-in. Component-specific scrollbar rules win via
250
+ specificity (e.g. the nav/TOC reveal-on-hover overrides below).
251
+ `.velu-hide-scrollbar` stays as an explicit alias for existing markup.
252
+
253
+ No transitions: Firefox doesn't honour CSS transitions on `scrollbar-color`
254
+ consistently, and the in-between interpolation + its auto-derived
255
+ hover/active colours can flash a yellow/amber accent (an OS / system accent
256
+ when interpolating). Hard switches avoid that. Same reason every WebKit
257
+ thumb pseudo-state is set explicitly — otherwise Chromium can fall back to
258
+ the Windows 11 system accent on hover. */
259
+ *,
260
+ .velu-hide-scrollbar {
261
+ scrollbar-width: thin;
262
+ /* Single colour both at rest and on hover. Firefox auto-darkens on
263
+ interaction, but starting from a defined light grey keeps the
264
+ derived shade in the same family (no yellow flash). */
265
+ scrollbar-color: var(--border-color) transparent;
266
+ }
267
+ *::-webkit-scrollbar,
268
+ .velu-hide-scrollbar::-webkit-scrollbar {
269
+ inline-size: 6px;
270
+ block-size: 6px;
271
+ }
272
+ *::-webkit-scrollbar-track,
273
+ .velu-hide-scrollbar::-webkit-scrollbar-track {
274
+ background: transparent;
275
+ }
276
+ *::-webkit-scrollbar-thumb,
277
+ .velu-hide-scrollbar::-webkit-scrollbar-thumb {
278
+ background: var(--border-color);
279
+ border-radius: 999px;
280
+ }
281
+ *::-webkit-scrollbar-thumb:hover,
282
+ *::-webkit-scrollbar-thumb:active,
283
+ .velu-hide-scrollbar::-webkit-scrollbar-thumb:hover,
284
+ .velu-hide-scrollbar::-webkit-scrollbar-thumb:active {
285
+ background: var(--muted-color);
286
+ }
287
+
288
+ /* Unitless line-height ratios (height ÷ font-size from Figma) */
289
+ body {
290
+ font-size: var(--f-body);
291
+ line-height: var(--lh-body);
292
+ font-weight: var(--weight-normal);
293
+ background: var(--page-bg);
294
+ color: var(--text-color);
295
+ }
296
+ h1 { font-size: var(--f-h1); line-height: var(--lh-h1); font-weight: var(--weight-semibold); }
297
+ h2 { font-size: var(--f-h2); line-height: var(--lh-h2); font-weight: var(--weight-medium); }
298
+ h3 { font-size: var(--f-h3); line-height: var(--lh-h3); font-weight: var(--weight-medium); }
299
+ h4 { font-size: var(--f-h4); line-height: var(--lh-h4); font-weight: var(--weight-normal); }
300
+ h5 { font-size: var(--f-h5); line-height: var(--lh-h5); font-weight: var(--weight-normal); }
301
+ h6 { font-size: var(--f-h6); line-height: var(--lh-h6); font-weight: var(--weight-normal); }
302
+
303
+ * {
304
+ max-width: var(--measure);
305
+ box-sizing: border-box;
306
+ }
307
+
308
+ html,
309
+ body,
310
+ div,
311
+ header,
312
+ nav,
313
+ main,
314
+ footer,
315
+ pre,
316
+ code,
317
+ span,
318
+ button {
319
+ max-width: none;
320
+ }