@veluai/velu 0.1.13 → 0.1.14
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/README.md +80 -80
- package/dist/cli.js +18 -18
- package/package.json +3 -2
- package/runtime/velu-ui/components/Accordion.jsx +64 -64
- package/runtime/velu-ui/components/ApiClient.jsx +121 -121
- package/runtime/velu-ui/components/ApiField.jsx +87 -87
- package/runtime/velu-ui/components/ApiPath.jsx +63 -63
- package/runtime/velu-ui/components/ApiSidebar.jsx +122 -122
- package/runtime/velu-ui/components/AskBar.jsx +71 -71
- package/runtime/velu-ui/components/Callout.jsx +114 -114
- package/runtime/velu-ui/components/Card.jsx +131 -131
- package/runtime/velu-ui/components/Chatbot.jsx +596 -596
- package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
- package/runtime/velu-ui/components/Columns.jsx +56 -56
- package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
- package/runtime/velu-ui/components/Field.jsx +81 -81
- package/runtime/velu-ui/components/Image.jsx +163 -163
- package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
- package/runtime/velu-ui/components/NavSelect.jsx +108 -108
- package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
- package/runtime/velu-ui/components/PageFooter.jsx +213 -213
- package/runtime/velu-ui/components/PageHeader.jsx +414 -414
- package/runtime/velu-ui/components/PageNav.jsx +77 -77
- package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
- package/runtime/velu-ui/components/Prompt.jsx +115 -115
- package/runtime/velu-ui/components/Search.jsx +411 -366
- package/runtime/velu-ui/components/Sidebar.jsx +191 -191
- package/runtime/velu-ui/components/Steps.jsx +65 -65
- package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
- package/runtime/velu-ui/components/Toc.jsx +537 -537
- package/runtime/velu-ui/components/TocBar.jsx +195 -195
- package/runtime/velu-ui/components/Tree.jsx +87 -87
- package/runtime/velu-ui/components/TryItBar.jsx +90 -90
- package/runtime/velu-ui/components/accordion.css +92 -92
- package/runtime/velu-ui/components/api.css +479 -479
- package/runtime/velu-ui/components/ask-bar.css +94 -94
- package/runtime/velu-ui/components/card.css +105 -105
- package/runtime/velu-ui/components/chatbot.css +617 -617
- package/runtime/velu-ui/components/code-block.css +263 -263
- package/runtime/velu-ui/components/docs-layout.css +784 -775
- package/runtime/velu-ui/components/field.css +82 -82
- package/runtime/velu-ui/components/image.css +237 -237
- package/runtime/velu-ui/components/nav-select.css +157 -157
- package/runtime/velu-ui/components/page-feedback.css +241 -241
- package/runtime/velu-ui/components/page-footer.css +130 -130
- package/runtime/velu-ui/components/page-header.css +520 -520
- package/runtime/velu-ui/components/page-nav.css +50 -50
- package/runtime/velu-ui/components/powered-by.css +66 -66
- package/runtime/velu-ui/components/prompt.css +99 -99
- package/runtime/velu-ui/components/search.css +307 -307
- package/runtime/velu-ui/components/sidebar.css +144 -144
- package/runtime/velu-ui/components/steps.css +77 -77
- package/runtime/velu-ui/components/theme-toggle.css +101 -101
- package/runtime/velu-ui/components/toc-bar.css +234 -234
- package/runtime/velu-ui/components/tree.css +49 -49
- package/runtime/velu-ui/index.js +46 -46
- package/runtime/velu-ui/lib/component-schemas.js +100 -100
- package/runtime/velu-ui/lib/copyText.js +64 -64
- package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
- package/runtime/velu-ui/lib/prism-langs.js +957 -957
- package/runtime/velu-ui/lib/prism-loader.js +74 -74
- package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
- package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
- package/runtime/velu-ui/mdx-components.jsx +105 -105
- package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
- package/runtime/velu-ui/primitives/Stack.jsx +63 -63
- package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
- package/runtime/velu-ui/primitives/stack.css +3 -3
- package/runtime/velu-ui/primitives/switcher.css +25 -25
- package/runtime/velu-ui/styles.css +43 -43
- package/runtime/velu-ui/tokens.css +4 -4
- package/schema/velu.schema.json +167 -167
- package/src/lib/extract-mdx-error.js +170 -170
- package/src/lib/issues.js +159 -159
- package/src/lib/known-components.js +34 -34
- package/src/navigation.js +434 -434
- package/src/runtime/App.jsx +1490 -1476
- package/src/runtime/ErrorBoundary.jsx +54 -54
- package/src/runtime/client-entry.jsx +22 -22
- package/src/runtime/server-entry.jsx +16 -16
- package/src/template.html +48 -48
- package/templates/starter/ai-tools/claude-code.mdx +26 -26
- package/templates/starter/ai-tools/cursor.mdx +17 -17
- package/templates/starter/api-reference/endpoint/create.mdx +24 -24
- package/templates/starter/api-reference/endpoint/get.mdx +27 -27
- package/templates/starter/api-reference/introduction.mdx +28 -28
- package/templates/starter/development.mdx +19 -19
- package/templates/starter/essentials/code.mdx +29 -29
- package/templates/starter/essentials/images.mdx +29 -29
- package/templates/starter/essentials/markdown.mdx +25 -25
- package/templates/starter/essentials/navigation.mdx +39 -39
- package/templates/starter/essentials/settings.mdx +30 -30
- package/templates/starter/favicon.svg +6 -6
- package/templates/starter/index.mdx +31 -31
- package/templates/starter/quickstart.mdx +31 -31
- package/templates/starter/velu.json +33 -33
|
@@ -1,144 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,77 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,101 +1,101 @@
|
|
|
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
|
-
transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
40
|
-
}
|
|
41
|
-
.velu-theme-toggle:hover {
|
|
42
|
-
border-color: var(--accent-color);
|
|
43
|
-
}
|
|
44
|
-
/* Tactile press feedback on click. */
|
|
45
|
-
.velu-theme-toggle:active {
|
|
46
|
-
transform: scale(0.9);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/* Both icons stack here, centred. Each animates in/out on a theme flip:
|
|
50
|
-
the hidden glyph is faded, shrunk, and rotated off its axis; the active
|
|
51
|
-
one springs to full size at 0deg. Centring is done with margins (above),
|
|
52
|
-
so `transform` is free to drive the rotate/scale animation. */
|
|
53
|
-
.velu-theme-toggle__icon {
|
|
54
|
-
position: absolute;
|
|
55
|
-
inset-block-start: 50%;
|
|
56
|
-
inset-inline-start: 50%;
|
|
57
|
-
inline-size: 1.125rem;
|
|
58
|
-
block-size: 1.125rem;
|
|
59
|
-
margin-block-start: -0.5625rem; /* half of block-size */
|
|
60
|
-
margin-inline-start: -0.5625rem; /* half of inline-size */
|
|
61
|
-
color: var(--text-color);
|
|
62
|
-
opacity: 0;
|
|
63
|
-
transition: opacity 0.28s ease,
|
|
64
|
-
transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
65
|
-
pointer-events: none;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/* Hidden state per glyph — sun spins out clockwise, moon anti-clockwise,
|
|
69
|
-
so the swap reads as one rotating through the other. */
|
|
70
|
-
.velu-theme-toggle__icon--sun {
|
|
71
|
-
transform: rotate(90deg) scale(0.4);
|
|
72
|
-
}
|
|
73
|
-
.velu-theme-toggle__icon--moon {
|
|
74
|
-
transform: rotate(-90deg) scale(0.4);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/* Exactly one glyph is shown — the sun in light, the moon in dark — at full
|
|
78
|
-
size, upright. Higher specificity than the hidden defaults above, so it
|
|
79
|
-
wins for whichever glyph the active theme selects. */
|
|
80
|
-
:root:not([data-theme='dark']) .velu-theme-toggle__icon--sun,
|
|
81
|
-
[data-theme='dark'] .velu-theme-toggle__icon--moon {
|
|
82
|
-
opacity: 1;
|
|
83
|
-
transform: rotate(0deg) scale(1);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* Honour reduced-motion: keep a plain crossfade — no spin, scale, or press. */
|
|
87
|
-
@media (prefers-reduced-motion: reduce) {
|
|
88
|
-
.velu-theme-toggle {
|
|
89
|
-
transition: border-color 0.12s ease, background 0.12s ease;
|
|
90
|
-
}
|
|
91
|
-
.velu-theme-toggle:active {
|
|
92
|
-
transform: none;
|
|
93
|
-
}
|
|
94
|
-
.velu-theme-toggle__icon {
|
|
95
|
-
transition: opacity 0.2s ease;
|
|
96
|
-
}
|
|
97
|
-
.velu-theme-toggle__icon--sun,
|
|
98
|
-
.velu-theme-toggle__icon--moon {
|
|
99
|
-
transform: none;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
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
|
+
transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
40
|
+
}
|
|
41
|
+
.velu-theme-toggle:hover {
|
|
42
|
+
border-color: var(--accent-color);
|
|
43
|
+
}
|
|
44
|
+
/* Tactile press feedback on click. */
|
|
45
|
+
.velu-theme-toggle:active {
|
|
46
|
+
transform: scale(0.9);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Both icons stack here, centred. Each animates in/out on a theme flip:
|
|
50
|
+
the hidden glyph is faded, shrunk, and rotated off its axis; the active
|
|
51
|
+
one springs to full size at 0deg. Centring is done with margins (above),
|
|
52
|
+
so `transform` is free to drive the rotate/scale animation. */
|
|
53
|
+
.velu-theme-toggle__icon {
|
|
54
|
+
position: absolute;
|
|
55
|
+
inset-block-start: 50%;
|
|
56
|
+
inset-inline-start: 50%;
|
|
57
|
+
inline-size: 1.125rem;
|
|
58
|
+
block-size: 1.125rem;
|
|
59
|
+
margin-block-start: -0.5625rem; /* half of block-size */
|
|
60
|
+
margin-inline-start: -0.5625rem; /* half of inline-size */
|
|
61
|
+
color: var(--text-color);
|
|
62
|
+
opacity: 0;
|
|
63
|
+
transition: opacity 0.28s ease,
|
|
64
|
+
transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
65
|
+
pointer-events: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Hidden state per glyph — sun spins out clockwise, moon anti-clockwise,
|
|
69
|
+
so the swap reads as one rotating through the other. */
|
|
70
|
+
.velu-theme-toggle__icon--sun {
|
|
71
|
+
transform: rotate(90deg) scale(0.4);
|
|
72
|
+
}
|
|
73
|
+
.velu-theme-toggle__icon--moon {
|
|
74
|
+
transform: rotate(-90deg) scale(0.4);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Exactly one glyph is shown — the sun in light, the moon in dark — at full
|
|
78
|
+
size, upright. Higher specificity than the hidden defaults above, so it
|
|
79
|
+
wins for whichever glyph the active theme selects. */
|
|
80
|
+
:root:not([data-theme='dark']) .velu-theme-toggle__icon--sun,
|
|
81
|
+
[data-theme='dark'] .velu-theme-toggle__icon--moon {
|
|
82
|
+
opacity: 1;
|
|
83
|
+
transform: rotate(0deg) scale(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Honour reduced-motion: keep a plain crossfade — no spin, scale, or press. */
|
|
87
|
+
@media (prefers-reduced-motion: reduce) {
|
|
88
|
+
.velu-theme-toggle {
|
|
89
|
+
transition: border-color 0.12s ease, background 0.12s ease;
|
|
90
|
+
}
|
|
91
|
+
.velu-theme-toggle:active {
|
|
92
|
+
transform: none;
|
|
93
|
+
}
|
|
94
|
+
.velu-theme-toggle__icon {
|
|
95
|
+
transition: opacity 0.2s ease;
|
|
96
|
+
}
|
|
97
|
+
.velu-theme-toggle__icon--sun,
|
|
98
|
+
.velu-theme-toggle__icon--moon {
|
|
99
|
+
transform: none;
|
|
100
|
+
}
|
|
101
|
+
}
|