@veluai/velu 0.2.39 → 0.2.41

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.
@@ -116,6 +116,7 @@ equivalents. Original references in code examples and prose remain unchanged.
116
116
  Mintlify's `theme: "sequoia"` is converted to `theme: "vepa"` and activates the [Vepa preset](vepa.md).
117
117
  Mintlify's `theme: "mint"` is converted to `theme: "thulir"` and activates the [Thulir preset](thulir.md).
118
118
  Mintlify's `theme: "maple"` is converted to `theme: "aalam"` and activates the [Aalam preset](aalam.md).
119
+ Mintlify's `theme: "aspen"` is converted to `theme: "nila"` and activates the [Nila preset](nila.md).
119
120
  The presets are not a certification of pixel equality for every component or custom script.
120
121
 
121
122
  Features without full Velu equivalents require review: remaining Mintlify themes, advanced
package/docs/nila.md ADDED
@@ -0,0 +1,13 @@
1
+ # Nila theme
2
+
3
+ Nila is Velu's independently implemented Aspen equivalent. Set `"theme": "nila"` in `velu.json`, or run `velu migrate mintlify SOURCE --out DESTINATION` to map Mintlify's `"theme": "aspen"` automatically. The existing `maple` to `aalam` mapping remains supported.
4
+
5
+ The layout uses a compact 96px desktop header, sidebar group separators, a 632px reading column at a 1440px viewport, page icons, and light/dark/system preferences. Nila shares the responsive navigation foundation with Thulir: a left-opening drawer, labeled section picker, focus trapping, Escape dismissal, scroll locking, and an on-page TOC dropdown when the right rail is hidden. Vertical hierarchy lines remain disabled in the drawer and mobile TOC.
6
+
7
+ The reference is the cached npm renderer `@mintlify/client@0.0.3566`, displaying the same source showcase. Theme matching is not a guarantee of identical component behavior: icon artwork, custom CSS selectors, compatibility adapters, generated API examples and interactive playgrounds still require review.
8
+
9
+ For the migrated showcase, set `VELU_TEST_URL=http://localhost:8474` and run `node packages/velu-cli/test/aalam-browser.mjs`. This shared browser regression covers drawer behavior, section selection, content tabs, responsive widths, and TOC heading clearance.
10
+
11
+ Mintlify migration enables `navigation.preservePagePaths` when the source uses product, version, or language axes. These axes select the navigation context while retaining source page URLs, rather than adding another path prefix. Native Velu configurations retain their existing prefix behavior unless they opt into this setting.
12
+
13
+ The navigation stress fixture adds three products (Platform, Analytics, Automation), two versions each, and 32 guides per product/version pair. With that migrated fixture running on port 8474, `node packages/velu-cli/test/nila-navigation-browser.mjs` checks five viewport widths, sidebar scrolling, product/version switching, direct reloads, horizontal overflow, and nested Escape behavior. This fixture has 220 pages including the existing component/API showcase; its additional Guides tab differs from the original four-tab showcase used by the shared browser test above.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veluai/velu",
3
- "version": "0.2.39",
3
+ "version": "0.2.41",
4
4
  "type": "module",
5
5
  "bin": "./dist/cli.js",
6
6
  "publishConfig": {
@@ -20,7 +20,8 @@
20
20
  "docs/mintlify-migration.md",
21
21
  "docs/vepa.md",
22
22
  "docs/thulir.md",
23
- "docs/aalam.md"
23
+ "docs/aalam.md",
24
+ "docs/nila.md"
24
25
  ],
25
26
  "scripts": {
26
27
  "build": "node scripts/build.mjs",
@@ -78,7 +78,7 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
78
78
  className="velu-api-page__tryit"
79
79
  />
80
80
 
81
- {(preset === 'vepa' || ['thulir', 'aalam'].includes(preset)) && <div className="velu-api-page__inline-samples"><ApiSamples idPrefix="inline-" samples={samples} responses={operation.responses} preset={preset} title={operation.title} /></div>}
81
+ {(preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) && <div className="velu-api-page__inline-samples"><ApiSamples idPrefix="inline-" samples={samples} responses={operation.responses} preset={preset} title={operation.title} /></div>}
82
82
  {children && <div className="velu-api-page__custom-content">{children}</div>}
83
83
 
84
84
  {/* Authorizations */}
@@ -114,7 +114,7 @@ export default function ApiReferencePage({ operation, samples = [], apiOperation
114
114
  {operation.body?.fields?.length > 0 && (
115
115
  <api-section class="velu-api-page__section">
116
116
  <h2 data-component="api-section-heading">Body</h2>
117
- {['thulir', 'aalam'].includes(preset) && operation.body.contentType && <span className="velu-api-body-type">{operation.body.contentType}</span>}
117
+ {['thulir', 'aalam', 'nila'].includes(preset) && operation.body.contentType && <span className="velu-api-body-type">{operation.body.contentType}</span>}
118
118
  {operation.body.fields.map((f, i) => (
119
119
  <Field key={i} name={f.name} type={f.type} required={f.required}>
120
120
  {f.description}
@@ -180,10 +180,10 @@ function EnumHint({ values }) {
180
180
  }
181
181
 
182
182
  function ResponseSections({ responses, preset }) {
183
- if (preset === 'vepa' || ['thulir', 'aalam'].includes(preset)) return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
183
+ if (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) return <div className="velu-vepa-responses">{responses.map((r, i) => <section key={i}>
184
184
  <div className="velu-vepa-responses__status">{r.status}{r.contentType && ` · ${r.contentType}`}</div>
185
185
  {r.description && <p>{r.description}</p>}
186
- {(['thulir', 'aalam'].includes(preset) ? [...(r.fields || [])].sort((a, b) => Number(Boolean(b.required)) - Number(Boolean(a.required))) : r.fields)?.map((f, j) => <Field key={f.name || j} name={f.name} type={f.type} required={f.required}>{f.description}</Field>)}
186
+ {(['thulir', 'aalam', 'nila'].includes(preset) ? [...(r.fields || [])].sort((a, b) => Number(Boolean(b.required)) - Number(Boolean(a.required))) : r.fields)?.map((f, j) => <Field key={f.name || j} name={f.name} type={f.type} required={f.required}>{f.description}</Field>)}
187
187
  </section>)}</div>;
188
188
  return (
189
189
  <AccordionGroup className="velu-api-resp">
@@ -14,7 +14,7 @@ export default function ApiSamples({ samples = [], responses = [], preset, title
14
14
  const sample = samples.find((s) => s.key === selected) || samples[0];
15
15
  // Mint displays JSON request bodies across lines. Keep custom/non-JSON
16
16
  // snippets intact, and change whitespace only in a valid JSON body.
17
- const sampleCode = ['thulir', 'aalam'].includes(preset) && sample?.key === 'curl'
17
+ const sampleCode = ['thulir', 'aalam', 'nila'].includes(preset) && sample?.key === 'curl'
18
18
  ? sample.code.replace(/(--data )'([\s\S]*)'$/, (original, prefix, body) => {
19
19
  try { return `${prefix}'\n${JSON.stringify(JSON.parse(body), null, 2)}\n'`; }
20
20
  catch { return original; }
@@ -22,7 +22,7 @@ export default function ApiSamples({ samples = [], responses = [], preset, title
22
22
  const withExample = responses.filter((r) => r.example != null);
23
23
  return (
24
24
  <div className="velu-api-samples">
25
- {sample && (preset === 'vepa' || ['thulir', 'aalam'].includes(preset)) ? <section id={idPrefix + 'request-example'} className="velu-vepa-request">
25
+ {sample && (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) ? <section id={idPrefix + 'request-example'} className="velu-vepa-request">
26
26
  <div className="velu-vepa-request__header"><span>{title}</span><select aria-label="Request example language" value={sample.key} onChange={(e) => setSelected(e.target.value)}>{samples.map((s) => <option key={s.key} value={s.key}>{s.label}</option>)}</select></div>
27
27
  <CodeBlock language={sample.language}>{sampleCode}</CodeBlock>
28
28
  </section> : samples.length > 0 && (
@@ -33,21 +33,6 @@ import Cluster from '../primitives/Cluster.jsx';
33
33
  * demo answer (so dev preview / offline still shows the experience).
34
34
  */
35
35
 
36
- /* ── Brand mark — the Velu double-hook logo ─────────────────────────── */
37
- // `size` is the glyph height; width derives from the 32:24 viewBox.
38
- function VeluMark({ size = 'var(--icon-size-lg)' }) {
39
- return (
40
- <svg
41
- className="velu-chatbot__mark"
42
- viewBox="0 0 32 24"
43
- fill="currentColor"
44
- aria-hidden="true"
45
- style={{ width: `calc(${size} * (32 / 24))`, height: size }}
46
- >
47
- <path d="M 29.656 14.153 C 29.574 14.562 29.351 14.929 29.025 15.193 C 28.699 15.457 28.291 15.601 27.869 15.601 L 21.37 15.601 C 20.534 15.601 19.805 16.163 19.603 16.964 L 18.167 22.637 C 18.068 23.026 17.841 23.372 17.521 23.619 C 17.2 23.866 16.805 24 16.399 24 L 10.438 24 C 10.161 24 9.888 23.938 9.639 23.818 C 9.39 23.698 9.172 23.524 9.001 23.308 C 8.831 23.092 8.712 22.841 8.655 22.574 C 8.598 22.306 8.603 22.029 8.67 21.764 L 10.189 15.764 C 10.287 15.374 10.515 15.029 10.835 14.782 C 11.155 14.535 11.55 14.401 11.956 14.4 L 18.393 14.4 C 19.262 14.4 20.01 13.795 20.18 12.953 L 22.388 2.048 C 22.471 1.639 22.694 1.272 23.019 1.008 C 23.345 0.744 23.754 0.6 24.175 0.6 L 30.177 0.6 C 30.447 0.6 30.713 0.659 30.957 0.773 C 31.201 0.886 31.416 1.052 31.587 1.258 C 31.758 1.464 31.881 1.705 31.946 1.964 C 32.011 2.222 32.017 2.492 31.964 2.753 L 29.656 14.153 Z M 9.611 13.554 C 9.528 13.962 9.305 14.329 8.979 14.593 C 8.653 14.857 8.245 15.001 7.824 15.001 L 1.823 15.001 C 1.553 15.001 1.287 14.942 1.043 14.828 C 0.799 14.714 0.584 14.548 0.413 14.342 C 0.242 14.136 0.119 13.895 0.054 13.637 C -0.011 13.378 -0.017 13.109 0.036 12.848 L 2.344 1.447 C 2.426 1.039 2.649 0.672 2.975 0.408 C 3.301 0.144 3.709 0 4.131 0 L 10.132 0 C 10.402 0 10.668 0.059 10.912 0.173 C 11.155 0.287 11.371 0.453 11.541 0.659 C 11.712 0.865 11.835 1.106 11.9 1.364 C 11.965 1.623 11.972 1.892 11.919 2.153 L 9.611 13.553 L 9.611 13.554 Z" />
48
- </svg>
49
- );
50
- }
51
36
 
52
37
  /* ── "Thinking" loader — 3x3 grid of squares, opacity wave sweeps the
53
38
  anti-diagonal so it ripples top-left → bottom-right. ─────────────── */
@@ -346,7 +331,6 @@ function ChatHeader({ onClose, onNew, onHistory, historyOpen, showClose = true }
346
331
  className="velu-chatbot__header"
347
332
  >
348
333
  <Cluster space="var(--s-2)" align="center">
349
- <VeluMark />
350
334
  <span className="velu-chatbot__brand">
351
335
  <span className="velu-chatbot__brand-name">Velu</span>
352
336
  <span className="velu-chatbot__brand-sep">/</span>
@@ -49,7 +49,7 @@ export default function NavSelect({
49
49
  if (!rootRef.current?.contains(e.target)) setOpen(false);
50
50
  };
51
51
  const onKey = (e) => {
52
- if (e.key === 'Escape') setOpen(false);
52
+ if (e.key === 'Escape') { setOpen(false); rootRef.current?.querySelector('button')?.focus(); }
53
53
  };
54
54
  document.addEventListener('mousedown', onDoc);
55
55
  document.addEventListener('keydown', onKey);
@@ -124,3 +124,4 @@ export default function NavSelect({
124
124
  </div>
125
125
  );
126
126
  }
127
+
@@ -47,26 +47,6 @@ import SocialLinks from './SocialLinks.jsx';
47
47
  * linkComponent?: React.ElementType, className?: string }} props
48
48
  */
49
49
 
50
- /* ── Brand mark — the Velu double-hook logo. Color: currentColor for
51
- the surrounding wordmark; the mark itself uses --accent-color via
52
- the wrapping span. ───────────────────────────────────────────────── */
53
- function VeluMark() {
54
- return (
55
- <svg
56
- className="velu-footer__mark"
57
- viewBox="0 0 32 24"
58
- fill="currentColor"
59
- aria-hidden="true"
60
- >
61
- <path
62
- fillRule="evenodd"
63
- clipRule="evenodd"
64
- d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70537 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.553L9.61081 13.5538Z"
65
- />
66
- </svg>
67
- );
68
- }
69
-
70
50
  export default function PageFooter({
71
51
  preset,
72
52
  brand,
@@ -77,7 +57,7 @@ export default function PageFooter({
77
57
  ...rest
78
58
  }) {
79
59
  const Link = linkComponent;
80
- if (preset === 'vepa' || ['thulir', 'aalam'].includes(preset)) {
60
+ if (preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset)) {
81
61
  return <VepaFooter preset={preset} brand={brand} columns={columns} socials={socials} linkComponent={Link} />;
82
62
  }
83
63
  const brandLabel = brand?.label ?? 'Velu';
@@ -85,7 +65,6 @@ export default function PageFooter({
85
65
 
86
66
  const brandNode = (
87
67
  <Cluster space="var(--s-3)" align="center" className="velu-footer__brand">
88
- <VeluMark />
89
68
  <span className="velu-footer__wordmark">{brandLabel}</span>
90
69
  </Cluster>
91
70
  );
@@ -50,7 +50,9 @@ import Logo from './Logo.jsx';
50
50
 
51
51
  /* Velu brand mark. Color: --accent-color via the wrapping wordmark
52
52
  span; the path uses currentColor. Exported so consumers (e.g. the
53
- mobile nav drawer) can render the mark alongside its own chrome. */
53
+ mobile nav drawer) can render the mark alongside its own chrome.
54
+ Only the default theme shows it — the vepa / thulir / aalam / nila
55
+ presets hide it via CSS (text-only branding). */
54
56
  export function VeluMark() {
55
57
  return (
56
58
  <svg
@@ -62,7 +64,7 @@ export function VeluMark() {
62
64
  <path
63
65
  fillRule="evenodd"
64
66
  clipRule="evenodd"
65
- d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70537 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.5538Z"
67
+ d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70535 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.5538Z"
66
68
  />
67
69
  </svg>
68
70
  );
@@ -292,7 +294,7 @@ export default function PageHeader({
292
294
  return (
293
295
  <Stack
294
296
  as="header"
295
- space={preset === 'vepa' || ['thulir', 'aalam'].includes(preset) ? '0px' : 'var(--s0)'}
297
+ space={preset === 'vepa' || ['thulir', 'aalam', 'nila'].includes(preset) ? '0px' : 'var(--s0)'}
296
298
  data-has-tabs={tabs.length > 0 ? 'true' : 'false'}
297
299
  className={`velu-header${scrolled ? ' velu-header--scrolled' : ''}${
298
300
  showTabsRow ? '' : ' velu-header--no-tabs'
@@ -8,25 +8,6 @@ import React from 'react';
8
8
  * @param {{ href?: string, className?: string }} props
9
9
  */
10
10
 
11
- /* Velu brand mark — same path as PageHeader's mark, sized for the
12
- badge via .velu-powered-by__mark CSS. */
13
- function VeluMark() {
14
- return (
15
- <svg
16
- className="velu-powered-by__mark"
17
- viewBox="0 0 32 24"
18
- fill="currentColor"
19
- aria-hidden="true"
20
- >
21
- <path
22
- fillRule="evenodd"
23
- clipRule="evenodd"
24
- d="M29.6564 14.1534C29.5739 14.5616 29.3507 14.9288 29.0248 15.1926C28.699 15.4565 28.2907 15.6007 27.8694 15.6006H21.3702C20.5341 15.6006 19.8051 16.1627 19.6031 16.9638L18.1671 22.6368C18.0685 23.0263 17.8409 23.372 17.5205 23.619C17.2002 23.866 16.8054 24.0001 16.399 24H10.4378C10.1609 24.0001 9.88759 23.9378 9.63868 23.8179C9.38976 23.6979 9.1718 23.5235 9.00135 23.3079C8.8309 23.0923 8.71245 22.8412 8.65499 22.5736C8.59754 22.306 8.6026 22.029 8.66978 21.7636L10.1888 15.7636C10.2874 15.3743 10.5148 15.0287 10.835 14.7817C11.1551 14.5347 11.5496 14.4005 11.9559 14.4004H18.3931C19.2621 14.4004 20.0101 13.7949 20.1801 12.9532L22.3882 2.04774C22.4705 1.63945 22.6936 1.27198 23.0195 1.00793C23.3453 0.74388 23.7538 0.599578 24.1752 0.599605H30.1774C30.4471 0.599529 30.7134 0.658587 30.9572 0.772515C31.201 0.886444 31.4161 1.0524 31.5871 1.25842C31.758 1.46443 31.8806 1.70537 31.9458 1.96383C32.0111 2.2223 32.0175 2.49185 31.9645 2.75305L29.6564 14.1534ZM9.61081 13.5538C9.52825 13.962 9.30507 14.3292 8.97924 14.593C8.65341 14.857 8.24508 15.0011 7.82375 15.001H1.82257C1.55289 15.0011 1.28656 14.942 1.04279 14.8281C0.79902 14.7141 0.583892 14.5482 0.412928 14.3422C0.241965 14.1362 0.11943 13.8952 0.0541639 13.6368C-0.0111017 13.3783 -0.0174699 13.1087 0.0355186 12.8475L2.34359 1.44715C2.42614 1.03904 2.64933 0.671798 2.97516 0.407944C3.30099 0.144089 3.70932 -7.24082e-05 4.13064 7.25899e-08H10.1318C10.4015 -7.55792e-05 10.6678 0.0589818 10.9116 0.172911C11.1554 0.286839 11.3705 0.453799 11.5415 0.659813C11.7124 0.864828 11.835 1.10576 11.9002 1.36423C11.9655 1.62269 11.9719 1.89225 11.9189 2.15344L9.61081 13.5538Z"
25
- />
26
- </svg>
27
- );
28
- }
29
-
30
11
  export default function PoweredBy({
31
12
  href = 'https://veludocs.com',
32
13
  className = '',
@@ -42,7 +23,6 @@ export default function PoweredBy({
42
23
  >
43
24
  <span className="velu-powered-by__label">Powered by</span>
44
25
  <span className="velu-powered-by__brand">
45
- <VeluMark />
46
26
  <span className="velu-powered-by__wordmark">Velu</span>
47
27
  </span>
48
28
  </a>
@@ -6,7 +6,7 @@ import ThemePreferenceMenu from './ThemePreferenceMenu.jsx';
6
6
  export default function VepaFooter({ preset = 'vepa', brand, columns = [], socials = [], linkComponent: Link = 'a' }) {
7
7
  const single = columns.length === 1;
8
8
  const completeHeadings = columns.every((c) => c.title);
9
- const root = ['thulir', 'aalam'].includes(preset) ? 'thulir-footer' : 'vepa-footer';
9
+ const root = ['thulir', 'aalam', 'nila'].includes(preset) ? 'thulir-footer' : 'vepa-footer';
10
10
  return <footer id="footer" className={root} data-layout={columns.length <= 3 ? 'compact' : 'expanded'} data-single={single} data-headings={completeHeadings}>
11
11
  <div className={`${root}__inner`}>
12
12
  <div className={`${root}__top`}>
@@ -14,7 +14,7 @@ export default function VepaFooter({ preset = 'vepa', brand, columns = [], socia
14
14
  <Link href={brand?.href || '/'} className={`${root}__brand`}>
15
15
  {brand?.logo ? <Logo logo={brand.logo} alt={brand.label} /> : <span>{brand?.label || 'Velu'}</span>}
16
16
  </Link>
17
- {['thulir', 'aalam'].includes(preset) && <SocialLinks socials={socials} className="thulir-footer__brand-mobile-socials" />}
17
+ {['thulir', 'aalam', 'nila'].includes(preset) && <SocialLinks socials={socials} className="thulir-footer__brand-mobile-socials" />}
18
18
  <SocialLinks socials={socials} className={`${root}__brand-socials`} />
19
19
  </div>
20
20
  <div className={`${root}__columns`} style={{ '--footer-columns': columns.length }}>
@@ -427,7 +427,11 @@
427
427
  opacity 0.15s ease,
428
428
  transform 0.15s ease,
429
429
  visibility 0s linear 0.15s;
430
- z-index: 1;
430
+ /* Above the sidebar nav: its items are `z-index: 1` and sticky
431
+ section headers `z-index: 2` (sidebar.css), all in the same
432
+ stacking context as this menu (the drawer aside), so a tie would
433
+ let the nav paint OVER the dropdown and show through it. */
434
+ z-index: 30;
431
435
  }
432
436
  .velu-docs-layout__drawer-docselect[data-open='true']
433
437
  .velu-docs-layout__drawer-docselect-menu {
@@ -835,6 +839,16 @@
835
839
  display: none;
836
840
  }
837
841
 
842
+ /* Center mode (changelogs) hides the aside above; at mobile the aside
843
+ IS the nav drawer, so re-show it there — otherwise changelog pages
844
+ have no way to reach the navigation. Must come after the page-mode
845
+ hide rule above (same specificity, later source order wins). */
846
+ @container docs (max-width: 640px) {
847
+ .velu-docs-layout[data-page-mode='center'] .velu-docs-layout__aside--left {
848
+ display: flex;
849
+ }
850
+ }
851
+
838
852
  /* Custom / frame are a landing canvas: center a max-w-4xl column.
839
853
  Frame keeps the sidebar; custom does not. */
840
854
  .velu-docs-layout[data-page-mode='custom'] .velu-docs-layout__main,
@@ -66,3 +66,4 @@
66
66
  @import './themes/vepa.css' layer(velu);
67
67
  @import './themes/thulir.css' layer(velu);
68
68
  @import './themes/aalam.css' layer(velu);
69
+ @import './themes/nila.css' layer(velu);
@@ -0,0 +1,62 @@
1
+ /* Nila: Aspen's compact header and divided sidebar groups.
2
+ Shared components and accessible responsive navigation live in thulir.css. */
3
+ [data-site-theme='nila'] .velu-header__top { height: 56px; padding-inline: 36px; }
4
+ [data-site-theme='nila'] .velu-header__top::after { display: none; }
5
+ [data-site-theme='nila'] .velu-header__tabs { height: 40px; padding-inline: 36px; }
6
+ [data-site-theme='nila'] .velu-header__tab { height: 40px; }
7
+ [data-site-theme='nila'] #search-bar-entry { border: 0; border-radius: 999px; background: var(--surface-color); }
8
+ [data-site-theme='nila'] .velu-header__action:not(.velu-header__action--primary) { padding: 8px 14px; background: var(--surface-color); border-radius: 14px; }
9
+ [data-site-theme='nila'] .velu-sidebar__indicator { display: none; }
10
+ /* Keep reading content separate from the next/previous navigation. */
11
+ [data-site-theme='nila'] .velu-pagenav { margin-block-start: 48px; }
12
+ [data-site-theme='nila'] .velu-sidebar { gap: 0 !important; }
13
+ [data-site-theme='nila'] .velu-docs-nav-scroll { overflow-y: auto; background-image: none; scrollbar-color: transparent transparent; }
14
+ [data-site-theme='nila'] .velu-docs-nav-scroll { overflow-x: hidden; min-width: 0; }
15
+ [data-site-theme='nila'] .velu-sidebar__label { white-space: normal; overflow-wrap: anywhere; min-width: 0; }
16
+ [data-site-theme='nila'] .velu-sidebar__item { align-items: flex-start; }
17
+ [data-site-theme='nila'] .velu-sidebar__icon { flex: none; }
18
+ [data-site-theme='nila'] .velu-docs-nav-scroll:hover { scrollbar-color: var(--border-color) transparent; }
19
+ [data-site-theme='nila'] .velu-docs-nav-scroll::-webkit-scrollbar-track { background: transparent; }
20
+ [data-site-theme='nila'] .velu-docs-nav-scroll::-webkit-scrollbar-thumb { background: transparent; }
21
+ [data-site-theme='nila'] .velu-docs-nav-scroll:hover::-webkit-scrollbar-thumb { background: var(--border-color); }
22
+ [data-site-theme='nila'] .velu-sidebar__item--active { background: color-mix(in srgb, var(--accent-color) 10%, transparent); }
23
+ [data-site-theme='nila'] .velu-sidebar__section { font-size: 12px; font-weight: 600; line-height: 20px; }
24
+ [data-site-theme='nila'] .velu-sidebar > sidebar-group { margin-bottom: 16px; padding-bottom: 16px; }
25
+ [data-site-theme='nila'] .velu-sidebar > sidebar-group + sidebar-group { border-top: 1px solid var(--border-color); padding-top: 16px; }
26
+ [data-site-theme='nila'] .velu-sidebar__item { min-height: 32px; padding-block: 4px; }
27
+ @container docs (min-width: 1024px) {
28
+ [data-site-theme='nila'] .velu-header__col--brand > .velu-hide-on-mobile { display: flex; align-items: center; gap: 36px; }
29
+ [data-site-theme='nila'] .velu-header__col--brand { min-width: 0; flex-wrap: nowrap !important; }
30
+ [data-site-theme='nila'] .velu-header .velu-nav-select__btn { border: 0; background: transparent; padding: 4px 8px; font-size: 14px; }
31
+ [data-site-theme='nila'] .velu-header .velu-nav-select__btn-icon { display: none; }
32
+ [data-site-theme='nila'] .velu-docs-context:not(:has(.velu-docs-anchors)) { display: none !important; }
33
+ [data-site-theme='nila'] .velu-header { height: 96px; }
34
+ [data-site-theme='nila'] .velu-docs-layout__aside--left { left: 0; top: 96px; bottom: 0; width: 308px; padding: 16px 20px; border-right: 1px solid var(--border-color); }
35
+ [data-site-theme='nila'] .velu-sidebar { width: 268px; }
36
+ [data-site-theme='nila'] .velu-docs-layout__center { margin-left: 308px; margin-right: 324px; }
37
+ [data-site-theme='nila'] .velu-docs-layout__main { padding: 44px 32px 0; }
38
+ [data-site-theme='nila'] .velu-docs-layout__article { max-inline-size: 632px; margin-inline: auto; }
39
+ [data-site-theme='nila'] .velu-docs-layout__aside--right { top: 96px; right: 36px; width: 288px; }
40
+ [data-site-theme='nila'] > [data-velu-footer] { margin-left: 308px; }
41
+ }
42
+ @container docs (min-width: 1024px) and (max-width: 1399px) {
43
+ [data-site-theme='nila'] .velu-header__top { grid-template-columns: auto minmax(100px, 1fr) auto; gap: 20px !important; }
44
+ [data-site-theme='nila'] .velu-header__col--brand > .velu-hide-on-mobile { gap: 8px; }
45
+ [data-site-theme='nila'] .velu-header__col--center { width: 100%; }
46
+ }
47
+ @container docs (min-width: 1024px) and (max-width: 1279px) {
48
+ [data-site-theme='nila'] .velu-docs-layout__center { margin-right: 0; }
49
+ [data-site-theme='nila'] .velu-toc-bar { inset-block-start: 96px; }
50
+ }
51
+ @container docs (max-width: 1023px) {
52
+ [data-site-theme='nila'] .velu-hide-on-mobile { display: none; }
53
+ [data-site-theme='nila'] .velu-show-on-mobile { display: contents; }
54
+ [data-site-theme='nila'] .velu-docs-context { padding-inline: 16px; position: relative; z-index: 9; }
55
+ [data-site-theme='nila'] .velu-docs-context .velu-nav-select { width: 100%; min-width: 0; }
56
+ [data-site-theme='nila'] .velu-nav-select__menu { max-width: 100%; }
57
+ [data-site-theme='nila'] .velu-nav-select__item { white-space: normal; overflow-wrap: anywhere; }
58
+ [data-site-theme='nila'] .velu-header__top { height: 64px; padding-inline: 16px; }
59
+ [data-site-theme='nila'] .velu-header__tabs { height: 56px; padding-inline: 16px; }
60
+ [data-site-theme='nila'] .velu-header__tab { height: 56px; }
61
+ }
62
+