@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,775 @@
|
|
|
1
|
+
/* DocsLayout — the chrome of a Velu docs page (header, fixed left
|
|
2
|
+
sidebar, center column, fixed right TOC, footer). The interesting
|
|
3
|
+
bit lives in the @container rule at the bottom: at narrow widths
|
|
4
|
+
the right TOC is hidden and the center column expands, with no
|
|
5
|
+
media queries. Container query → reacts to the layout wrapper's
|
|
6
|
+
inline-size, not the viewport, so the same component works in any
|
|
7
|
+
surrounding shell (modal, split view, etc).
|
|
8
|
+
|
|
9
|
+
All values are tokens; light/dark via [data-theme]. */
|
|
10
|
+
|
|
11
|
+
.velu-docs-layout {
|
|
12
|
+
/* `inline-size` containment lets @container queries below react to
|
|
13
|
+
this element's width. `docs` is the named container so component
|
|
14
|
+
CSS elsewhere can target it specifically. */
|
|
15
|
+
container-type: inline-size;
|
|
16
|
+
container-name: docs;
|
|
17
|
+
|
|
18
|
+
/* Rail width — exposed as a custom property so the actual rail
|
|
19
|
+
element AND the centre-column margin that tracks it can reference
|
|
20
|
+
a single source of truth. Set OUTSIDE the @container block so the
|
|
21
|
+
value is unconditionally inherited by descendants (defining it
|
|
22
|
+
inside the container query was leaving it un-set for descendants
|
|
23
|
+
in some cascade edge cases, so the rail collapsed to 0 width). */
|
|
24
|
+
--velu-rail-width: 3.5rem; /* 56px */
|
|
25
|
+
/* Left sidebar column width — single source of truth so the aside
|
|
26
|
+
and the centre-column margin that reserves space for it stay in
|
|
27
|
+
sync. Wide enough that nav labels don't wrap given the left inset
|
|
28
|
+
gutter on the aside. */
|
|
29
|
+
--velu-sidebar-width: 18rem; /* 288px */
|
|
30
|
+
/* Right column reserve — the TOC aside width AND the centre's right
|
|
31
|
+
margin. Constant whether the TOC or the chatbot is shown, so the
|
|
32
|
+
article never shifts when the chatbot opens. The chatbot is wider
|
|
33
|
+
than this and simply slides OVER the right edge as a floating
|
|
34
|
+
panel (its own --vchat-width), rather than reflowing the article. */
|
|
35
|
+
--velu-aside-right-width: 17.5rem; /* 280px */
|
|
36
|
+
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
min-height: 100vh;
|
|
40
|
+
background: var(--page-bg);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ── Asides (fixed left sidebar + right TOC) ───────────────────────── */
|
|
44
|
+
.velu-docs-layout__aside {
|
|
45
|
+
position: fixed;
|
|
46
|
+
/* Span full viewport height so the aside extends behind the
|
|
47
|
+
translucent header (its top shows through the header's frost on
|
|
48
|
+
scroll). Inner padding-top pushes the nav content down below the
|
|
49
|
+
header band; scroll-padding-top keeps auto-scroll out of it. */
|
|
50
|
+
inset-block-start: 0;
|
|
51
|
+
inset-block-end: 0;
|
|
52
|
+
overflow-y: auto;
|
|
53
|
+
/* Horizontal clip: the left aside animates its `inline-size` down
|
|
54
|
+
to a thin rail when collapsed, and its child nav (which keeps
|
|
55
|
+
its full intrinsic width) MUST be visually cropped to the new
|
|
56
|
+
box. `overflow-y: auto` + `overflow-x: clip` is allowed (clip
|
|
57
|
+
is the spec partner that works with a scrollable y-axis) and
|
|
58
|
+
gives reliable visual clipping that pure `overflow: hidden`
|
|
59
|
+
can interact badly with on a `position: fixed` element. */
|
|
60
|
+
overflow-x: clip;
|
|
61
|
+
overscroll-behavior: contain;
|
|
62
|
+
padding-block-start: calc(var(--velu-header-height) + var(--s3));
|
|
63
|
+
scroll-padding-block-start: var(--velu-header-height);
|
|
64
|
+
z-index: 10;
|
|
65
|
+
/* `inline-size` is animated for the narrow-width collapse
|
|
66
|
+
(240px ↔ rail). `transform` is animated for the mobile drawer
|
|
67
|
+
(translateX(-100%) ↔ 0). Both timings live on the same property
|
|
68
|
+
list so each animation runs only when its property actually
|
|
69
|
+
changes at the active breakpoint. */
|
|
70
|
+
transition: inline-size 0.2s ease, transform 0.25s ease;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.velu-docs-layout__aside--left {
|
|
74
|
+
inset-inline-start: 0;
|
|
75
|
+
inline-size: var(--velu-sidebar-width);
|
|
76
|
+
/* Left gutter so the nav sits inset from the viewport edge rather
|
|
77
|
+
than hugging it. (At mobile the @container block resets this to 0
|
|
78
|
+
— the drawer manages its own padding.) */
|
|
79
|
+
padding-inline-start: var(--s5);
|
|
80
|
+
padding-inline-end: 0;
|
|
81
|
+
/* The aside itself does NOT scroll — it's a flex column whose context
|
|
82
|
+
zone (anchors/switchers) stays pinned and whose nav region
|
|
83
|
+
(.velu-docs-nav-scroll) scrolls on its own. Overrides the base
|
|
84
|
+
aside's `overflow-y: auto`. */
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
/* Bottom boundary: end the column a fixed gap above the viewport
|
|
89
|
+
bottom (mirrors the pinned top), so the nav scrolls within a
|
|
90
|
+
contained panel rather than running to the screen edge. The value
|
|
91
|
+
is driven by --velu-aside-bottom (set inline in App.jsx) so the
|
|
92
|
+
edge lifts to stay above the footer as it scrolls in; falls back
|
|
93
|
+
to a static gap. The mobile drawer resets this to 0. */
|
|
94
|
+
inset-block-end: var(--velu-aside-bottom, var(--s4));
|
|
95
|
+
}
|
|
96
|
+
/* Stack wrapper fills the aside so the inner nav region can flex-grow
|
|
97
|
+
and own the scroll. */
|
|
98
|
+
.velu-docs-aside-stack {
|
|
99
|
+
flex: 1 1 auto;
|
|
100
|
+
min-block-size: 0;
|
|
101
|
+
}
|
|
102
|
+
/* Region wrapping the scroll area + the overlay arrows. Fills the
|
|
103
|
+
remaining aside height; relative so the arrows position to its
|
|
104
|
+
top/bottom edges. */
|
|
105
|
+
.velu-docs-nav-region {
|
|
106
|
+
position: relative;
|
|
107
|
+
flex: 1 1 auto;
|
|
108
|
+
min-block-size: 0;
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
}
|
|
112
|
+
.velu-docs-nav-scroll {
|
|
113
|
+
flex: 1 1 auto;
|
|
114
|
+
min-block-size: 0;
|
|
115
|
+
overflow-y: auto;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Up / down scroll arrows — circular buttons centered on the top and
|
|
119
|
+
bottom edges of the scroll region. Hidden by default; revealed (via
|
|
120
|
+
the sibling combinator off the scroll div's data-fade-* attrs) only
|
|
121
|
+
when there's content beyond that edge. Clicking smooth-scrolls the
|
|
122
|
+
nav fully to that end (handlers in App.jsx). */
|
|
123
|
+
.velu-docs-nav-arrow {
|
|
124
|
+
position: absolute;
|
|
125
|
+
inset-inline-start: 50%;
|
|
126
|
+
display: inline-flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
justify-content: center;
|
|
129
|
+
inline-size: 2rem;
|
|
130
|
+
block-size: 2rem;
|
|
131
|
+
padding: 0;
|
|
132
|
+
border: var(--border-width) solid var(--border-color);
|
|
133
|
+
border-radius: 999px;
|
|
134
|
+
background: var(--page-bg);
|
|
135
|
+
color: var(--muted-color);
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
z-index: 3;
|
|
138
|
+
opacity: 0;
|
|
139
|
+
pointer-events: none;
|
|
140
|
+
transform: translateX(-50%) scale(0.9);
|
|
141
|
+
transition: opacity 0.15s ease, transform 0.15s ease, color 0.12s ease,
|
|
142
|
+
border-color 0.12s ease;
|
|
143
|
+
box-shadow: 0 var(--s-4) var(--s-1) color-mix(in srgb, #000 10%, transparent);
|
|
144
|
+
}
|
|
145
|
+
.velu-docs-nav-arrow:hover {
|
|
146
|
+
color: var(--accent-color);
|
|
147
|
+
border-color: var(--accent-color);
|
|
148
|
+
}
|
|
149
|
+
.velu-docs-nav-arrow svg {
|
|
150
|
+
inline-size: 1.1em;
|
|
151
|
+
block-size: 1.1em;
|
|
152
|
+
}
|
|
153
|
+
.velu-docs-nav-arrow--up {
|
|
154
|
+
inset-block-start: var(--s-3);
|
|
155
|
+
}
|
|
156
|
+
.velu-docs-nav-arrow--down {
|
|
157
|
+
inset-block-end: var(--s-3);
|
|
158
|
+
}
|
|
159
|
+
/* Reveal only while hovering the sidebar AND there's content beyond
|
|
160
|
+
that edge. */
|
|
161
|
+
.velu-docs-layout__aside--left:hover
|
|
162
|
+
.velu-docs-nav-scroll[data-fade-top='true']
|
|
163
|
+
~ .velu-docs-nav-arrow--up,
|
|
164
|
+
.velu-docs-layout__aside--left:hover
|
|
165
|
+
.velu-docs-nav-scroll[data-fade-bottom='true']
|
|
166
|
+
~ .velu-docs-nav-arrow--down {
|
|
167
|
+
opacity: 1;
|
|
168
|
+
pointer-events: auto;
|
|
169
|
+
transform: translateX(-50%) scale(1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Edge fade: the scroll regions (left nav + right TOC) fade to
|
|
173
|
+
transparent at whichever edge has more content beyond it. The
|
|
174
|
+
--vf-top / --vf-bottom lengths default to 0 (no fade) and are
|
|
175
|
+
switched on by the data-fade-* attrs that App.jsx sets from scroll
|
|
176
|
+
position — so the top only fades once you've scrolled down, and the
|
|
177
|
+
bottom fade vanishes when you reach the end. */
|
|
178
|
+
.velu-docs-nav-scroll,
|
|
179
|
+
.velu-docs-layout__aside--right {
|
|
180
|
+
--vf-top: 0px;
|
|
181
|
+
--vf-bottom: 0px;
|
|
182
|
+
-webkit-mask-image: linear-gradient(
|
|
183
|
+
to bottom,
|
|
184
|
+
transparent,
|
|
185
|
+
#000 var(--vf-top),
|
|
186
|
+
#000 calc(100% - var(--vf-bottom)),
|
|
187
|
+
transparent
|
|
188
|
+
);
|
|
189
|
+
mask-image: linear-gradient(
|
|
190
|
+
to bottom,
|
|
191
|
+
transparent,
|
|
192
|
+
#000 var(--vf-top),
|
|
193
|
+
#000 calc(100% - var(--vf-bottom)),
|
|
194
|
+
transparent
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
/* Top fade only on the right TOC — the left nav uses sticky section
|
|
198
|
+
headers as its top treatment, so a top fade would just blur the
|
|
199
|
+
pinned heading. */
|
|
200
|
+
.velu-docs-layout__aside--right[data-fade-top='true'] {
|
|
201
|
+
--vf-top: var(--s2);
|
|
202
|
+
}
|
|
203
|
+
/* The left nav's under-heading fade (the ::after below each sticky
|
|
204
|
+
section heading) is shown only while the nav is scrolled up, so a
|
|
205
|
+
section's first item isn't dimmed at rest. */
|
|
206
|
+
.velu-docs-nav-scroll[data-fade-top='true'] .velu-sidebar__section::after {
|
|
207
|
+
opacity: 1;
|
|
208
|
+
}
|
|
209
|
+
.velu-docs-nav-scroll[data-fade-bottom='true'],
|
|
210
|
+
.velu-docs-layout__aside--right[data-fade-bottom='true'] {
|
|
211
|
+
--vf-bottom: var(--s2);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.velu-docs-layout__aside--right {
|
|
215
|
+
inset-inline-end: 0;
|
|
216
|
+
inline-size: var(--velu-aside-right-width);
|
|
217
|
+
padding-inline: var(--s1);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Scrollbars hide at rest and reveal only while the cursor is over the
|
|
221
|
+
column. Applies to the right TOC aside (scrolls directly) and the
|
|
222
|
+
left nav's inner scroll region (.velu-docs-nav-scroll). The gutter
|
|
223
|
+
stays reserved (scrollbar-width: thin from .velu-hide-scrollbar) —
|
|
224
|
+
only the thumb's color toggles — so revealing it never shifts
|
|
225
|
+
content. Overrides .velu-hide-scrollbar's always-on thumb. */
|
|
226
|
+
.velu-docs-layout__aside--right,
|
|
227
|
+
.velu-docs-nav-scroll {
|
|
228
|
+
scrollbar-color: transparent transparent;
|
|
229
|
+
}
|
|
230
|
+
.velu-docs-layout__aside--right:hover,
|
|
231
|
+
.velu-docs-layout__aside--left:hover .velu-docs-nav-scroll {
|
|
232
|
+
scrollbar-color: var(--border-color) transparent;
|
|
233
|
+
}
|
|
234
|
+
.velu-docs-layout__aside--right::-webkit-scrollbar-thumb,
|
|
235
|
+
.velu-docs-nav-scroll::-webkit-scrollbar-thumb {
|
|
236
|
+
background: transparent;
|
|
237
|
+
}
|
|
238
|
+
.velu-docs-layout__aside--right:hover::-webkit-scrollbar-thumb,
|
|
239
|
+
.velu-docs-layout__aside--left:hover .velu-docs-nav-scroll::-webkit-scrollbar-thumb {
|
|
240
|
+
background: var(--border-color);
|
|
241
|
+
}
|
|
242
|
+
.velu-docs-layout__aside--right:hover::-webkit-scrollbar-thumb:hover,
|
|
243
|
+
.velu-docs-layout__aside--left:hover .velu-docs-nav-scroll::-webkit-scrollbar-thumb:hover {
|
|
244
|
+
background: var(--muted-color);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* ── Center column ─────────────────────────────────────────────────── */
|
|
248
|
+
/* Reserves the aside widths via margin so the article never slides
|
|
249
|
+
under them. The end margin collapses to 0 while the chatbot is
|
|
250
|
+
open (the right TOC is hidden then, the chatbot uses that slot).
|
|
251
|
+
Both can be overridden by the container query below. */
|
|
252
|
+
.velu-docs-layout__center {
|
|
253
|
+
position: relative;
|
|
254
|
+
min-inline-size: 0;
|
|
255
|
+
margin-inline-start: var(--velu-sidebar-width);
|
|
256
|
+
/* Right reserve stays constant whether the right TOC or the chatbot
|
|
257
|
+
occupies it — so opening the chatbot does NOT shift the article.
|
|
258
|
+
The chatbot's desktop width matches this reserve (chatbot.css) so
|
|
259
|
+
it never overlaps the content. */
|
|
260
|
+
margin-inline-end: var(--velu-aside-right-width);
|
|
261
|
+
/* Tracks the sidebar's `inline-size` animation at narrow widths so
|
|
262
|
+
the article slides in sync with the collapse, no snap. Same 0.2s
|
|
263
|
+
timing as the aside's own `inline-size` transition. */
|
|
264
|
+
transition: margin-inline-start 0.2s ease;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* <main> inside the centre column owns the article-area padding —
|
|
268
|
+
defined here (not inline) so the narrow + sidebar-closed @container
|
|
269
|
+
rule below can override just the inline-start side. */
|
|
270
|
+
.velu-docs-layout__main {
|
|
271
|
+
padding-block-start: var(--s3);
|
|
272
|
+
padding-inline: var(--s4);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Article wrapper inside <main> — caps the prose at 46rem and centres
|
|
276
|
+
it by default. Container query below switches it to left-aligned
|
|
277
|
+
when the sidebar is closed at narrow widths, so the article hugs
|
|
278
|
+
the freed space rather than centring inside it (which would leave
|
|
279
|
+
the perceived "ghost sidebar" on the left). */
|
|
280
|
+
.velu-docs-layout__article {
|
|
281
|
+
max-inline-size: 46rem;
|
|
282
|
+
margin-inline: auto;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* Inner sidebar content (the <Sidebar> nav root, direct child of the
|
|
286
|
+
left aside) cross-fades during the collapse animation. Hidden when
|
|
287
|
+
the layout's `data-sidebar-open='false'` flag is set — see the
|
|
288
|
+
@container block below for the actual opacity:0 toggle, which is
|
|
289
|
+
scoped to narrow widths only (sidebar can't collapse at wide). */
|
|
290
|
+
.velu-docs-layout__aside--left > * {
|
|
291
|
+
transition: opacity 0.15s ease;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* ── Scrim (mobile-only) ───────────────────────────────────────────── */
|
|
295
|
+
/* Backdrop layer behind the mobile drawer — dims + blurs the article
|
|
296
|
+
underneath and absorbs pointer events so nothing beneath is
|
|
297
|
+
clickable while the drawer is open. Always in the DOM so the
|
|
298
|
+
opacity + visibility transition runs in both directions; toggled
|
|
299
|
+
visible via [data-sidebar-open='true'] in the @container block
|
|
300
|
+
at the bottom. */
|
|
301
|
+
.velu-docs-layout__scrim {
|
|
302
|
+
/* Hidden until the mobile @container block flips display on. */
|
|
303
|
+
display: none;
|
|
304
|
+
position: fixed;
|
|
305
|
+
inset: 0;
|
|
306
|
+
z-index: 34;
|
|
307
|
+
background: color-mix(in srgb, #000 28%, transparent);
|
|
308
|
+
backdrop-filter: blur(4px);
|
|
309
|
+
-webkit-backdrop-filter: blur(4px);
|
|
310
|
+
/* Closed state — invisible + non-interactive. The
|
|
311
|
+
[data-sidebar-open='true'] rule below flips both back on. */
|
|
312
|
+
opacity: 0;
|
|
313
|
+
pointer-events: none;
|
|
314
|
+
visibility: hidden;
|
|
315
|
+
transition:
|
|
316
|
+
opacity 0.25s ease,
|
|
317
|
+
visibility 0s linear 0.25s;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* ── Drawer chrome (mobile-only) ───────────────────────────────────── */
|
|
322
|
+
/* Drawer-only elements (brand bar + docs-set selector) — hidden at
|
|
323
|
+
wide / narrow widths; the @container block at the bottom flips them
|
|
324
|
+
on at mobile. Defined here (above the @container blocks) so source
|
|
325
|
+
order works in the cascade's favor. */
|
|
326
|
+
.velu-docs-layout__drawer-head,
|
|
327
|
+
.velu-docs-layout__drawer-docselect {
|
|
328
|
+
display: none;
|
|
329
|
+
}
|
|
330
|
+
.velu-docs-layout__drawer-brand {
|
|
331
|
+
display: inline-flex;
|
|
332
|
+
align-items: center;
|
|
333
|
+
gap: var(--s-3);
|
|
334
|
+
color: inherit;
|
|
335
|
+
text-decoration: none;
|
|
336
|
+
}
|
|
337
|
+
.velu-docs-layout__drawer-brand .velu-header__mark {
|
|
338
|
+
inline-size: 2em;
|
|
339
|
+
block-size: 1.5em;
|
|
340
|
+
color: var(--accent-color);
|
|
341
|
+
}
|
|
342
|
+
.velu-docs-layout__drawer-close {
|
|
343
|
+
display: inline-flex;
|
|
344
|
+
align-items: center;
|
|
345
|
+
justify-content: center;
|
|
346
|
+
inline-size: 1.5rem;
|
|
347
|
+
block-size: 1.5rem;
|
|
348
|
+
padding: 0;
|
|
349
|
+
background: transparent;
|
|
350
|
+
border: 0;
|
|
351
|
+
color: var(--text-color);
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
}
|
|
354
|
+
.velu-docs-layout__drawer-close svg {
|
|
355
|
+
inline-size: 1.5rem;
|
|
356
|
+
block-size: 1.5rem;
|
|
357
|
+
stroke-width: 1;
|
|
358
|
+
}
|
|
359
|
+
.velu-docs-layout__drawer-docselect {
|
|
360
|
+
position: relative;
|
|
361
|
+
}
|
|
362
|
+
.velu-docs-layout__drawer-docselect-btn {
|
|
363
|
+
display: flex;
|
|
364
|
+
align-items: center;
|
|
365
|
+
justify-content: space-between;
|
|
366
|
+
inline-size: 100%;
|
|
367
|
+
/* 8px top/bottom — matches the modular-scale step --s-3 (~0.5rem). */
|
|
368
|
+
padding-block: var(--s-3);
|
|
369
|
+
padding-inline: var(--s0);
|
|
370
|
+
background: var(--page-bg);
|
|
371
|
+
border: var(--border-width) solid var(--border-color);
|
|
372
|
+
border-radius: var(--radius-sm);
|
|
373
|
+
font: inherit;
|
|
374
|
+
font-size: var(--f-h5);
|
|
375
|
+
color: var(--text-color);
|
|
376
|
+
text-align: start;
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
transition: border-color 0.12s ease;
|
|
379
|
+
}
|
|
380
|
+
.velu-docs-layout__drawer-docselect-btn:hover {
|
|
381
|
+
border-color: var(--accent-color);
|
|
382
|
+
}
|
|
383
|
+
.velu-docs-layout__drawer-docselect-label {
|
|
384
|
+
flex: 1;
|
|
385
|
+
min-inline-size: 0;
|
|
386
|
+
overflow: hidden;
|
|
387
|
+
text-overflow: ellipsis;
|
|
388
|
+
white-space: nowrap;
|
|
389
|
+
}
|
|
390
|
+
.velu-docs-layout__drawer-docselect-chev {
|
|
391
|
+
inline-size: 1em;
|
|
392
|
+
block-size: 1em;
|
|
393
|
+
stroke-width: 1;
|
|
394
|
+
/* Match the label color (not the muted variant the kebab/search
|
|
395
|
+
icons use) — the chevron reads as part of the trigger text, not
|
|
396
|
+
a secondary indicator. */
|
|
397
|
+
color: var(--text-color);
|
|
398
|
+
stroke: currentColor;
|
|
399
|
+
flex: none;
|
|
400
|
+
transition: transform 0.18s ease;
|
|
401
|
+
}
|
|
402
|
+
.velu-docs-layout__drawer-docselect[data-open='true']
|
|
403
|
+
.velu-docs-layout__drawer-docselect-chev {
|
|
404
|
+
transform: rotate(180deg);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/* Menu — anchored below the trigger, full width of the wrapper.
|
|
408
|
+
Always in the DOM so the open/close transition can run in both
|
|
409
|
+
directions; visibility delayed so the closed menu isn't focusable
|
|
410
|
+
or click-able. */
|
|
411
|
+
.velu-docs-layout__drawer-docselect-menu {
|
|
412
|
+
position: absolute;
|
|
413
|
+
inset-block-start: calc(100% + var(--s-3));
|
|
414
|
+
inset-inline-start: 0;
|
|
415
|
+
inset-inline-end: 0;
|
|
416
|
+
margin: 0;
|
|
417
|
+
padding-block: var(--s-2);
|
|
418
|
+
padding-inline: 0;
|
|
419
|
+
list-style: none;
|
|
420
|
+
background: var(--page-bg);
|
|
421
|
+
border: var(--border-width) solid var(--border-color);
|
|
422
|
+
border-radius: var(--radius-sm);
|
|
423
|
+
box-shadow: 0 var(--s-2) var(--s1)
|
|
424
|
+
color-mix(in srgb, #000 12%, transparent);
|
|
425
|
+
opacity: 0;
|
|
426
|
+
visibility: hidden;
|
|
427
|
+
transform: translateY(-0.25rem);
|
|
428
|
+
pointer-events: none;
|
|
429
|
+
transition:
|
|
430
|
+
opacity 0.15s ease,
|
|
431
|
+
transform 0.15s ease,
|
|
432
|
+
visibility 0s linear 0.15s;
|
|
433
|
+
z-index: 1;
|
|
434
|
+
}
|
|
435
|
+
.velu-docs-layout__drawer-docselect[data-open='true']
|
|
436
|
+
.velu-docs-layout__drawer-docselect-menu {
|
|
437
|
+
opacity: 1;
|
|
438
|
+
visibility: visible;
|
|
439
|
+
transform: translateY(0);
|
|
440
|
+
pointer-events: auto;
|
|
441
|
+
transition:
|
|
442
|
+
opacity 0.15s ease,
|
|
443
|
+
transform 0.15s ease,
|
|
444
|
+
visibility 0s;
|
|
445
|
+
}
|
|
446
|
+
.velu-docs-layout__drawer-docselect-item {
|
|
447
|
+
display: block;
|
|
448
|
+
padding-block: var(--s-2);
|
|
449
|
+
padding-inline: var(--s0);
|
|
450
|
+
font-size: var(--f-h5);
|
|
451
|
+
color: var(--text-color);
|
|
452
|
+
text-decoration: none;
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
transition: background 0.12s ease, color 0.12s ease;
|
|
455
|
+
}
|
|
456
|
+
.velu-docs-layout__drawer-docselect-item:hover {
|
|
457
|
+
background: var(--surface-color);
|
|
458
|
+
color: var(--accent-color);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/* ── Sidebar toggle (narrow-width only) ────────────────────────────── */
|
|
462
|
+
/* Small chevron button straddling the sidebar/article boundary.
|
|
463
|
+
`position: fixed` (not absolute) so it stays pinned to the
|
|
464
|
+
viewport while the article scrolls — the chevron is always
|
|
465
|
+
reachable, never buried below the fold. It sits one button-radius
|
|
466
|
+
below the header and is centred on the boundary between the
|
|
467
|
+
sidebar/rail and the article column. That boundary is exposed
|
|
468
|
+
via the `--velu-sidebar-boundary` custom property (defaults to
|
|
469
|
+
240px when the sidebar is open; collapses to the rail width
|
|
470
|
+
when closed — see @container rules below).
|
|
471
|
+
|
|
472
|
+
Hidden by default; revealed only at narrow widths. */
|
|
473
|
+
.velu-docs-layout__sidebar-toggle {
|
|
474
|
+
display: none;
|
|
475
|
+
position: fixed;
|
|
476
|
+
/* Toggle sits well below the TocBar so it doesn't crowd the
|
|
477
|
+
bar's bottom border. --s5 (~49px) clears the TocBar height
|
|
478
|
+
itself; adding --s2 (~25px) gives a clear breathing gap. */
|
|
479
|
+
inset-block-start: calc(var(--velu-header-height) + var(--s5) + var(--s2));
|
|
480
|
+
/* Sidebar's right edge minus half the toggle's own width — centres
|
|
481
|
+
the 2rem button on the divider. Tracks the actual sidebar width. */
|
|
482
|
+
inset-inline-start: calc(var(--velu-sidebar-boundary, var(--velu-sidebar-width)) - 1rem);
|
|
483
|
+
inline-size: 2rem;
|
|
484
|
+
block-size: 2rem;
|
|
485
|
+
align-items: center;
|
|
486
|
+
justify-content: center;
|
|
487
|
+
padding: 0;
|
|
488
|
+
border: var(--border-width) solid var(--border-color);
|
|
489
|
+
border-radius: 999px;
|
|
490
|
+
background: var(--page-bg);
|
|
491
|
+
color: var(--muted-color);
|
|
492
|
+
cursor: pointer;
|
|
493
|
+
transition: border-color 0.12s ease, color 0.12s ease,
|
|
494
|
+
inset-inline-start 0.2s ease;
|
|
495
|
+
z-index: 11;
|
|
496
|
+
}
|
|
497
|
+
.velu-docs-layout__sidebar-toggle:hover {
|
|
498
|
+
border-color: var(--accent-color);
|
|
499
|
+
color: var(--accent-color);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/* ── Narrow-width layout ───────────────────────────────────────────── */
|
|
503
|
+
/* Container query (not media query) so the layout reacts to the
|
|
504
|
+
wrapper's inline-size — works the same inside a modal, split pane,
|
|
505
|
+
or shrunken window. At < 1024px the right TOC drops out and the
|
|
506
|
+
centre column expands to fill its slot; the left sidebar becomes
|
|
507
|
+
toggleable via the sidebar-toggle button (default open, per
|
|
508
|
+
design). Closing pushes the centre column over rather than overlaying. */
|
|
509
|
+
@container docs (max-width: 1024px) {
|
|
510
|
+
.velu-docs-layout__aside--right {
|
|
511
|
+
display: none;
|
|
512
|
+
}
|
|
513
|
+
.velu-docs-layout__center {
|
|
514
|
+
margin-inline-end: 0;
|
|
515
|
+
}
|
|
516
|
+
.velu-docs-layout__sidebar-toggle {
|
|
517
|
+
display: inline-flex;
|
|
518
|
+
}
|
|
519
|
+
/* Hairline divider on the left sidebar — only at narrow widths,
|
|
520
|
+
where the sidebar visually separates from the article column.
|
|
521
|
+
Same --surface-color recipe as the header/footer rails. The big
|
|
522
|
+
desktop left-inset is dialed back here (tablet); mobile resets it
|
|
523
|
+
to 0 via the drawer block below. */
|
|
524
|
+
.velu-docs-layout__aside--left {
|
|
525
|
+
border-inline-end: var(--border-width) solid var(--surface-color);
|
|
526
|
+
padding-inline-start: var(--s2);
|
|
527
|
+
}
|
|
528
|
+
/* Sidebar closed → shrink the aside's `inline-size` from 240px to
|
|
529
|
+
the rail width. The aside stays in the DOM and keeps its own
|
|
530
|
+
border-inline-end — that border IS the rail's right edge when
|
|
531
|
+
collapsed, no separate rail element needed. `overflow-x: clip`
|
|
532
|
+
on the aside (set above) crops the still-rendered Sidebar nav
|
|
533
|
+
to the narrowed box; the opacity rule below fades the nav out
|
|
534
|
+
so the collapsed state reads as a clean stub.
|
|
535
|
+
The aside's `transition: inline-size 0.2s ease` (set above) is
|
|
536
|
+
what makes the collapse animate; the centre column tracks via
|
|
537
|
+
its own `margin-inline-start` transition. */
|
|
538
|
+
.velu-docs-layout[data-sidebar-open='false']
|
|
539
|
+
.velu-docs-layout__aside--left {
|
|
540
|
+
inline-size: var(--velu-rail-width);
|
|
541
|
+
/* Collapsed state is a thin stub — the (faded) nav is irrelevant,
|
|
542
|
+
so suppress the y-scrollbar that would otherwise appear over
|
|
543
|
+
the rail. `overflow-y: auto` is restored implicitly at wide
|
|
544
|
+
widths / open state by the base `.velu-docs-layout__aside`
|
|
545
|
+
rule above. */
|
|
546
|
+
overflow-y: hidden;
|
|
547
|
+
}
|
|
548
|
+
/* Fade out the Sidebar nav while the aside shrinks — opacity
|
|
549
|
+
transitions in parallel with `inline-size`, so the user sees a
|
|
550
|
+
single coherent collapse (shrink + fade) rather than a snap. */
|
|
551
|
+
.velu-docs-layout[data-sidebar-open='false']
|
|
552
|
+
.velu-docs-layout__aside--left > * {
|
|
553
|
+
opacity: 0;
|
|
554
|
+
pointer-events: none;
|
|
555
|
+
}
|
|
556
|
+
.velu-docs-layout[data-sidebar-open='false']
|
|
557
|
+
.velu-docs-layout__center {
|
|
558
|
+
margin-inline-start: var(--velu-rail-width);
|
|
559
|
+
}
|
|
560
|
+
/* Toggle position tracks the active boundary directly — when the
|
|
561
|
+
sidebar is closed the boundary moves from 240px (sidebar's right
|
|
562
|
+
edge) to var(--velu-rail-width) (rail's right edge), so the
|
|
563
|
+
toggle slides with it. Overriding the property here (instead of
|
|
564
|
+
going through a custom-prop indirection) keeps the transition
|
|
565
|
+
reliable across engines. */
|
|
566
|
+
.velu-docs-layout[data-sidebar-open='false']
|
|
567
|
+
.velu-docs-layout__sidebar-toggle {
|
|
568
|
+
inset-inline-start: calc(var(--velu-rail-width) - 1rem);
|
|
569
|
+
}
|
|
570
|
+
/* At narrow widths the article drops its 46rem cap and fills the
|
|
571
|
+
available main width. Cap survives at wide widths so the 46rem
|
|
572
|
+
prose column lives between the two asides. */
|
|
573
|
+
.velu-docs-layout__article {
|
|
574
|
+
max-inline-size: none;
|
|
575
|
+
margin-inline: 0;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/* ── Mobile-width layout (< 640px) ─────────────────────────────────── */
|
|
580
|
+
/* At mobile widths the centre column reclaims full width regardless
|
|
581
|
+
of sidebar state. The sidebar becomes a RIGHT-edge drawer (anchored
|
|
582
|
+
to the trailing edge, slides in from the right) — opened by the
|
|
583
|
+
header's burger OR by tapping the breadcrumb strip, both of which
|
|
584
|
+
call `onMenuClick` → toggles the layout's `data-drawer-open` flag.
|
|
585
|
+
Note: mobile uses its OWN `data-drawer-open` attribute, separate
|
|
586
|
+
from `data-sidebar-open` (which drives the narrow-width rail
|
|
587
|
+
collapse). Two independent states avoid the SSR/hydration default
|
|
588
|
+
collision that would force the drawer to render open on refresh.
|
|
589
|
+
The floating chevron toggle is removed; it was a wide-layout
|
|
590
|
+
affordance and is redundant at mobile. */
|
|
591
|
+
@container docs (max-width: 640px) {
|
|
592
|
+
.velu-docs-layout__sidebar-toggle {
|
|
593
|
+
display: none;
|
|
594
|
+
}
|
|
595
|
+
.velu-docs-layout__center,
|
|
596
|
+
.velu-docs-layout[data-sidebar-open='false'] .velu-docs-layout__center {
|
|
597
|
+
margin-inline-start: 0;
|
|
598
|
+
}
|
|
599
|
+
.velu-docs-layout__aside--left {
|
|
600
|
+
/* Anchor to the right edge instead of the left — override the
|
|
601
|
+
base `inset-inline-start: 0` from .velu-docs-layout__aside--left
|
|
602
|
+
so the drawer slides in from the trailing edge. Full viewport
|
|
603
|
+
height comes from the base `inset-block: 0 0` on the aside.
|
|
604
|
+
Drop the header-clearance top padding — the drawer has its own
|
|
605
|
+
brand bar. Inline padding stays at 0 — the Sidebar items have
|
|
606
|
+
their own `padding-inline: var(--s0)` (16px), and the drawer
|
|
607
|
+
chrome below gets matching `padding-inline` directly, so the
|
|
608
|
+
inner content lines up 16px from the drawer edge without
|
|
609
|
+
double-padding. */
|
|
610
|
+
inset-inline-start: auto;
|
|
611
|
+
inset-inline-end: 0;
|
|
612
|
+
/* Full-height drawer — no bottom boundary gap (overrides the
|
|
613
|
+
desktop panel inset). */
|
|
614
|
+
inset-block-end: 0;
|
|
615
|
+
inline-size: min(20rem, 85vw);
|
|
616
|
+
padding-block-start: var(--s0);
|
|
617
|
+
padding-inline: 0;
|
|
618
|
+
/* Closed default — fully off the trailing edge. The
|
|
619
|
+
data-sidebar-open='true' rule below brings it back in. */
|
|
620
|
+
transform: translateX(100%);
|
|
621
|
+
/* Above the sticky header (z-index 30 in page-header.css) so the
|
|
622
|
+
drawer covers the header bar too, but below the search palette
|
|
623
|
+
(z-index 60) and chatbot (z-index 50). The aside already spans
|
|
624
|
+
the full viewport via the base `inset-block: 0 0`. */
|
|
625
|
+
z-index: 35;
|
|
626
|
+
/* Solid body background — the aside has no background by default
|
|
627
|
+
(it inherits the layout's page-bg by virtue of being a child),
|
|
628
|
+
but at mobile it's a `position: fixed` drawer that overlays the
|
|
629
|
+
article + header, so transparency would let everything beneath
|
|
630
|
+
bleed through. Explicit --page-bg makes the drawer opaque. */
|
|
631
|
+
background: var(--page-bg);
|
|
632
|
+
}
|
|
633
|
+
/* Shadow only when the drawer is OPEN — when closed the drawer
|
|
634
|
+
sits off-screen right via `translateX(100%)`, and a left-edge
|
|
635
|
+
shadow would bleed leftward into the visible viewport (a ghost
|
|
636
|
+
band along the right edge). Gating to the open state keeps the
|
|
637
|
+
depth cue while the drawer is visible and hides it the rest of
|
|
638
|
+
the time. */
|
|
639
|
+
.velu-docs-layout[data-drawer-open='true']
|
|
640
|
+
.velu-docs-layout__aside--left {
|
|
641
|
+
transform: translateX(0);
|
|
642
|
+
box-shadow:
|
|
643
|
+
calc(-1 * var(--s0)) 0 var(--s2)
|
|
644
|
+
color-mix(in srgb, #000 18%, transparent);
|
|
645
|
+
}
|
|
646
|
+
/* Drop the narrow-width opacity fade / overflow-hidden on the
|
|
647
|
+
drawer's inner content — at mobile the narrow-width rules above
|
|
648
|
+
(data-sidebar-open='false') still apply because sidebarOpen
|
|
649
|
+
defaults to true and may flip via the chevron at narrow widths,
|
|
650
|
+
but at mobile we want the inner nav fully visible and the
|
|
651
|
+
drawer scrollable regardless of those narrow rules. */
|
|
652
|
+
.velu-docs-layout__aside--left > * {
|
|
653
|
+
opacity: 1;
|
|
654
|
+
pointer-events: auto;
|
|
655
|
+
}
|
|
656
|
+
/* The aside stays a non-scrolling flex column on mobile too; the
|
|
657
|
+
nav region (.velu-docs-nav-scroll) owns the scroll, with the
|
|
658
|
+
context zone pinned above it. */
|
|
659
|
+
.velu-docs-layout__aside--left {
|
|
660
|
+
overflow: hidden;
|
|
661
|
+
}
|
|
662
|
+
/* Drawer chrome — brand bar + docs-set selector — visible only at
|
|
663
|
+
mobile (default-hidden above). Both elements get `padding-inline:
|
|
664
|
+
var(--s0)` so their content lines up at the same 16px-from-edge
|
|
665
|
+
position the Sidebar items below them use (which inherit
|
|
666
|
+
`padding-inline: var(--s0)` from sidebar.css). */
|
|
667
|
+
.velu-docs-layout__drawer-head {
|
|
668
|
+
display: flex;
|
|
669
|
+
align-items: center;
|
|
670
|
+
gap: var(--s-1);
|
|
671
|
+
padding-inline: var(--s0);
|
|
672
|
+
}
|
|
673
|
+
.velu-docs-layout__drawer-head .velu-theme-toggle {
|
|
674
|
+
margin-inline-start: auto;
|
|
675
|
+
}
|
|
676
|
+
.velu-docs-layout__drawer-docselect {
|
|
677
|
+
display: block;
|
|
678
|
+
margin-inline: var(--s0);
|
|
679
|
+
/* Inset 16px from each drawer edge; Stack (column flex with
|
|
680
|
+
align-items: stretch) stretches it across the remaining width.
|
|
681
|
+
`align-self: stretch` explicit for engines that don't honor
|
|
682
|
+
the parent's default stretch. */
|
|
683
|
+
align-self: stretch;
|
|
684
|
+
}
|
|
685
|
+
/* Scrim — present at mobile; toggled on by the mobile nav drawer
|
|
686
|
+
here. The chatbot's bottom-sheet variant exists at the wider
|
|
687
|
+
`< 1024px` threshold, so the chatbot-driven scrim is enabled in
|
|
688
|
+
a SECOND @container block below (at the same wider threshold) —
|
|
689
|
+
keeping the drawer-only scrim scoped to mobile and the
|
|
690
|
+
chatbot-driven scrim available to both narrow and mobile. */
|
|
691
|
+
.velu-docs-layout__scrim {
|
|
692
|
+
display: block;
|
|
693
|
+
}
|
|
694
|
+
.velu-docs-layout[data-drawer-open='true'] .velu-docs-layout__scrim {
|
|
695
|
+
opacity: 1;
|
|
696
|
+
pointer-events: auto;
|
|
697
|
+
visibility: visible;
|
|
698
|
+
transition:
|
|
699
|
+
opacity 0.25s ease,
|
|
700
|
+
visibility 0s;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* ── Chatbot scrim (narrow + mobile, < 1024px) ─────────────────────── */
|
|
705
|
+
/* The chatbot becomes a bottom sheet at < 1024px (see chatbot.css);
|
|
706
|
+
the scrim follows the same threshold so the dim+blur layer appears
|
|
707
|
+
over the article whenever the sheet is up. The mobile-only
|
|
708
|
+
`data-drawer-open` trigger lives in the < 640px block above —
|
|
709
|
+
separate because the drawer itself doesn't exist at narrow widths. */
|
|
710
|
+
@container docs (max-width: 1024px) {
|
|
711
|
+
.velu-docs-layout__scrim {
|
|
712
|
+
display: block;
|
|
713
|
+
}
|
|
714
|
+
.velu-docs-layout[data-chat-open='true'] .velu-docs-layout__scrim {
|
|
715
|
+
opacity: 1;
|
|
716
|
+
pointer-events: auto;
|
|
717
|
+
visibility: visible;
|
|
718
|
+
transition:
|
|
719
|
+
opacity 0.25s ease,
|
|
720
|
+
visibility 0s;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/* ── Sidebar context zone + responsive switcher helpers ────────────── */
|
|
725
|
+
/* Product / version / language switchers + anchors above the group
|
|
726
|
+
nav. Inset matches the Sidebar items (which carry padding-inline:
|
|
727
|
+
var(--s0) from sidebar.css). */
|
|
728
|
+
.velu-docs-context {
|
|
729
|
+
flex: none;
|
|
730
|
+
padding-inline: var(--s0);
|
|
731
|
+
}
|
|
732
|
+
.velu-docs-anchors {
|
|
733
|
+
list-style: none;
|
|
734
|
+
margin: 0;
|
|
735
|
+
padding: 0;
|
|
736
|
+
display: flex;
|
|
737
|
+
flex-direction: column;
|
|
738
|
+
gap: var(--s-3);
|
|
739
|
+
}
|
|
740
|
+
.velu-docs-anchors__link {
|
|
741
|
+
display: flex;
|
|
742
|
+
align-items: center;
|
|
743
|
+
gap: var(--s-3);
|
|
744
|
+
color: var(--text-color);
|
|
745
|
+
text-decoration: none;
|
|
746
|
+
font-size: var(--f-h6);
|
|
747
|
+
transition: color 0.12s ease;
|
|
748
|
+
}
|
|
749
|
+
.velu-docs-anchors__link:hover {
|
|
750
|
+
color: var(--accent-color);
|
|
751
|
+
}
|
|
752
|
+
.velu-docs-anchors__icon {
|
|
753
|
+
display: inline-flex;
|
|
754
|
+
flex: none;
|
|
755
|
+
color: var(--muted-color);
|
|
756
|
+
}
|
|
757
|
+
.velu-docs-anchors__link:hover .velu-docs-anchors__icon {
|
|
758
|
+
color: var(--accent-color);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/* Show/hide helpers, scoped to the docs container's mobile breakpoint.
|
|
762
|
+
Version + language switchers live in the header at desktop/tablet
|
|
763
|
+
(hide-on-mobile) and re-appear in the drawer context zone on mobile
|
|
764
|
+
(show-on-mobile). */
|
|
765
|
+
.velu-show-on-mobile {
|
|
766
|
+
display: none;
|
|
767
|
+
}
|
|
768
|
+
@container docs (max-width: 640px) {
|
|
769
|
+
.velu-hide-on-mobile {
|
|
770
|
+
display: none !important;
|
|
771
|
+
}
|
|
772
|
+
.velu-show-on-mobile {
|
|
773
|
+
display: block;
|
|
774
|
+
}
|
|
775
|
+
}
|