@veluai/velu 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 (90) hide show
  1. package/dist/cli.js +11 -0
  2. package/package.json +52 -0
  3. package/runtime/velu-ui/base.css +311 -0
  4. package/runtime/velu-ui/components/Accordion.jsx +64 -0
  5. package/runtime/velu-ui/components/ApiClient.jsx +121 -0
  6. package/runtime/velu-ui/components/ApiField.jsx +87 -0
  7. package/runtime/velu-ui/components/ApiPath.jsx +63 -0
  8. package/runtime/velu-ui/components/ApiSidebar.jsx +122 -0
  9. package/runtime/velu-ui/components/AskBar.jsx +71 -0
  10. package/runtime/velu-ui/components/Callout.jsx +114 -0
  11. package/runtime/velu-ui/components/Card.jsx +131 -0
  12. package/runtime/velu-ui/components/Chatbot.jsx +596 -0
  13. package/runtime/velu-ui/components/CodeBlock.jsx +375 -0
  14. package/runtime/velu-ui/components/Columns.jsx +56 -0
  15. package/runtime/velu-ui/components/Field.jsx +81 -0
  16. package/runtime/velu-ui/components/Image.jsx +163 -0
  17. package/runtime/velu-ui/components/MethodBadge.jsx +31 -0
  18. package/runtime/velu-ui/components/NavSelect.jsx +108 -0
  19. package/runtime/velu-ui/components/PageFeedback.jsx +219 -0
  20. package/runtime/velu-ui/components/PageFooter.jsx +213 -0
  21. package/runtime/velu-ui/components/PageHeader.jsx +414 -0
  22. package/runtime/velu-ui/components/PageNav.jsx +77 -0
  23. package/runtime/velu-ui/components/PoweredBy.jsx +51 -0
  24. package/runtime/velu-ui/components/Prompt.jsx +115 -0
  25. package/runtime/velu-ui/components/Search.jsx +366 -0
  26. package/runtime/velu-ui/components/Sidebar.jsx +191 -0
  27. package/runtime/velu-ui/components/Steps.jsx +65 -0
  28. package/runtime/velu-ui/components/ThemeToggle.jsx +48 -0
  29. package/runtime/velu-ui/components/Toc.jsx +537 -0
  30. package/runtime/velu-ui/components/TocBar.jsx +195 -0
  31. package/runtime/velu-ui/components/Tree.jsx +87 -0
  32. package/runtime/velu-ui/components/TryItBar.jsx +90 -0
  33. package/runtime/velu-ui/components/accordion.css +92 -0
  34. package/runtime/velu-ui/components/api.css +479 -0
  35. package/runtime/velu-ui/components/ask-bar.css +94 -0
  36. package/runtime/velu-ui/components/card.css +105 -0
  37. package/runtime/velu-ui/components/chatbot.css +617 -0
  38. package/runtime/velu-ui/components/code-block.css +263 -0
  39. package/runtime/velu-ui/components/docs-layout.css +775 -0
  40. package/runtime/velu-ui/components/field.css +82 -0
  41. package/runtime/velu-ui/components/image.css +237 -0
  42. package/runtime/velu-ui/components/nav-select.css +157 -0
  43. package/runtime/velu-ui/components/page-feedback.css +241 -0
  44. package/runtime/velu-ui/components/page-footer.css +130 -0
  45. package/runtime/velu-ui/components/page-header.css +520 -0
  46. package/runtime/velu-ui/components/page-nav.css +50 -0
  47. package/runtime/velu-ui/components/powered-by.css +66 -0
  48. package/runtime/velu-ui/components/prompt.css +99 -0
  49. package/runtime/velu-ui/components/search.css +307 -0
  50. package/runtime/velu-ui/components/sidebar.css +144 -0
  51. package/runtime/velu-ui/components/steps.css +77 -0
  52. package/runtime/velu-ui/components/theme-toggle.css +70 -0
  53. package/runtime/velu-ui/components/toc-bar.css +234 -0
  54. package/runtime/velu-ui/components/tree.css +49 -0
  55. package/runtime/velu-ui/index.js +45 -0
  56. package/runtime/velu-ui/lib/copyText.js +64 -0
  57. package/runtime/velu-ui/lib/lang-icons.jsx +156 -0
  58. package/runtime/velu-ui/lib/prism-langs.js +957 -0
  59. package/runtime/velu-ui/lib/prism-loader.js +74 -0
  60. package/runtime/velu-ui/lib/resolveIcon.jsx +29 -0
  61. package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -0
  62. package/runtime/velu-ui/mdx-components.jsx +85 -0
  63. package/runtime/velu-ui/primitives/Cluster.jsx +49 -0
  64. package/runtime/velu-ui/primitives/Stack.jsx +63 -0
  65. package/runtime/velu-ui/primitives/Switcher.jsx +57 -0
  66. package/runtime/velu-ui/primitives/stack.css +3 -0
  67. package/runtime/velu-ui/primitives/switcher.css +25 -0
  68. package/runtime/velu-ui/styles.css +43 -0
  69. package/runtime/velu-ui/tokens.css +4 -0
  70. package/schema/velu.schema.json +167 -0
  71. package/src/navigation.js +434 -0
  72. package/src/runtime/App.jsx +1473 -0
  73. package/src/runtime/client-entry.jsx +22 -0
  74. package/src/runtime/server-entry.jsx +16 -0
  75. package/src/template.html +48 -0
  76. package/templates/starter/ai-tools/claude-code.mdx +26 -0
  77. package/templates/starter/ai-tools/cursor.mdx +17 -0
  78. package/templates/starter/api-reference/endpoint/create.mdx +24 -0
  79. package/templates/starter/api-reference/endpoint/get.mdx +27 -0
  80. package/templates/starter/api-reference/introduction.mdx +28 -0
  81. package/templates/starter/development.mdx +19 -0
  82. package/templates/starter/essentials/code.mdx +28 -0
  83. package/templates/starter/essentials/images.mdx +29 -0
  84. package/templates/starter/essentials/markdown.mdx +25 -0
  85. package/templates/starter/essentials/navigation.mdx +39 -0
  86. package/templates/starter/essentials/settings.mdx +30 -0
  87. package/templates/starter/favicon.svg +6 -0
  88. package/templates/starter/index.mdx +31 -0
  89. package/templates/starter/quickstart.mdx +31 -0
  90. package/templates/starter/velu.json +33 -0
@@ -0,0 +1,520 @@
1
+ /* PageHeader — top-of-page header. All values are tokens; light/dark
2
+ via [data-theme]. Vertical rhythm is the Stack primitive; columns
3
+ are CSS grid (so the centre slot stays centred regardless of how
4
+ wide the brand or actions get). */
5
+
6
+ .velu-header {
7
+ /* Sticky to the viewport top so the header stays visible as the
8
+ page scrolls. z-index is above sidebars/article content but
9
+ below modals (chatbot z:50, search palette z:60). */
10
+ position: sticky;
11
+ inset-block-start: 0;
12
+ z-index: 30;
13
+ /* No bottom padding — the tabs row sits flush at the header's
14
+ bottom edge so the active tab's 2px underline meets the header's
15
+ bottom border (the selector "touches" the header line). */
16
+ padding-block-start: var(--s-1);
17
+ padding-block-end: 0;
18
+ /* Symmetric inline gutter so the brand (left) and the actions
19
+ (right) are pulled in to the same level and align with the page
20
+ content below. */
21
+ padding-inline: var(--s5);
22
+ /* --surface-color resolves to #f2f3f4 in light mode (and themes
23
+ to #323334 in dark) — a subtle hairline separator. */
24
+ border-block-end: var(--border-width) solid var(--surface-color);
25
+ background: var(--page-bg);
26
+ color: var(--text-color);
27
+ /* Background + shadow animate so the rest-state↔scrolled-state flip
28
+ is smooth (not a hard cut). backdrop-filter can't transition
29
+ cleanly in every engine, so we don't try. */
30
+ transition: background 0.18s ease, box-shadow 0.18s ease;
31
+ /* Lift shadow recipe, theme-aware. On the near-black dark surface a
32
+ low-alpha black shadow is invisible, so dark mode uses a much
33
+ higher alpha + larger blur to read as a lift. */
34
+ --velu-header-shadow: 0 var(--s-4) var(--s0)
35
+ color-mix(in srgb, #000 8%, transparent);
36
+ }
37
+ [data-theme='dark'] .velu-header {
38
+ --velu-header-shadow: 0 var(--s-3) var(--s1)
39
+ color-mix(in srgb, #000 55%, transparent);
40
+ }
41
+
42
+ /* Once the user has scrolled even a single pixel, the header becomes
43
+ a translucent frosted-glass overlay so the article content shows
44
+ through faintly underneath. color-mix keeps the recipe token-driven:
45
+ 75% --page-bg + transparent → 75% opaque in either theme. */
46
+ .velu-header--scrolled {
47
+ background: color-mix(in srgb, var(--page-bg) 75%, transparent);
48
+ backdrop-filter: saturate(140%) blur(12px);
49
+ -webkit-backdrop-filter: saturate(140%) blur(12px);
50
+ /* Lift shadow (theme-aware recipe defined on .velu-header) so the
51
+ header reads as raised above the article scrolling beneath it. */
52
+ box-shadow: var(--velu-header-shadow);
53
+ }
54
+
55
+
56
+ /* ── Top row ────────────────────────────────────────────────────────── */
57
+ /* 3-column grid: brand at the inline-start, center slot in the middle,
58
+ actions at the inline-end. The center column flexes (1fr) so the
59
+ brand and actions stay snug to their edges while the search sits
60
+ between them; justify-self centers the center node within its 1fr
61
+ cell so it doesn't stretch. */
62
+ .velu-header__top {
63
+ display: grid;
64
+ grid-template-columns: auto 1fr auto;
65
+ align-items: center;
66
+ gap: var(--s1);
67
+ }
68
+ .velu-header__col--brand {
69
+ justify-self: start;
70
+ }
71
+ .velu-header__col--center {
72
+ justify-self: center;
73
+ min-inline-size: 0;
74
+ }
75
+ .velu-header__col--actions {
76
+ justify-self: end;
77
+ }
78
+
79
+ /* Brand */
80
+ .velu-header__brand-link {
81
+ display: inline-flex;
82
+ align-items: center;
83
+ text-decoration: none;
84
+ color: inherit;
85
+ }
86
+ .velu-header__mark {
87
+ flex: none;
88
+ /* 32 × 24 — exact ratio of the brand mark's viewBox. */
89
+ inline-size: 2em;
90
+ block-size: 1.5em;
91
+ color: var(--accent-color);
92
+ }
93
+ .velu-header__wordmark {
94
+ font-family: var(--font-brand);
95
+ font-weight: var(--weight-light);
96
+ /* 32px wordmark. */
97
+ font-size: 2rem;
98
+ line-height: 1;
99
+ letter-spacing: -0.01em;
100
+ color: var(--text-color);
101
+ }
102
+
103
+ /* ── Action buttons ─────────────────────────────────────────────────── */
104
+ .velu-header__action {
105
+ display: inline-flex;
106
+ align-items: center;
107
+ gap: var(--s-3);
108
+ padding: var(--s-3);
109
+ border-radius: var(--radius-sm);
110
+ font: inherit;
111
+ font-size: var(--f-h6);
112
+ font-weight: var(--weight-medium);
113
+ /* No explicit line-height — inherits the page's --lh-body so the
114
+ button computes to the same height as the Search trigger (which
115
+ also inherits). The button's text contributes the same vertical
116
+ space as the search label. */
117
+ text-decoration: none;
118
+ cursor: pointer;
119
+ transition: background 0.12s ease, border-color 0.12s ease,
120
+ color 0.12s ease;
121
+ }
122
+ .velu-header__action--outlined {
123
+ background: var(--page-bg);
124
+ border: var(--border-width) solid var(--border-color);
125
+ color: var(--text-color);
126
+ }
127
+ .velu-header__action--outlined:hover {
128
+ border-color: var(--accent-color);
129
+ color: var(--accent-color);
130
+ }
131
+ .velu-header__action--primary {
132
+ background: var(--accent-color);
133
+ border: var(--border-width) solid var(--accent-color);
134
+ color: #fff;
135
+ }
136
+ .velu-header__action--primary:hover {
137
+ background: color-mix(in srgb, var(--accent-color) 88%, #000);
138
+ }
139
+ .velu-header__action-icon {
140
+ display: inline-flex;
141
+ flex: none;
142
+ /* Match the Search trigger's icon at rest — muted. Flips to the
143
+ accent color on hover of an outlined action (the text + border
144
+ also go accent there, so the icon shifts with them). */
145
+ color: var(--muted-color);
146
+ transition: color 0.12s ease;
147
+ }
148
+ .velu-header__action--outlined:hover .velu-header__action-icon {
149
+ color: var(--accent-color);
150
+ }
151
+
152
+ /* Inline list of action buttons. Same cluster spacing as before; gets
153
+ `display: none` at narrow widths so the kebab takes over. */
154
+ .velu-header__actions-list {
155
+ display: inline-flex;
156
+ align-items: center;
157
+ gap: var(--s-2);
158
+ }
159
+
160
+ /* ── Kebab overflow menu (narrow widths only) ──────────────────────── */
161
+ /* Hidden at wide widths — the inline action list is shown instead. */
162
+ .velu-header__kebab {
163
+ position: relative;
164
+ display: none;
165
+ }
166
+ .velu-header__kebab-toggle {
167
+ display: inline-flex;
168
+ align-items: center;
169
+ justify-content: center;
170
+ inline-size: 2rem;
171
+ block-size: 2rem;
172
+ padding: 0;
173
+ background: transparent;
174
+ border: 0;
175
+ border-radius: var(--radius-sm);
176
+ color: var(--muted-color);
177
+ cursor: pointer;
178
+ transition: color 0.12s ease, background 0.12s ease;
179
+ }
180
+ .velu-header__kebab-toggle:hover {
181
+ color: var(--text-color);
182
+ background: var(--surface-color);
183
+ }
184
+
185
+ /* Dropdown — anchored to the kebab button's bottom-right. Same fabric
186
+ as the page (no shadow flourish — matches the TocBar dropdown). */
187
+ .velu-header__kebab-menu {
188
+ position: absolute;
189
+ inset-block-start: calc(100% + var(--s-3));
190
+ inset-inline-end: 0;
191
+ margin: 0;
192
+ padding-block: var(--s-2);
193
+ padding-inline: 0;
194
+ list-style: none;
195
+ min-inline-size: 10rem;
196
+ background: var(--page-bg);
197
+ border: var(--border-width) solid var(--border-color);
198
+ border-radius: var(--radius-sm);
199
+ opacity: 0;
200
+ visibility: hidden;
201
+ transform: translateY(-0.25rem);
202
+ pointer-events: none;
203
+ transition:
204
+ opacity 0.15s ease,
205
+ transform 0.15s ease,
206
+ visibility 0s linear 0.15s;
207
+ z-index: 31;
208
+ }
209
+ .velu-header__kebab[data-open='true'] .velu-header__kebab-menu {
210
+ opacity: 1;
211
+ visibility: visible;
212
+ transform: translateY(0);
213
+ pointer-events: auto;
214
+ transition:
215
+ opacity 0.15s ease,
216
+ transform 0.15s ease,
217
+ visibility 0s;
218
+ }
219
+ .velu-header__kebab-item {
220
+ display: flex;
221
+ align-items: center;
222
+ gap: var(--s-3);
223
+ inline-size: 100%;
224
+ padding-block: var(--s-2);
225
+ padding-inline: var(--s0);
226
+ background: transparent;
227
+ border: 0;
228
+ font: inherit;
229
+ font-size: var(--f-h6);
230
+ color: var(--text-color);
231
+ text-align: start;
232
+ text-decoration: none;
233
+ cursor: pointer;
234
+ transition: background 0.12s ease, color 0.12s ease;
235
+ }
236
+ .velu-header__kebab-item:hover {
237
+ background: var(--surface-color);
238
+ color: var(--accent-color);
239
+ }
240
+ .velu-header__kebab-icon {
241
+ display: inline-flex;
242
+ flex: none;
243
+ color: var(--muted-color);
244
+ }
245
+ .velu-header__kebab-item:hover .velu-header__kebab-icon {
246
+ color: var(--accent-color);
247
+ }
248
+
249
+ /* ── Breadcrumb (mobile-only replacement for tabs) ─────────────────── */
250
+ /* Single-line trail with character-unit max width — long content
251
+ truncates with `text-overflow: ellipsis` rather than reflowing the
252
+ row or pushing the trailing burger. `display: inline-block` (not
253
+ flex) so the inline children can be ellipsed by text-overflow,
254
+ which only applies to inline-level content. Hidden by default;
255
+ the @container block at the bottom flips it on at mobile widths. */
256
+ .velu-header__crumbs {
257
+ --velu-crumb-max: 24ch;
258
+ display: none;
259
+ align-items: center;
260
+ flex-wrap: nowrap;
261
+ max-inline-size: var(--velu-crumb-max);
262
+ min-inline-size: 0;
263
+ overflow: hidden;
264
+ /* Native-button reset — the strip is now a <button> so we need to
265
+ null out the browser's default chrome (background, border,
266
+ focus padding) and inherit the font from the header. */
267
+ padding: 0;
268
+ background: transparent;
269
+ border: 0;
270
+ font: inherit;
271
+ color: var(--text-color);
272
+ font-size: var(--f-h6);
273
+ cursor: pointer;
274
+ }
275
+ /* Each segment is a flex child that can shrink independently — the
276
+ final ("current page") segment is the one that ellipses if the
277
+ trail outgrows the max-width. */
278
+ .velu-header__crumb {
279
+ flex: 0 1 auto;
280
+ min-inline-size: 0;
281
+ overflow: hidden;
282
+ white-space: nowrap;
283
+ text-overflow: ellipsis;
284
+ }
285
+ .velu-header__crumb-link {
286
+ flex: 0 1 auto;
287
+ min-inline-size: 0;
288
+ overflow: hidden;
289
+ white-space: nowrap;
290
+ text-overflow: ellipsis;
291
+ color: inherit;
292
+ text-decoration: none;
293
+ }
294
+ .velu-header__crumb-link:hover {
295
+ color: var(--accent-color);
296
+ }
297
+ /* Chevron separator — never shrinks (flex: none). Sized in `em` so
298
+ it scales with the breadcrumb's font-size (h6 currently); 1em
299
+ matches the text glyph height, keeping the trail visually
300
+ balanced if --f-h6 ever changes. Gap to neighbouring segments
301
+ comes from `margin-inline: var(--s-3)` (~8px). */
302
+ .velu-header__crumb-sep {
303
+ flex: none;
304
+ inline-size: 1em;
305
+ block-size: 1em;
306
+ stroke-width: 1;
307
+ margin-inline: var(--s-3);
308
+ }
309
+
310
+ /* ── Mobile burger nav button ──────────────────────────────────────── */
311
+ /* Hidden by default; the @container query at the bottom flips it to
312
+ `inline-flex` at mobile widths. Defined BEFORE the @container so
313
+ that block's `display: inline-flex` overrides this `display: none`
314
+ via source-order cascade (same specificity). Sized 1.5rem (24px)
315
+ to match the top-row icon glyphs — no inner padding, the button
316
+ IS the icon. */
317
+ /* Trailing slot at the right end of the tabs row (e.g. language
318
+ switcher). Pushed right by auto margin; hidden on mobile (above). */
319
+ .velu-header__tabs-trailing {
320
+ margin-inline-start: auto;
321
+ }
322
+
323
+ .velu-header__menu {
324
+ display: none;
325
+ align-items: center;
326
+ justify-content: center;
327
+ inline-size: 1.5rem;
328
+ block-size: 1.5rem;
329
+ padding: 0;
330
+ background: transparent;
331
+ border: 0;
332
+ border-radius: var(--radius-sm);
333
+ color: var(--muted-color);
334
+ cursor: pointer;
335
+ transition: color 0.12s ease, background 0.12s ease;
336
+ }
337
+ .velu-header__menu:hover {
338
+ color: var(--text-color);
339
+ background: var(--surface-color);
340
+ }
341
+
342
+ /* ── Narrow-width swap ─────────────────────────────────────────────── */
343
+ /* Same threshold as docs-layout / toc-bar — at < 1024px the inline
344
+ action buttons collapse into the kebab menu. The header lives inside
345
+ `.velu-docs-layout` (which is the `docs` container), so this query
346
+ reacts to the layout's width, not the viewport. */
347
+ @container docs (max-width: 1024px) {
348
+ .velu-header__actions-list {
349
+ display: none;
350
+ }
351
+ .velu-header__kebab {
352
+ display: inline-block;
353
+ }
354
+ /* The big desktop inline inset is desktop-only; dial it back at
355
+ tablet (mobile reduces it further below). */
356
+ .velu-header {
357
+ padding-inline: var(--s2);
358
+ }
359
+ }
360
+
361
+ /* ── Mobile collapse (< 640px) ─────────────────────────────────────── */
362
+ /* At mobile widths the header has to fit brand + search-icon + ask-ai
363
+ + kebab on one row at 375px. Hide the labels on action buttons (the
364
+ icon span keeps its class, so the unnamed text span is the second
365
+ child) and tighten the column gap. The trailing slot (theme toggle)
366
+ is hidden — it can be re-surfaced inside the kebab dropdown later. */
367
+ @container docs (max-width: 640px) {
368
+ /* Language switcher (tabs-row trailing slot) folds into the drawer
369
+ on mobile. */
370
+ .velu-header__tabs-trailing {
371
+ display: none;
372
+ }
373
+ /* Drop the visible "Ask AI" / "Book Demo" label; icon survives. */
374
+ .velu-header__action > span:not(.velu-header__action-icon) {
375
+ display: none;
376
+ }
377
+ /* Compact padding when only the icon remains. */
378
+ .velu-header__action {
379
+ padding: var(--s-3);
380
+ }
381
+ /* Theme toggle hidden at mobile — too crowded; users can rely on
382
+ OS-level theme until we surface it inside the kebab. */
383
+ .velu-header__col--actions .velu-theme-toggle {
384
+ display: none;
385
+ }
386
+ /* 16px gap between the top-right icons (Search ↔ AskAI ↔ Kebab) —
387
+ applied at three levels because the icons live in two separate
388
+ Cluster components whose `space` prop sets an inline `gap`, so the
389
+ CSS needs `!important` to win over the inline style. --s0 is 1rem
390
+ which evaluates to 16px at the default root font-size. */
391
+ .velu-header__top {
392
+ gap: var(--s0) !important;
393
+ }
394
+ .velu-header__col--center,
395
+ .velu-header__col--actions {
396
+ gap: var(--s0) !important;
397
+ }
398
+ /* Reduce the header's outer horizontal padding so brand + actions
399
+ get more breathing room at 375px. */
400
+ .velu-header {
401
+ padding-inline: var(--s-1);
402
+ }
403
+ /* Shrink the wordmark slightly — 2rem competes with the icon row
404
+ for horizontal space; 1.5rem still reads as the brand. */
405
+ .velu-header__wordmark {
406
+ font-size: 1.5rem;
407
+ }
408
+ /* Flatten the 3-col grid into a flex row so the center cluster
409
+ (Search + Ask AI) sits flush with the actions cluster (Kebab) on
410
+ the right side. `margin-inline-start: auto` on center pushes both
411
+ clusters to the trailing edge, brand stays at the leading edge. */
412
+ .velu-header__top {
413
+ display: flex;
414
+ align-items: center;
415
+ }
416
+ .velu-header__col--center {
417
+ justify-self: auto;
418
+ margin-inline-start: auto;
419
+ }
420
+ .velu-header__col--actions {
421
+ justify-self: auto;
422
+ }
423
+ /* Borderless / transparent icon buttons in the right cluster — at
424
+ mobile they read as a row of icon chips alongside the kebab, not
425
+ pill-shaped buttons. */
426
+ .velu-header__col--center .velu-search__trigger,
427
+ .velu-header__col--center .velu-header__action--outlined,
428
+ .velu-header__col--center .velu-header__action--primary {
429
+ background: transparent;
430
+ border: 0;
431
+ }
432
+ /* Uniform 1.5rem glyphs across all icon buttons in the right cluster
433
+ (Search, Ask AI, kebab, burger) so they read as a single coherent
434
+ row. Targets the SVG directly — lucide icons render as <svg>.
435
+ `stroke-width: 1` thins the lucide default (2) so the icons read
436
+ as light line glyphs rather than chunky outlines. */
437
+ .velu-header__col--center .velu-search__trigger-icon svg,
438
+ .velu-header__col--center .velu-header__action-icon svg,
439
+ .velu-header__kebab-toggle svg,
440
+ .velu-header__menu svg {
441
+ inline-size: 1.5rem;
442
+ block-size: 1.5rem;
443
+ stroke-width: 1;
444
+ }
445
+ /* Use the body text color (not muted) for the icon buttons at
446
+ mobile — without the surrounding label/border to give them
447
+ weight, the muted-grey tint reads as "disabled". */
448
+ .velu-header__col--center .velu-search__trigger,
449
+ .velu-header__col--center .velu-search__trigger-icon,
450
+ .velu-header__col--center .velu-header__action,
451
+ .velu-header__col--center .velu-header__action-icon,
452
+ .velu-header__kebab-toggle,
453
+ .velu-header__menu {
454
+ color: var(--text-color);
455
+ }
456
+ /* Tabs row swap: hide the individual tabs (they collapse into the
457
+ mobile menu later), reveal the breadcrumb in their place. The
458
+ `.velu-header__tabs > .velu-header__tab` selector raises the
459
+ specificity above the base `.velu-header__tab { display: inline-flex }`
460
+ rule that appears later in this file, so the cascade resolves in
461
+ this block's favor without re-ordering the whole file. */
462
+ .velu-header__tabs > .velu-header__tab {
463
+ display: none;
464
+ }
465
+ .velu-header__crumbs {
466
+ display: flex;
467
+ }
468
+ /* Burger sits at the trailing edge of the tabs row — pushes itself
469
+ to the right via auto margin so the tabs cluster left as usual.
470
+ Hidden at wider widths via the default `display: none` below. */
471
+ .velu-header__menu {
472
+ display: inline-flex;
473
+ margin-inline-start: auto;
474
+ }
475
+ .velu-header__menu svg {
476
+ inline-size: 1.5rem;
477
+ block-size: 1.5rem;
478
+ }
479
+ }
480
+
481
+
482
+ /* ── Tabs row ───────────────────────────────────────────────────────── */
483
+ /* Tab is a horizontal link row with an optional icon, a label, and an
484
+ optional external-link chip. Active tab has accent color + an
485
+ underline rail. */
486
+ .velu-header__tab {
487
+ display: inline-flex;
488
+ align-items: center;
489
+ gap: var(--s-3);
490
+ padding-block: var(--s-3);
491
+ padding-inline: var(--s-3);
492
+ border-block-end: 2px solid transparent;
493
+ color: var(--text-color);
494
+ font-size: var(--f-h6);
495
+ font-weight: var(--weight-medium);
496
+ text-decoration: none;
497
+ transition: color 0.12s ease, border-color 0.12s ease;
498
+ }
499
+ .velu-header__tab:hover {
500
+ color: var(--accent-color);
501
+ }
502
+ .velu-header__tab--active {
503
+ color: var(--accent-color);
504
+ border-block-end-color: var(--accent-color);
505
+ }
506
+ .velu-header__tab-icon,
507
+ .velu-header__tab-external {
508
+ display: inline-flex;
509
+ flex: none;
510
+ color: var(--muted-color);
511
+ }
512
+ /* On hover / active, the icon follows the tab's accent color. */
513
+ .velu-header__tab:hover .velu-header__tab-icon,
514
+ .velu-header__tab--active .velu-header__tab-icon {
515
+ color: inherit;
516
+ }
517
+ .velu-header__tab-external {
518
+ font-size: var(--f-h7);
519
+ opacity: 0.7;
520
+ }
@@ -0,0 +1,50 @@
1
+ /* PageNav — previous/next page cards. The two-column / stacked layout
2
+ is the <Switcher> primitive; this file owns only the card chrome.
3
+ All values are tokens; light/dark via [data-theme]. */
4
+
5
+ .velu-pagenav__card {
6
+ display: flex;
7
+ flex-direction: column;
8
+ gap: var(--s-3);
9
+ min-inline-size: 0;
10
+ padding: var(--s0);
11
+ background: var(--page-bg);
12
+ border: var(--border-width) solid var(--border-color);
13
+ border-radius: var(--radius-md);
14
+ color: var(--text-color);
15
+ text-decoration: none;
16
+ transition: border-color 0.12s ease;
17
+ }
18
+ .velu-pagenav__card:hover {
19
+ border-color: var(--accent-color);
20
+ }
21
+
22
+ /* Previous hugs the inline start, Next the inline end. */
23
+ .velu-pagenav__card--prev {
24
+ align-items: flex-start;
25
+ text-align: start;
26
+ }
27
+ .velu-pagenav__card--next {
28
+ align-items: flex-end;
29
+ text-align: end;
30
+ }
31
+
32
+ .velu-pagenav__title {
33
+ font-size: var(--f-h5);
34
+ line-height: var(--lh-h5);
35
+ font-weight: var(--weight-medium);
36
+ color: var(--text-color);
37
+ }
38
+
39
+ /* "‹ Previous" / "Next ›" — muted, turns accent on card hover. */
40
+ .velu-pagenav__dir {
41
+ font-size: var(--f-h6);
42
+ color: var(--muted-color);
43
+ transition: color 0.12s ease;
44
+ }
45
+ .velu-pagenav__dir svg {
46
+ flex: none;
47
+ }
48
+ .velu-pagenav__card:hover .velu-pagenav__dir {
49
+ color: var(--accent-color);
50
+ }
@@ -0,0 +1,66 @@
1
+ /* PoweredBy — "Powered by Velu" badge, right-aligned. Sits above the
2
+ site footer. All values are tokens; light/dark via [data-theme]. */
3
+
4
+ .velu-powered-by {
5
+ display: flex;
6
+ justify-content: flex-end;
7
+ /* 32px gap above (to the previous element — usually the AskBar) and
8
+ 32px gap below (to the site footer). Margins, not padding, so the
9
+ gap is OUTSIDE the badge — i.e. it's the actual whitespace the
10
+ reader sees rather than a tap-target buffer. */
11
+ margin-block-start: 2rem;
12
+ margin-block-end: 2rem;
13
+ padding-block: 0;
14
+ padding-inline: var(--s3);
15
+ }
16
+
17
+ .velu-powered-by__link {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ /* Gap here is only between the "Powered by" label and the brand
21
+ sub-cluster — the mark↔wordmark spacing is set tighter inside
22
+ .velu-powered-by__brand. */
23
+ gap: var(--s-3);
24
+ text-decoration: none;
25
+ color: var(--text-color);
26
+ font-size: var(--f-h6);
27
+ transition: opacity 0.12s ease;
28
+ }
29
+ .velu-powered-by__link:hover {
30
+ opacity: 0.78;
31
+ }
32
+
33
+ .velu-powered-by__label {
34
+ color: var(--muted-color);
35
+ font-weight: var(--weight-normal);
36
+ }
37
+
38
+ /* Mark + wordmark form one visual unit ("Velu" the brand) — tighter
39
+ gap than the label↔brand spacing above. */
40
+ .velu-powered-by__brand {
41
+ display: inline-flex;
42
+ align-items: center;
43
+ gap: var(--s-5);
44
+ }
45
+
46
+ /* Mark: same 32×24 ratio as the brand mark, sized one modular step up
47
+ from body text so it reads at the same scale as the wordmark beside
48
+ it. Muted (not accent) here so the badge sits quietly as an
49
+ attribution rather than a brand statement. */
50
+ .velu-powered-by__mark {
51
+ flex: none;
52
+ inline-size: calc(var(--icon-size-lg) * (32 / 24));
53
+ block-size: var(--icon-size-lg);
54
+ color: var(--muted-color);
55
+ }
56
+
57
+ .velu-powered-by__wordmark {
58
+ font-family: var(--font-brand);
59
+ font-weight: var(--weight-light);
60
+ font-size: var(--f-h4);
61
+ line-height: 1;
62
+ letter-spacing: -0.01em;
63
+ /* Muted to match the mark — the whole badge is a single tonal
64
+ attribution. */
65
+ color: var(--muted-color);
66
+ }