@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.
- package/dist/cli.js +11 -0
- package/package.json +52 -0
- package/runtime/velu-ui/base.css +311 -0
- package/runtime/velu-ui/components/Accordion.jsx +64 -0
- package/runtime/velu-ui/components/ApiClient.jsx +121 -0
- package/runtime/velu-ui/components/ApiField.jsx +87 -0
- package/runtime/velu-ui/components/ApiPath.jsx +63 -0
- package/runtime/velu-ui/components/ApiSidebar.jsx +122 -0
- package/runtime/velu-ui/components/AskBar.jsx +71 -0
- package/runtime/velu-ui/components/Callout.jsx +114 -0
- package/runtime/velu-ui/components/Card.jsx +131 -0
- package/runtime/velu-ui/components/Chatbot.jsx +596 -0
- package/runtime/velu-ui/components/CodeBlock.jsx +375 -0
- package/runtime/velu-ui/components/Columns.jsx +56 -0
- package/runtime/velu-ui/components/Field.jsx +81 -0
- package/runtime/velu-ui/components/Image.jsx +163 -0
- package/runtime/velu-ui/components/MethodBadge.jsx +31 -0
- package/runtime/velu-ui/components/NavSelect.jsx +108 -0
- package/runtime/velu-ui/components/PageFeedback.jsx +219 -0
- package/runtime/velu-ui/components/PageFooter.jsx +213 -0
- package/runtime/velu-ui/components/PageHeader.jsx +414 -0
- package/runtime/velu-ui/components/PageNav.jsx +77 -0
- package/runtime/velu-ui/components/PoweredBy.jsx +51 -0
- package/runtime/velu-ui/components/Prompt.jsx +115 -0
- package/runtime/velu-ui/components/Search.jsx +366 -0
- package/runtime/velu-ui/components/Sidebar.jsx +191 -0
- package/runtime/velu-ui/components/Steps.jsx +65 -0
- package/runtime/velu-ui/components/ThemeToggle.jsx +48 -0
- package/runtime/velu-ui/components/Toc.jsx +537 -0
- package/runtime/velu-ui/components/TocBar.jsx +195 -0
- package/runtime/velu-ui/components/Tree.jsx +87 -0
- package/runtime/velu-ui/components/TryItBar.jsx +90 -0
- package/runtime/velu-ui/components/accordion.css +92 -0
- package/runtime/velu-ui/components/api.css +479 -0
- package/runtime/velu-ui/components/ask-bar.css +94 -0
- package/runtime/velu-ui/components/card.css +105 -0
- package/runtime/velu-ui/components/chatbot.css +617 -0
- package/runtime/velu-ui/components/code-block.css +263 -0
- package/runtime/velu-ui/components/docs-layout.css +775 -0
- package/runtime/velu-ui/components/field.css +82 -0
- package/runtime/velu-ui/components/image.css +237 -0
- package/runtime/velu-ui/components/nav-select.css +157 -0
- package/runtime/velu-ui/components/page-feedback.css +241 -0
- package/runtime/velu-ui/components/page-footer.css +130 -0
- package/runtime/velu-ui/components/page-header.css +520 -0
- package/runtime/velu-ui/components/page-nav.css +50 -0
- package/runtime/velu-ui/components/powered-by.css +66 -0
- package/runtime/velu-ui/components/prompt.css +99 -0
- package/runtime/velu-ui/components/search.css +307 -0
- package/runtime/velu-ui/components/sidebar.css +144 -0
- package/runtime/velu-ui/components/steps.css +77 -0
- package/runtime/velu-ui/components/theme-toggle.css +70 -0
- package/runtime/velu-ui/components/toc-bar.css +234 -0
- package/runtime/velu-ui/components/tree.css +49 -0
- package/runtime/velu-ui/index.js +45 -0
- package/runtime/velu-ui/lib/copyText.js +64 -0
- package/runtime/velu-ui/lib/lang-icons.jsx +156 -0
- package/runtime/velu-ui/lib/prism-langs.js +957 -0
- package/runtime/velu-ui/lib/prism-loader.js +74 -0
- package/runtime/velu-ui/lib/resolveIcon.jsx +29 -0
- package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -0
- package/runtime/velu-ui/mdx-components.jsx +85 -0
- package/runtime/velu-ui/primitives/Cluster.jsx +49 -0
- package/runtime/velu-ui/primitives/Stack.jsx +63 -0
- package/runtime/velu-ui/primitives/Switcher.jsx +57 -0
- package/runtime/velu-ui/primitives/stack.css +3 -0
- package/runtime/velu-ui/primitives/switcher.css +25 -0
- package/runtime/velu-ui/styles.css +43 -0
- package/runtime/velu-ui/tokens.css +4 -0
- package/schema/velu.schema.json +167 -0
- package/src/navigation.js +434 -0
- package/src/runtime/App.jsx +1473 -0
- package/src/runtime/client-entry.jsx +22 -0
- package/src/runtime/server-entry.jsx +16 -0
- package/src/template.html +48 -0
- package/templates/starter/ai-tools/claude-code.mdx +26 -0
- package/templates/starter/ai-tools/cursor.mdx +17 -0
- package/templates/starter/api-reference/endpoint/create.mdx +24 -0
- package/templates/starter/api-reference/endpoint/get.mdx +27 -0
- package/templates/starter/api-reference/introduction.mdx +28 -0
- package/templates/starter/development.mdx +19 -0
- package/templates/starter/essentials/code.mdx +28 -0
- package/templates/starter/essentials/images.mdx +29 -0
- package/templates/starter/essentials/markdown.mdx +25 -0
- package/templates/starter/essentials/navigation.mdx +39 -0
- package/templates/starter/essentials/settings.mdx +30 -0
- package/templates/starter/favicon.svg +6 -0
- package/templates/starter/index.mdx +31 -0
- package/templates/starter/quickstart.mdx +31 -0
- 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
|
+
}
|