@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,99 @@
|
|
|
1
|
+
/* Prompt — surface card with title, monospace truncated body, and a
|
|
2
|
+
solid accent "Copy Prompt" button. All values are tokens; light /
|
|
3
|
+
dark auto via [data-theme] since the surface, text, and accent vars
|
|
4
|
+
all switch. */
|
|
5
|
+
|
|
6
|
+
.velu-prompt {
|
|
7
|
+
background: var(--surface-color);
|
|
8
|
+
border: var(--border-width) solid var(--border-color);
|
|
9
|
+
border-radius: var(--radius-md);
|
|
10
|
+
padding: var(--s2);
|
|
11
|
+
color: var(--text-color);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.velu-prompt__title {
|
|
15
|
+
font-size: var(--f-h4);
|
|
16
|
+
line-height: var(--lh-h4);
|
|
17
|
+
font-weight: var(--weight-medium);
|
|
18
|
+
margin-block-end: var(--s1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Truncated multi-line body. white-space:pre-wrap preserves the
|
|
22
|
+
newlines / leading whitespace of the source prompt; -webkit-box +
|
|
23
|
+
-webkit-line-clamp adds the auto ellipsis on the Nth line. The
|
|
24
|
+
`lines` prop sets -webkit-line-clamp inline via React. */
|
|
25
|
+
.velu-prompt__body {
|
|
26
|
+
margin: 0;
|
|
27
|
+
font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
28
|
+
font-size: var(--f-h6);
|
|
29
|
+
line-height: var(--lh-h6);
|
|
30
|
+
white-space: pre-wrap;
|
|
31
|
+
overflow-wrap: anywhere;
|
|
32
|
+
display: -webkit-box;
|
|
33
|
+
-webkit-box-orient: vertical;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Layout (flex + gap + justify-end) is provided by <Cluster>; this
|
|
38
|
+
rule only owns the spacing FROM the body above. */
|
|
39
|
+
.velu-prompt__actions {
|
|
40
|
+
margin-block-start: var(--s1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* Accent-filled copy button — same visual weight as the CTA on Card,
|
|
44
|
+
sized to read as the row's primary action. Hover dims the accent
|
|
45
|
+
slightly via color-mix. */
|
|
46
|
+
.velu-prompt__copy {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
gap: var(--s-2);
|
|
50
|
+
padding-block: var(--s-6);
|
|
51
|
+
padding-inline: var(--s-3);
|
|
52
|
+
background: var(--accent-color);
|
|
53
|
+
border: 0;
|
|
54
|
+
border-radius: var(--radius-sm);
|
|
55
|
+
color: #fff;
|
|
56
|
+
font: inherit;
|
|
57
|
+
font-size: var(--f-h6);
|
|
58
|
+
line-height: var(--lh-h6);
|
|
59
|
+
font-weight: var(--weight-medium);
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
}
|
|
62
|
+
.velu-prompt__copy:hover {
|
|
63
|
+
background: color-mix(in srgb, var(--accent-color) 88%, #000);
|
|
64
|
+
}
|
|
65
|
+
.velu-prompt__copy:focus-visible {
|
|
66
|
+
outline: var(--border-width) solid var(--text-color);
|
|
67
|
+
outline-offset: 2px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.velu-prompt__copy-icon {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Open in Cursor — same shape as copy, inverted neutral palette so it
|
|
76
|
+
reads as the secondary action sitting next to the accent primary.
|
|
77
|
+
Inherits the cursor brand glyph color via `currentColor`. */
|
|
78
|
+
.velu-prompt__open-cursor {
|
|
79
|
+
display: inline-flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: var(--s-2);
|
|
82
|
+
padding-block: var(--s-6);
|
|
83
|
+
padding-inline: var(--s-3);
|
|
84
|
+
background: var(--text-color);
|
|
85
|
+
color: var(--page-bg);
|
|
86
|
+
border-radius: var(--radius-sm);
|
|
87
|
+
font: inherit;
|
|
88
|
+
font-size: var(--f-h6);
|
|
89
|
+
line-height: var(--lh-h6);
|
|
90
|
+
font-weight: var(--weight-medium);
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
}
|
|
93
|
+
.velu-prompt__open-cursor:hover {
|
|
94
|
+
background: color-mix(in srgb, var(--text-color) 88%, var(--accent-color));
|
|
95
|
+
}
|
|
96
|
+
.velu-prompt__open-cursor:focus-visible {
|
|
97
|
+
outline: var(--border-width) solid var(--accent-color);
|
|
98
|
+
outline-offset: 2px;
|
|
99
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/* Search — command-palette search. Ported from the Claude-Design "Velu
|
|
2
|
+
Search" handoff and retokenized: the design's surf/txt/bord layers
|
|
3
|
+
map onto velu-ui tokens, so it themes for free via [data-theme].
|
|
4
|
+
surf-page / surf-elevated → --page-bg
|
|
5
|
+
surf-row-hover / kbd → --surface-color
|
|
6
|
+
surf-row-selected → accent tint
|
|
7
|
+
txt-1 → --text-color
|
|
8
|
+
txt-2 / txt-3 / muted → --muted-color
|
|
9
|
+
bord-* → --border-color
|
|
10
|
+
accent → --accent-color
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/* ── Shared kbd chip (⌘K trigger hint / esc / ↵) ────────────────────── */
|
|
14
|
+
/* line-height: 1 so a tall glyph like ↵ doesn't stretch the chip;
|
|
15
|
+
justify-content centers single-character contents. */
|
|
16
|
+
.velu-search__kbd {
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
flex: none;
|
|
21
|
+
min-inline-size: var(--s0);
|
|
22
|
+
padding-block: var(--s-5);
|
|
23
|
+
padding-inline: var(--s-3);
|
|
24
|
+
background: var(--surface-color);
|
|
25
|
+
border: var(--border-width) solid var(--border-color);
|
|
26
|
+
border-radius: var(--radius-sm);
|
|
27
|
+
font-size: var(--f-h7);
|
|
28
|
+
line-height: 1;
|
|
29
|
+
font-weight: var(--weight-medium);
|
|
30
|
+
color: var(--text-color);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ── Trigger box (placed at the top of the page) ────────────────────── */
|
|
34
|
+
/* Content-sized; the label's flex:1 only matters if the consumer gives
|
|
35
|
+
the trigger an explicit width. */
|
|
36
|
+
.velu-search__trigger {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: var(--s-3);
|
|
40
|
+
padding-block: var(--s-3);
|
|
41
|
+
padding-inline: var(--s-2);
|
|
42
|
+
background: var(--page-bg);
|
|
43
|
+
border: var(--border-width) solid var(--border-color);
|
|
44
|
+
border-radius: var(--radius-sm);
|
|
45
|
+
color: var(--muted-color);
|
|
46
|
+
font: inherit;
|
|
47
|
+
font-size: var(--f-h6);
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
transition: border-color 0.12s ease;
|
|
50
|
+
}
|
|
51
|
+
.velu-search__trigger:hover {
|
|
52
|
+
border-color: var(--accent-color);
|
|
53
|
+
}
|
|
54
|
+
.velu-search__trigger-icon {
|
|
55
|
+
display: inline-flex;
|
|
56
|
+
flex: none;
|
|
57
|
+
color: var(--muted-color);
|
|
58
|
+
}
|
|
59
|
+
.velu-search__trigger-label {
|
|
60
|
+
flex: 1;
|
|
61
|
+
text-align: start;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ── Mobile collapse — trigger becomes icon-only ───────────────────── */
|
|
65
|
+
/* At mobile widths (< 640px) the header is too crowded to show the
|
|
66
|
+
full search box; collapse to a magnifier icon. App.jsx passes
|
|
67
|
+
`inline-size: 30ch` inline on the trigger to fix its desktop width,
|
|
68
|
+
so we need !important here to win over the inline style. The kbd
|
|
69
|
+
chip is also hidden — at mobile there's no keyboard shortcut hint
|
|
70
|
+
to display. */
|
|
71
|
+
@container docs (max-width: 640px) {
|
|
72
|
+
.velu-search__trigger {
|
|
73
|
+
inline-size: auto !important;
|
|
74
|
+
padding-inline: var(--s-3);
|
|
75
|
+
}
|
|
76
|
+
.velu-search__trigger-label,
|
|
77
|
+
.velu-search__trigger .velu-search__kbd {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ── Scrim + palette ────────────────────────────────────────────────── */
|
|
83
|
+
.velu-search__scrim {
|
|
84
|
+
position: fixed;
|
|
85
|
+
inset: 0;
|
|
86
|
+
z-index: 60;
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: flex-start;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
/* Top offset eases down on short screens; side gutter is small so the
|
|
91
|
+
palette uses most of a narrow viewport (no effect on desktop —
|
|
92
|
+
the palette is capped at --vsearch-width there). */
|
|
93
|
+
padding-block-start: clamp(var(--s1), 8vh, 12vh);
|
|
94
|
+
padding-inline: var(--s-3);
|
|
95
|
+
background: color-mix(in srgb, #000 48%, transparent);
|
|
96
|
+
-webkit-backdrop-filter: blur(4px);
|
|
97
|
+
backdrop-filter: blur(4px);
|
|
98
|
+
animation: velu-search-scrim-in 0.18s ease-out;
|
|
99
|
+
}
|
|
100
|
+
@keyframes velu-search-scrim-in {
|
|
101
|
+
from { opacity: 0; }
|
|
102
|
+
to { opacity: 1; }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.velu-search__palette {
|
|
106
|
+
/* Overridable panel width (≈ the design's 720px). */
|
|
107
|
+
--vsearch-width: 45rem;
|
|
108
|
+
inline-size: var(--vsearch-width);
|
|
109
|
+
max-inline-size: 100%;
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
background: var(--page-bg);
|
|
114
|
+
color: var(--text-color);
|
|
115
|
+
border: var(--border-width) solid var(--border-color);
|
|
116
|
+
border-radius: var(--radius-md);
|
|
117
|
+
box-shadow:
|
|
118
|
+
0 var(--s2) var(--s4) color-mix(in srgb, #000 32%, transparent),
|
|
119
|
+
0 var(--s0) var(--s2) color-mix(in srgb, #000 18%, transparent);
|
|
120
|
+
animation: velu-search-palette-in 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
121
|
+
}
|
|
122
|
+
@keyframes velu-search-palette-in {
|
|
123
|
+
from {
|
|
124
|
+
opacity: 0;
|
|
125
|
+
transform: translateY(calc(var(--s-3) * -1)) scale(0.985);
|
|
126
|
+
}
|
|
127
|
+
to {
|
|
128
|
+
opacity: 1;
|
|
129
|
+
transform: none;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/* base.css's global `* { max-width: 66ch }` would cap the <input>;
|
|
133
|
+
reset within the palette — flex / the panel govern width here. */
|
|
134
|
+
.velu-search__palette * {
|
|
135
|
+
max-inline-size: none;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ── Input row ──────────────────────────────────────────────────────── */
|
|
139
|
+
.velu-search__input-wrap {
|
|
140
|
+
display: flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
gap: var(--s-2);
|
|
143
|
+
padding: var(--s-1) var(--s0);
|
|
144
|
+
border-block-end: var(--border-width) solid var(--border-color);
|
|
145
|
+
}
|
|
146
|
+
.velu-search__input-icon {
|
|
147
|
+
display: inline-flex;
|
|
148
|
+
flex: none;
|
|
149
|
+
color: var(--muted-color);
|
|
150
|
+
}
|
|
151
|
+
.velu-search__input {
|
|
152
|
+
flex: 1 1 auto;
|
|
153
|
+
min-inline-size: 0;
|
|
154
|
+
padding: 0;
|
|
155
|
+
background: transparent;
|
|
156
|
+
border: 0;
|
|
157
|
+
outline: 0;
|
|
158
|
+
font: inherit;
|
|
159
|
+
font-size: var(--f-h4);
|
|
160
|
+
font-weight: var(--weight-light);
|
|
161
|
+
line-height: var(--lh-h4);
|
|
162
|
+
color: var(--text-color);
|
|
163
|
+
}
|
|
164
|
+
.velu-search__input::placeholder {
|
|
165
|
+
color: var(--muted-color);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ── Result list ────────────────────────────────────────────────────── */
|
|
169
|
+
.velu-search__list {
|
|
170
|
+
flex: 1 1 auto;
|
|
171
|
+
overflow-y: auto;
|
|
172
|
+
padding: var(--s-3);
|
|
173
|
+
max-block-size: 72vh;
|
|
174
|
+
}
|
|
175
|
+
.velu-search__group {
|
|
176
|
+
padding: var(--s-1) var(--s-1) var(--s-4);
|
|
177
|
+
font-size: var(--f-h7);
|
|
178
|
+
font-weight: var(--weight-medium);
|
|
179
|
+
letter-spacing: 0.08em;
|
|
180
|
+
text-transform: uppercase;
|
|
181
|
+
color: var(--muted-color);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Result row — grid: glyph | (breadcrumb / title / desc) | meta. */
|
|
185
|
+
.velu-search__row {
|
|
186
|
+
display: grid;
|
|
187
|
+
grid-template-columns: auto 1fr auto;
|
|
188
|
+
gap: var(--s-6) var(--s0);
|
|
189
|
+
align-items: center;
|
|
190
|
+
padding: var(--s-2);
|
|
191
|
+
border-radius: var(--radius-sm);
|
|
192
|
+
color: var(--text-color);
|
|
193
|
+
cursor: pointer;
|
|
194
|
+
}
|
|
195
|
+
.velu-search__row + .velu-search__row {
|
|
196
|
+
margin-block-start: var(--s-6);
|
|
197
|
+
}
|
|
198
|
+
.velu-search__row:hover {
|
|
199
|
+
background: var(--surface-color);
|
|
200
|
+
}
|
|
201
|
+
.velu-search__row--selected {
|
|
202
|
+
background: color-mix(in srgb, var(--accent-color) 12%, transparent);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.velu-search__row-glyph {
|
|
206
|
+
grid-column: 1;
|
|
207
|
+
grid-row: 1 / -1;
|
|
208
|
+
display: flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
justify-content: center;
|
|
211
|
+
inline-size: 2.5em;
|
|
212
|
+
block-size: 2.5em;
|
|
213
|
+
color: var(--text-color);
|
|
214
|
+
}
|
|
215
|
+
.velu-search__glyph-hash {
|
|
216
|
+
font-size: var(--f-h3);
|
|
217
|
+
font-weight: var(--weight-light);
|
|
218
|
+
line-height: 1;
|
|
219
|
+
color: var(--text-color);
|
|
220
|
+
}
|
|
221
|
+
.velu-search__glyph-icon {
|
|
222
|
+
display: inline-flex;
|
|
223
|
+
inline-size: var(--icon-size-lg);
|
|
224
|
+
block-size: var(--icon-size-lg);
|
|
225
|
+
color: var(--muted-color);
|
|
226
|
+
}
|
|
227
|
+
.velu-search__row--selected .velu-search__glyph-icon {
|
|
228
|
+
color: var(--text-color);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.velu-search__crumbs {
|
|
232
|
+
grid-column: 2;
|
|
233
|
+
grid-row: 1;
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
flex-wrap: wrap;
|
|
237
|
+
gap: var(--s-4);
|
|
238
|
+
font-size: var(--f-h6);
|
|
239
|
+
line-height: var(--lh-h6);
|
|
240
|
+
color: var(--muted-color);
|
|
241
|
+
}
|
|
242
|
+
.velu-search__crumb-sep {
|
|
243
|
+
opacity: 0.6;
|
|
244
|
+
}
|
|
245
|
+
.velu-search__row-title {
|
|
246
|
+
grid-column: 2;
|
|
247
|
+
grid-row: 2;
|
|
248
|
+
font-size: var(--f-h5);
|
|
249
|
+
font-weight: var(--weight-medium);
|
|
250
|
+
line-height: var(--lh-h5);
|
|
251
|
+
color: var(--text-color);
|
|
252
|
+
}
|
|
253
|
+
.velu-search__row-desc {
|
|
254
|
+
grid-column: 2;
|
|
255
|
+
grid-row: 3;
|
|
256
|
+
font-size: var(--f-h6);
|
|
257
|
+
font-weight: var(--weight-light);
|
|
258
|
+
line-height: var(--lh-h6);
|
|
259
|
+
color: var(--muted-color);
|
|
260
|
+
overflow: hidden;
|
|
261
|
+
text-overflow: ellipsis;
|
|
262
|
+
white-space: nowrap;
|
|
263
|
+
}
|
|
264
|
+
.velu-search__row-meta {
|
|
265
|
+
grid-column: 3;
|
|
266
|
+
grid-row: 1 / -1;
|
|
267
|
+
display: flex;
|
|
268
|
+
align-items: center;
|
|
269
|
+
opacity: 0;
|
|
270
|
+
transition: opacity 0.15s ease;
|
|
271
|
+
}
|
|
272
|
+
.velu-search__row:hover .velu-search__row-meta,
|
|
273
|
+
.velu-search__row--selected .velu-search__row-meta {
|
|
274
|
+
opacity: 1;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* ── Empty / no-results ─────────────────────────────────────────────── */
|
|
278
|
+
.velu-search__empty {
|
|
279
|
+
display: flex;
|
|
280
|
+
flex-direction: column;
|
|
281
|
+
align-items: center;
|
|
282
|
+
gap: var(--s-6);
|
|
283
|
+
padding: var(--s3) var(--s0);
|
|
284
|
+
text-align: center;
|
|
285
|
+
}
|
|
286
|
+
.velu-search__empty-icon {
|
|
287
|
+
display: inline-flex;
|
|
288
|
+
margin-block-end: var(--s-3);
|
|
289
|
+
font-size: var(--f-h2);
|
|
290
|
+
color: var(--muted-color);
|
|
291
|
+
}
|
|
292
|
+
.velu-search__empty-title {
|
|
293
|
+
font-size: var(--f-h6);
|
|
294
|
+
font-weight: var(--weight-medium);
|
|
295
|
+
color: var(--text-color);
|
|
296
|
+
}
|
|
297
|
+
.velu-search__empty-sub {
|
|
298
|
+
font-size: var(--f-h6);
|
|
299
|
+
color: var(--muted-color);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@media (prefers-reduced-motion: reduce) {
|
|
303
|
+
.velu-search__scrim,
|
|
304
|
+
.velu-search__palette {
|
|
305
|
+
animation: none;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* Sidebar — sidebar-specific styling ONLY.
|
|
2
|
+
|
|
3
|
+
Vertical rhythm (nav, section lists, nested sublists) is composed from the
|
|
4
|
+
Stack primitive in Sidebar.jsx (DRY) — Stack supplies display/flex/gap
|
|
5
|
+
inline. This file holds only what Stack cannot express: the indent rails,
|
|
6
|
+
active accent rail, the horizontal item rows, summary marker, chevron
|
|
7
|
+
rotation, and centralized icon sizing/stroke. All values are tokens. */
|
|
8
|
+
|
|
9
|
+
.velu-sidebar {
|
|
10
|
+
border-left: var(--border-width) solid var(--surface-color);
|
|
11
|
+
margin-left: var(--s1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.velu-sidebar__section {
|
|
15
|
+
/* Sticky section header: each heading pins to the top of the scroll
|
|
16
|
+
region until its own section scrolls past, then the next heading
|
|
17
|
+
takes over (iOS-list style). It sticks within its section wrapper
|
|
18
|
+
(Sidebar.jsx renders each section in its own Stack), which bounds
|
|
19
|
+
the push-out. Full-width opaque background so scrolling items
|
|
20
|
+
don't show behind it; --page-bg matches the surroundings, so the
|
|
21
|
+
bar is invisible until it's actually covering scrolled content. */
|
|
22
|
+
position: sticky;
|
|
23
|
+
inset-block-start: 0;
|
|
24
|
+
z-index: 1;
|
|
25
|
+
background: var(--page-bg);
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: var(--s-2);
|
|
29
|
+
padding-block: var(--s-4);
|
|
30
|
+
padding-inline: var(--s-3);
|
|
31
|
+
/* A notch smaller than the default h5, with a little tracking so the
|
|
32
|
+
uppercase label stays legible at the reduced size. */
|
|
33
|
+
font-size: var(--f-h6);
|
|
34
|
+
font-weight: 500;
|
|
35
|
+
letter-spacing: 0.02em;
|
|
36
|
+
text-transform: uppercase;
|
|
37
|
+
}
|
|
38
|
+
/* Fade zone just below the pinned heading: a gradient overlay
|
|
39
|
+
(page-bg → transparent) that sits directly under the sticky header
|
|
40
|
+
and scrolls with it. Items sliding up pass behind it and fade out
|
|
41
|
+
before they tuck under the (crisp) heading. Absolutely positioned
|
|
42
|
+
so it adds no resting layout space; pointer-events:none so it never
|
|
43
|
+
eats clicks. Hidden by default — only shown once the nav is actually
|
|
44
|
+
scrolled (the layout toggles it via data-fade-top on the scroll
|
|
45
|
+
region), so a section's first item isn't faded at rest. */
|
|
46
|
+
.velu-sidebar__section::after {
|
|
47
|
+
content: '';
|
|
48
|
+
position: absolute;
|
|
49
|
+
inset-inline: 0;
|
|
50
|
+
inset-block-start: 100%;
|
|
51
|
+
block-size: var(--s3);
|
|
52
|
+
background: linear-gradient(
|
|
53
|
+
to bottom,
|
|
54
|
+
var(--page-bg),
|
|
55
|
+
color-mix(in srgb, var(--page-bg) 55%, transparent),
|
|
56
|
+
transparent
|
|
57
|
+
);
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
opacity: 0;
|
|
60
|
+
transition: opacity 0.15s ease;
|
|
61
|
+
}
|
|
62
|
+
/* When a heading becomes the newly-pinned one (data-stuck set by the
|
|
63
|
+
runtime as you scroll past sections), fade + slide it in so the
|
|
64
|
+
handoff from the previous heading reads as a transition rather than
|
|
65
|
+
a hard swap. */
|
|
66
|
+
.velu-sidebar__section[data-stuck='true'] {
|
|
67
|
+
animation: velu-sidebar-stick-in 0.2s ease;
|
|
68
|
+
}
|
|
69
|
+
@keyframes velu-sidebar-stick-in {
|
|
70
|
+
from {
|
|
71
|
+
opacity: 0.3;
|
|
72
|
+
transform: translateY(-0.2rem);
|
|
73
|
+
}
|
|
74
|
+
to {
|
|
75
|
+
opacity: 1;
|
|
76
|
+
transform: translateY(0);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Lists are Stacks (flex/gap come from Stack inline) — only reset list chrome. */
|
|
81
|
+
.velu-sidebar__list,
|
|
82
|
+
.velu-sidebar__sublist {
|
|
83
|
+
list-style: none;
|
|
84
|
+
margin: 0;
|
|
85
|
+
padding: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.velu-sidebar__sublist {
|
|
89
|
+
margin-left: var(--s0);
|
|
90
|
+
border-left: var(--border-width) solid var(--border-color);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Item rows are horizontal (icon · label · chevron) — Stack is vertical-only,
|
|
94
|
+
so these stay CSS until/if a Cluster/Inline primitive exists. */
|
|
95
|
+
.velu-sidebar__item,
|
|
96
|
+
.velu-sidebar__subitem {
|
|
97
|
+
display: flex;
|
|
98
|
+
align-items: center;
|
|
99
|
+
gap: var(--s-3);
|
|
100
|
+
padding-inline: var(--s0);
|
|
101
|
+
color: var(--text-color);
|
|
102
|
+
text-decoration: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.velu-sidebar__subitem {
|
|
106
|
+
padding-block: var(--s-5);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.velu-sidebar__item--active {
|
|
110
|
+
color: var(--accent-color);
|
|
111
|
+
border-left: 2px solid var(--accent-color);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.velu-sidebar__summary {
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
list-style: none;
|
|
117
|
+
}
|
|
118
|
+
.velu-sidebar__summary::-webkit-details-marker {
|
|
119
|
+
display: none;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Icon sizing centralized here; stroke weight is the global --icon-stroke
|
|
123
|
+
token (.lucide rule in base.css), not set per-component. */
|
|
124
|
+
.velu-sidebar__icon {
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
flex: none;
|
|
127
|
+
inline-size: var(--f-body);
|
|
128
|
+
block-size: var(--f-body);
|
|
129
|
+
}
|
|
130
|
+
.velu-sidebar__icon svg {
|
|
131
|
+
inline-size: 100%;
|
|
132
|
+
block-size: 100%;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.velu-sidebar__chevron {
|
|
136
|
+
flex: none;
|
|
137
|
+
inline-size: var(--f-body);
|
|
138
|
+
block-size: var(--f-body);
|
|
139
|
+
transition: transform 0.15s ease;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
details[open] > .velu-sidebar__summary .velu-sidebar__chevron {
|
|
143
|
+
transform: rotate(90deg);
|
|
144
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* Steps — numbered procedural list with a continuous accent rail
|
|
2
|
+
running through every step's left margin. All values are tokens;
|
|
3
|
+
light/dark via [data-theme]. */
|
|
4
|
+
|
|
5
|
+
.velu-steps {
|
|
6
|
+
display: block;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* Each step is a 2-col / 2-row grid placed explicitly:
|
|
10
|
+
|
|
11
|
+
col 1 col 2
|
|
12
|
+
┌──────────────────┬────────────────────┐
|
|
13
|
+
│ circle (row 1, │ title (row 1, │
|
|
14
|
+
│ align-self: end) │ align-self: end) │
|
|
15
|
+
├──────────────────┼────────────────────┤
|
|
16
|
+
│ line (row 2, │ body (row 2) │
|
|
17
|
+
│ 100% block-size) │ │
|
|
18
|
+
└──────────────────┴────────────────────┘
|
|
19
|
+
|
|
20
|
+
`align-self: end` on both row-1 items puts the circle's bottom and
|
|
21
|
+
the title's bottom flush together — so an oversize circle no longer
|
|
22
|
+
hovers above the title. Row 1 height is whichever child is taller
|
|
23
|
+
(the circle). row-gap stays at 0 so the line touches the next step's
|
|
24
|
+
circle with no visible break in the rail. */
|
|
25
|
+
.velu-step {
|
|
26
|
+
display: grid;
|
|
27
|
+
grid-template-columns: var(--step-circle, 2em) 1fr;
|
|
28
|
+
column-gap: var(--s0);
|
|
29
|
+
row-gap: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.velu-step__circle {
|
|
33
|
+
grid-row: 1;
|
|
34
|
+
grid-column: 1;
|
|
35
|
+
align-self: center;
|
|
36
|
+
justify-self: center;
|
|
37
|
+
inline-size: var(--step-circle, 2em);
|
|
38
|
+
block-size: var(--step-circle, 2em);
|
|
39
|
+
border-radius: 50%;
|
|
40
|
+
background: var(--accent-color);
|
|
41
|
+
color: var(--page-bg);
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
font-size: var(--f-h6);
|
|
46
|
+
line-height: 1;
|
|
47
|
+
font-weight: var(--weight-medium);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.velu-step__line {
|
|
51
|
+
grid-row: 2;
|
|
52
|
+
grid-column: 1;
|
|
53
|
+
justify-self: center;
|
|
54
|
+
inline-size: 2px;
|
|
55
|
+
block-size: 100%;
|
|
56
|
+
min-block-size: var(--s2);
|
|
57
|
+
background: var(--accent-color);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.velu-step__title {
|
|
61
|
+
grid-row: 1;
|
|
62
|
+
grid-column: 2;
|
|
63
|
+
align-self: center;
|
|
64
|
+
font-size: var(--f-h4);
|
|
65
|
+
line-height: var(--lh-h4);
|
|
66
|
+
font-weight: var(--weight-medium);
|
|
67
|
+
color: var(--text-color);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.velu-step__body {
|
|
71
|
+
grid-row: 2;
|
|
72
|
+
grid-column: 2;
|
|
73
|
+
padding-block: var(--s-1) var(--s2);
|
|
74
|
+
color: var(--text-color);
|
|
75
|
+
font-size: var(--f-body);
|
|
76
|
+
line-height: var(--lh-body);
|
|
77
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/* ThemeToggle — circular button with a sun/moon glyph; click flips
|
|
2
|
+
the theme. Geometry is a 32 × 32 circle with an 18 × 18 glyph
|
|
3
|
+
centred inside. The pill-with-slider design was replaced earlier;
|
|
4
|
+
only the circle survives now.
|
|
5
|
+
|
|
6
|
+
In light mode: --page-bg circle, sun glyph.
|
|
7
|
+
In dark mode: --page-bg circle, moon glyph.
|
|
8
|
+
|
|
9
|
+
Visibility model: BOTH icons are always rendered, stacked at the
|
|
10
|
+
same `position: absolute` slot inside the button. Opacity controls
|
|
11
|
+
which one is visible. We use opacity (not display: none) because:
|
|
12
|
+
|
|
13
|
+
1. Stacked icons render at the exact same point — no layout
|
|
14
|
+
shift when the theme flips.
|
|
15
|
+
2. Opacity is not touched anywhere else for these elements, so
|
|
16
|
+
the cascade is trivial — no risk of a stray rule (preflight,
|
|
17
|
+
utility class, dev-time fallback) overriding `display: none`
|
|
18
|
+
and revealing the wrong icon.
|
|
19
|
+
3. Identical markup on server + client → SSR-safe, no hydration
|
|
20
|
+
mismatch and no icon flash.
|
|
21
|
+
|
|
22
|
+
All values are tokens. */
|
|
23
|
+
|
|
24
|
+
.velu-theme-toggle {
|
|
25
|
+
/* `relative` so the absolutely-positioned icons inside are
|
|
26
|
+
anchored to this button, not the page. */
|
|
27
|
+
position: relative;
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
inline-size: 2rem;
|
|
32
|
+
block-size: 2rem;
|
|
33
|
+
border: var(--border-width) solid var(--border-color);
|
|
34
|
+
border-radius: 999px;
|
|
35
|
+
padding: 0;
|
|
36
|
+
background: var(--page-bg);
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
transition: border-color 0.12s ease, background 0.12s ease;
|
|
39
|
+
}
|
|
40
|
+
.velu-theme-toggle:hover {
|
|
41
|
+
border-color: var(--accent-color);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Both icons stack here, centred. Default opacity 0 — the per-theme
|
|
45
|
+
rules below switch one on. */
|
|
46
|
+
.velu-theme-toggle__icon {
|
|
47
|
+
position: absolute;
|
|
48
|
+
inset-block-start: 50%;
|
|
49
|
+
inset-inline-start: 50%;
|
|
50
|
+
inline-size: 1.125rem;
|
|
51
|
+
block-size: 1.125rem;
|
|
52
|
+
margin-block-start: -0.5625rem; /* half of block-size */
|
|
53
|
+
margin-inline-start: -0.5625rem; /* half of inline-size */
|
|
54
|
+
color: var(--text-color);
|
|
55
|
+
opacity: 0;
|
|
56
|
+
transition: opacity 0.2s ease;
|
|
57
|
+
pointer-events: none;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Default (light) → sun on. */
|
|
61
|
+
.velu-theme-toggle__icon--sun {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
}
|
|
64
|
+
/* Dark theme → moon on, sun off. */
|
|
65
|
+
[data-theme='dark'] .velu-theme-toggle__icon--sun {
|
|
66
|
+
opacity: 0;
|
|
67
|
+
}
|
|
68
|
+
[data-theme='dark'] .velu-theme-toggle__icon--moon {
|
|
69
|
+
opacity: 1;
|
|
70
|
+
}
|