@veluai/velu 0.2.25 → 0.2.27

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.
Files changed (114) hide show
  1. package/README.md +80 -80
  2. package/dist/cli.js +44 -44
  3. package/package.json +64 -64
  4. package/runtime/velu-ui/base.css +320 -320
  5. package/runtime/velu-ui/components/Accordion.jsx +64 -64
  6. package/runtime/velu-ui/components/ApiClient.jsx +207 -207
  7. package/runtime/velu-ui/components/ApiField.jsx +87 -87
  8. package/runtime/velu-ui/components/ApiPath.jsx +63 -63
  9. package/runtime/velu-ui/components/ApiReferencePage.jsx +384 -384
  10. package/runtime/velu-ui/components/ApiSamples.jsx +36 -36
  11. package/runtime/velu-ui/components/ApiSidebar.jsx +125 -125
  12. package/runtime/velu-ui/components/AskBar.jsx +71 -71
  13. package/runtime/velu-ui/components/Callout.jsx +114 -114
  14. package/runtime/velu-ui/components/Card.jsx +131 -131
  15. package/runtime/velu-ui/components/ChangelogFilters.jsx +75 -75
  16. package/runtime/velu-ui/components/Chatbot.jsx +972 -972
  17. package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
  18. package/runtime/velu-ui/components/Columns.jsx +56 -56
  19. package/runtime/velu-ui/components/ContextMenu.jsx +327 -327
  20. package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
  21. package/runtime/velu-ui/components/Field.jsx +81 -81
  22. package/runtime/velu-ui/components/Image.jsx +163 -163
  23. package/runtime/velu-ui/components/Logo.jsx +31 -31
  24. package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
  25. package/runtime/velu-ui/components/NavSelect.jsx +108 -108
  26. package/runtime/velu-ui/components/NotFound.jsx +63 -63
  27. package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
  28. package/runtime/velu-ui/components/PageFooter.jsx +145 -145
  29. package/runtime/velu-ui/components/PageHeader.jsx +422 -422
  30. package/runtime/velu-ui/components/PageNav.jsx +77 -77
  31. package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
  32. package/runtime/velu-ui/components/Prompt.jsx +115 -115
  33. package/runtime/velu-ui/components/Search.jsx +460 -460
  34. package/runtime/velu-ui/components/Sidebar.jsx +246 -246
  35. package/runtime/velu-ui/components/SocialLinks.jsx +90 -90
  36. package/runtime/velu-ui/components/Steps.jsx +65 -65
  37. package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
  38. package/runtime/velu-ui/components/Toc.jsx +537 -537
  39. package/runtime/velu-ui/components/TocBar.jsx +195 -195
  40. package/runtime/velu-ui/components/Tree.jsx +87 -87
  41. package/runtime/velu-ui/components/TryItBar.jsx +102 -102
  42. package/runtime/velu-ui/components/Update.jsx +92 -92
  43. package/runtime/velu-ui/components/accordion.css +92 -92
  44. package/runtime/velu-ui/components/api-page.css +208 -208
  45. package/runtime/velu-ui/components/api.css +635 -635
  46. package/runtime/velu-ui/components/ask-bar.css +103 -103
  47. package/runtime/velu-ui/components/card.css +105 -105
  48. package/runtime/velu-ui/components/changelog-filters.css +43 -43
  49. package/runtime/velu-ui/components/chatbot.css +687 -687
  50. package/runtime/velu-ui/components/code-block.css +263 -263
  51. package/runtime/velu-ui/components/context-menu.css +199 -199
  52. package/runtime/velu-ui/components/docs-layout.css +822 -822
  53. package/runtime/velu-ui/components/field.css +82 -82
  54. package/runtime/velu-ui/components/image.css +237 -237
  55. package/runtime/velu-ui/components/nav-select.css +157 -157
  56. package/runtime/velu-ui/components/not-found.css +94 -94
  57. package/runtime/velu-ui/components/page-feedback.css +261 -261
  58. package/runtime/velu-ui/components/page-footer.css +130 -130
  59. package/runtime/velu-ui/components/page-header.css +558 -558
  60. package/runtime/velu-ui/components/page-nav.css +50 -50
  61. package/runtime/velu-ui/components/powered-by.css +92 -92
  62. package/runtime/velu-ui/components/prompt.css +99 -99
  63. package/runtime/velu-ui/components/search.css +307 -307
  64. package/runtime/velu-ui/components/sidebar.css +205 -205
  65. package/runtime/velu-ui/components/steps.css +77 -77
  66. package/runtime/velu-ui/components/theme-toggle.css +102 -102
  67. package/runtime/velu-ui/components/toc-bar.css +234 -234
  68. package/runtime/velu-ui/components/tree.css +49 -49
  69. package/runtime/velu-ui/components/update.css +111 -111
  70. package/runtime/velu-ui/index.js +58 -58
  71. package/runtime/velu-ui/lib/api-send.js +118 -118
  72. package/runtime/velu-ui/lib/brand-icons.jsx +103 -103
  73. package/runtime/velu-ui/lib/component-schemas.js +100 -100
  74. package/runtime/velu-ui/lib/copyText.js +64 -64
  75. package/runtime/velu-ui/lib/docs-assistant.js +273 -273
  76. package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
  77. package/runtime/velu-ui/lib/page-feedback.js +72 -72
  78. package/runtime/velu-ui/lib/pagefind.js +116 -116
  79. package/runtime/velu-ui/lib/prism-langs.js +957 -957
  80. package/runtime/velu-ui/lib/prism-loader.js +74 -74
  81. package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
  82. package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
  83. package/runtime/velu-ui/mdx-components.jsx +107 -107
  84. package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
  85. package/runtime/velu-ui/primitives/Stack.jsx +63 -63
  86. package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
  87. package/runtime/velu-ui/primitives/stack.css +3 -3
  88. package/runtime/velu-ui/primitives/switcher.css +25 -25
  89. package/runtime/velu-ui/styles.css +48 -48
  90. package/runtime/velu-ui/tokens.css +4 -4
  91. package/schema/velu.schema.json +419 -419
  92. package/src/lib/extract-mdx-error.js +170 -170
  93. package/src/lib/issues.js +159 -159
  94. package/src/lib/known-components.js +35 -35
  95. package/src/navigation.js +459 -459
  96. package/src/runtime/App.jsx +1754 -1754
  97. package/src/runtime/ErrorBoundary.jsx +54 -54
  98. package/src/runtime/client-entry.jsx +27 -27
  99. package/src/runtime/server-entry.jsx +24 -24
  100. package/src/template.html +48 -48
  101. package/templates/starter/ai-tools/claude-code.mdx +26 -26
  102. package/templates/starter/ai-tools/cursor.mdx +17 -17
  103. package/templates/starter/api-reference/introduction.mdx +43 -43
  104. package/templates/starter/development.mdx +19 -19
  105. package/templates/starter/essentials/code.mdx +29 -29
  106. package/templates/starter/essentials/images.mdx +29 -29
  107. package/templates/starter/essentials/markdown.mdx +25 -25
  108. package/templates/starter/essentials/navigation.mdx +39 -39
  109. package/templates/starter/essentials/settings.mdx +30 -30
  110. package/templates/starter/favicon.svg +6 -6
  111. package/templates/starter/index.mdx +31 -31
  112. package/templates/starter/openapi.json +160 -160
  113. package/templates/starter/quickstart.mdx +31 -31
  114. package/templates/starter/velu.json +40 -40
@@ -1,82 +1,82 @@
1
- /* Field — parameter docs row. All values are tokens; light/dark via
2
- [data-theme]. The chip / pill recipes are kept simple so they read
3
- identically against both `--surface-color` and `--page-bg`. */
4
-
5
- .velu-field {
6
- display: block;
7
- }
8
-
9
- /* Header row of chips + name + type + required + default + post.
10
- flex-wrap so a long set of badges falls cleanly onto a second line
11
- on narrow widths. Items align on text baseline so the bold `name`
12
- never visually shifts when chips have different padding. */
13
- /* The chip row layout lives in <Cluster> now (primitives/Cluster.jsx) —
14
- no .velu-field__head selector needed. */
15
-
16
- /* Generic chip — used for pre / type / post. Hugs the line-height
17
- vertically (zero block padding) with 8px / --s-3 horizontal padding;
18
- reads as a tiny code-like badge. */
19
- .velu-field__chip {
20
- display: inline-flex;
21
- align-items: center;
22
- padding-block: 0;
23
- padding-inline: var(--s-3);
24
- background: var(--surface-color);
25
- border-radius: var(--radius-sm);
26
- font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
27
- font-size: var(--f-h6);
28
- line-height: var(--lh-h6);
29
- color: var(--muted-color);
30
- }
31
-
32
- /* The parameter name — accent, medium weight, slightly larger than the
33
- chips so it reads as the row's primary token. */
34
- .velu-field__name {
35
- font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
36
- font-size: var(--f-h5);
37
- line-height: var(--lh-h5);
38
- font-weight: var(--weight-medium);
39
- color: var(--accent-color);
40
- }
41
-
42
- /* Required pill — accent-tinted background + accent text. Same hugging
43
- line-height as the generic chip. */
44
- .velu-field__required {
45
- display: inline-flex;
46
- align-items: center;
47
- padding-block: 0;
48
- padding-inline: var(--s-3);
49
- background: color-mix(in srgb, var(--accent-color) 18%, transparent);
50
- border-radius: var(--radius-sm);
51
- font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
52
- font-size: var(--f-h6);
53
- line-height: var(--lh-h6);
54
- color: var(--accent-color);
55
- }
56
-
57
- /* default: VALUE — light italic Google Sans Code, regular text color
58
- (not muted). */
59
- .velu-field__default {
60
- font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
61
- font-style: italic;
62
- font-weight: var(--weight-light);
63
- font-size: var(--f-h6);
64
- line-height: var(--lh-h6);
65
- color: var(--text-color);
66
- }
67
-
68
- /* Description block beneath the chip row. */
69
- .velu-field__body {
70
- margin-block-start: var(--s-1);
71
- color: var(--text-color);
72
- font-size: var(--f-body);
73
- line-height: var(--lh-body);
74
- }
75
-
76
- /* Sibling separator — drop two <Field> next to each other and the
77
- hairline divider appears between them. No wrapper component needed. */
78
- .velu-field + .velu-field {
79
- margin-block-start: var(--s2);
80
- padding-block-start: var(--s2);
81
- border-block-start: var(--border-width) solid var(--border-color);
82
- }
1
+ /* Field — parameter docs row. All values are tokens; light/dark via
2
+ [data-theme]. The chip / pill recipes are kept simple so they read
3
+ identically against both `--surface-color` and `--page-bg`. */
4
+
5
+ .velu-field {
6
+ display: block;
7
+ }
8
+
9
+ /* Header row of chips + name + type + required + default + post.
10
+ flex-wrap so a long set of badges falls cleanly onto a second line
11
+ on narrow widths. Items align on text baseline so the bold `name`
12
+ never visually shifts when chips have different padding. */
13
+ /* The chip row layout lives in <Cluster> now (primitives/Cluster.jsx) —
14
+ no .velu-field__head selector needed. */
15
+
16
+ /* Generic chip — used for pre / type / post. Hugs the line-height
17
+ vertically (zero block padding) with 8px / --s-3 horizontal padding;
18
+ reads as a tiny code-like badge. */
19
+ .velu-field__chip {
20
+ display: inline-flex;
21
+ align-items: center;
22
+ padding-block: 0;
23
+ padding-inline: var(--s-3);
24
+ background: var(--surface-color);
25
+ border-radius: var(--radius-sm);
26
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
27
+ font-size: var(--f-h6);
28
+ line-height: var(--lh-h6);
29
+ color: var(--muted-color);
30
+ }
31
+
32
+ /* The parameter name — accent, medium weight, slightly larger than the
33
+ chips so it reads as the row's primary token. */
34
+ .velu-field__name {
35
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
36
+ font-size: var(--f-h5);
37
+ line-height: var(--lh-h5);
38
+ font-weight: var(--weight-medium);
39
+ color: var(--accent-color);
40
+ }
41
+
42
+ /* Required pill — accent-tinted background + accent text. Same hugging
43
+ line-height as the generic chip. */
44
+ .velu-field__required {
45
+ display: inline-flex;
46
+ align-items: center;
47
+ padding-block: 0;
48
+ padding-inline: var(--s-3);
49
+ background: color-mix(in srgb, var(--accent-color) 18%, transparent);
50
+ border-radius: var(--radius-sm);
51
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
52
+ font-size: var(--f-h6);
53
+ line-height: var(--lh-h6);
54
+ color: var(--accent-color);
55
+ }
56
+
57
+ /* default: VALUE — light italic Google Sans Code, regular text color
58
+ (not muted). */
59
+ .velu-field__default {
60
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
61
+ font-style: italic;
62
+ font-weight: var(--weight-light);
63
+ font-size: var(--f-h6);
64
+ line-height: var(--lh-h6);
65
+ color: var(--text-color);
66
+ }
67
+
68
+ /* Description block beneath the chip row. */
69
+ .velu-field__body {
70
+ margin-block-start: var(--s-1);
71
+ color: var(--text-color);
72
+ font-size: var(--f-body);
73
+ line-height: var(--lh-body);
74
+ }
75
+
76
+ /* Sibling separator — drop two <Field> next to each other and the
77
+ hairline divider appears between them. No wrapper component needed. */
78
+ .velu-field + .velu-field {
79
+ margin-block-start: var(--s2);
80
+ padding-block-start: var(--s2);
81
+ border-block-start: var(--border-width) solid var(--border-color);
82
+ }
@@ -1,237 +1,237 @@
1
- /* Image — plain / captioned / window-chrome / device-frame variants.
2
- Tokenized; light/dark via [data-theme]. The window + frame variants share
3
- an accent-glow + noise backdrop, plus the liquid-glass layered surface
4
- (effect/tint/shine/content) for chrome bar + device bezel — SVG
5
- feTurbulence→feDisplacementMap provides the actual refraction. */
6
-
7
- .velu-image {
8
- display: block;
9
- margin: 0; /* reset <figure> default margin */
10
- }
11
- /* Chrome variants: figure hugs the contained card so the caption
12
- naturally centers under it instead of under the full content column. */
13
- .velu-image--chrome {
14
- inline-size: fit-content;
15
- margin-inline: auto;
16
- }
17
-
18
- .velu-image__img {
19
- display: block;
20
- inline-size: 100%;
21
- block-size: auto;
22
- border-radius: var(--radius-md);
23
- object-fit: cover;
24
- }
25
- .velu-image__img--flush {
26
- border-radius: 0;
27
- }
28
-
29
- .velu-image__caption {
30
- margin-block-start: var(--s-1);
31
- text-align: center;
32
- font-size: var(--f-h6);
33
- line-height: var(--lh-h6);
34
- color: var(--muted-color);
35
- }
36
-
37
- /* ── Accent glow + noise grain (shared by window + frame) ────────────────── */
38
- /* Big soft blob peeking from outside the container. The position uses
39
- negative insets so the blob's center is outside; overflow:hidden on
40
- the parent clips it to a quarter visible. */
41
- .velu-image__glow {
42
- position: absolute;
43
- inset-block-start: calc(var(--s4) * -1);
44
- inset-inline-start: calc(var(--s4) * -1);
45
- inline-size: calc(var(--s5) * 5);
46
- block-size: calc(var(--s5) * 5);
47
- background: var(--accent-color);
48
- border-radius: 50%;
49
- filter: blur(var(--s3));
50
- opacity: 0.95;
51
- pointer-events: none;
52
- z-index: 0;
53
- }
54
- .velu-image__noise {
55
- position: absolute;
56
- inset: 0;
57
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
58
- opacity: 0.6;
59
- mix-blend-mode: overlay;
60
- pointer-events: none;
61
- z-index: 0;
62
- }
63
-
64
- /* ── Liquid-glass surface (lucasromerodb pattern) ────────────────────────── */
65
- /* Four-layer stack: effect (backdrop-blur + SVG displacement filter),
66
- tint (translucent white), shine (inset highlights), content (children).
67
- Stays usable across themes — tint+shine are universal optical constants
68
- (white), not palette tokens. */
69
- .velu-glass {
70
- position: relative;
71
- display: flex;
72
- /* No overflow:hidden — each inner layer rounds itself via
73
- `border-radius: inherit`, so we don't need to clip at the parent.
74
- Skipping the clip lets children's box-shadows (e.g. the raised
75
- screen inside the bezel) extend outside the glass surface. */
76
- box-shadow:
77
- 0 var(--s-1) var(--s-1) color-mix(in srgb, #000 20%, transparent),
78
- 0 0 var(--s1) color-mix(in srgb, #000 10%, transparent);
79
- }
80
- /* Each layer rounds itself rather than relying on parent overflow:hidden —
81
- Chrome doesn't reliably clip backdrop-filter / inset-box-shadow to a
82
- rounded parent, which produces 90° corners poking through curves. */
83
- .velu-glass__effect,
84
- .velu-glass__tint,
85
- .velu-glass__shine,
86
- .velu-glass__content {
87
- border-radius: inherit;
88
- }
89
- /* Displacement must be applied to the BACKDROP (what's behind the element),
90
- not to the element itself. `filter: url()` would apply to the (empty)
91
- effect div; `backdrop-filter: url()` refracts the actual backdrop.
92
- Chromium-only; Safari/Firefox fall back to plain blur. */
93
- .velu-glass__effect {
94
- position: absolute;
95
- inset: 0;
96
- z-index: 0;
97
- -webkit-backdrop-filter: blur(3px) url(#velu-glass-distortion);
98
- backdrop-filter: blur(3px) url(#velu-glass-distortion);
99
- isolation: isolate;
100
- }
101
- .velu-glass__tint {
102
- position: absolute;
103
- inset: 0;
104
- z-index: 1;
105
- background: rgba(255, 255, 255, 0.25);
106
- }
107
- .velu-glass__shine {
108
- position: absolute;
109
- inset: 0;
110
- z-index: 2;
111
- box-shadow:
112
- inset 2px 2px 1px 0 rgba(255, 255, 255, 0.5),
113
- inset -1px -1px 1px 1px rgba(255, 255, 255, 0.5);
114
- }
115
- .velu-glass__content {
116
- position: relative;
117
- z-index: 3;
118
- inline-size: 100%;
119
- }
120
-
121
- /* ── Window-chrome variant ──────────────────────────────────────────────── */
122
- /* Outer = grey card. Nested radii are properly concentric:
123
- outer_radius ▸ surface_radius (= outer - outer_padding) ▸
124
- inner_radius (= surface - surface_padding). Padding is kept small
125
- enough that every level's curve stays positive. */
126
- .velu-image__window {
127
- position: relative;
128
- padding: var(--s-1);
129
- border-radius: var(--radius-lg);
130
- overflow: hidden;
131
- background: var(--surface-color);
132
- max-inline-size: 36rem;
133
- margin-inline: auto;
134
- }
135
- /* Glass surface (tray) that the chrome+image card sits on. Peeks out
136
- around the inner card as a visible glass border.
137
- radius = outer_radius - outer_padding (concentric with outer card). */
138
- .velu-image__window-surface {
139
- position: relative;
140
- z-index: 1;
141
- inline-size: 100%;
142
- border-radius: calc(var(--radius-lg) - var(--s-1));
143
- }
144
- .velu-image__window-surface > .velu-glass__content {
145
- padding: var(--s-2);
146
- }
147
- /* Inner card holds the chrome bar + image.
148
- radius = surface_radius - surface_padding (concentric with surface).
149
- A soft drop-shadow lifts it off the glass tray. */
150
- .velu-image__window-inner {
151
- position: relative;
152
- inline-size: 100%;
153
- border-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
154
- overflow: hidden;
155
- box-shadow:
156
- 0 var(--s-2) var(--s-1) color-mix(in srgb, #000 18%, transparent),
157
- 0 var(--s-4) var(--s-3) color-mix(in srgb, #000 12%, transparent);
158
- }
159
- /* Chrome bar sits ABOVE the image (in flow, no overlap). The image
160
- renders below it. Chrome is glass-styled via the Glass wrapper.
161
- Top corners match the window's radius; bottom stays flat where it
162
- meets the image. Inner glass layers inherit via `border-radius:
163
- inherit`. */
164
- .velu-image__chrome {
165
- border-start-start-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
166
- border-start-end-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
167
- /* The default `.velu-glass` drop-shadow casts a dark line onto the
168
- image below — undesired here since the chrome meets the image
169
- directly. The bezel keeps its shadow because it sits in a padded
170
- grey card that absorbs it. */
171
- box-shadow: none;
172
- }
173
- .velu-image__chrome > .velu-glass__content {
174
- display: flex;
175
- align-items: center;
176
- gap: var(--s-1);
177
- padding: var(--s-1);
178
- }
179
- .velu-image__dots {
180
- display: inline-flex;
181
- gap: var(--s-3);
182
- padding-inline: var(--s-3);
183
- }
184
- .velu-image__dot {
185
- inline-size: 0.75em;
186
- block-size: 0.75em;
187
- border-radius: 50%;
188
- }
189
- .velu-image__dot--red { background: #ff5f57; }
190
- .velu-image__dot--yellow { background: #febc2e; }
191
- .velu-image__dot--green { background: #28c840; }
192
- /* URL bar is absolutely centered in the chrome regardless of the dots'
193
- width on the left — the .velu-glass__content already has
194
- position: relative, so this anchors to that. */
195
- .velu-image__urlbar {
196
- position: absolute;
197
- inset-inline-start: 50%;
198
- transform: translateX(-50%);
199
- inline-size: 15%;
200
- block-size: 0.5em;
201
- border-radius: 999px;
202
- background: color-mix(in srgb, var(--text-color) 14%, transparent);
203
- }
204
-
205
- /* ── Device-frame variant ───────────────────────────────────────────────── */
206
- /* Outer = grey card. Concentric nesting: outer → bezel → screen, each
207
- radius = parent_radius - padding so all corner curves align. */
208
- .velu-image__frame {
209
- position: relative;
210
- display: flex;
211
- justify-content: center;
212
- padding: var(--s-1);
213
- border-radius: var(--radius-lg);
214
- overflow: hidden;
215
- background: var(--surface-color);
216
- max-inline-size: 18rem;
217
- margin-inline: auto;
218
- }
219
- /* Bezel: concentric with outer card. */
220
- .velu-image__frame-bezel {
221
- position: relative;
222
- z-index: 1;
223
- inline-size: 13rem;
224
- max-inline-size: 100%;
225
- border-radius: calc(var(--radius-lg) - var(--s-1));
226
- }
227
- .velu-image__frame-bezel > .velu-glass__content {
228
- padding: var(--s-2);
229
- }
230
- /* Screen: concentric with bezel. Soft drop-shadow lifts it off the bezel. */
231
- .velu-image__frame-screen {
232
- border-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
233
- overflow: hidden;
234
- box-shadow:
235
- 0 var(--s-2) var(--s-1) color-mix(in srgb, #000 18%, transparent),
236
- 0 var(--s-4) var(--s-3) color-mix(in srgb, #000 12%, transparent);
237
- }
1
+ /* Image — plain / captioned / window-chrome / device-frame variants.
2
+ Tokenized; light/dark via [data-theme]. The window + frame variants share
3
+ an accent-glow + noise backdrop, plus the liquid-glass layered surface
4
+ (effect/tint/shine/content) for chrome bar + device bezel — SVG
5
+ feTurbulence→feDisplacementMap provides the actual refraction. */
6
+
7
+ .velu-image {
8
+ display: block;
9
+ margin: 0; /* reset <figure> default margin */
10
+ }
11
+ /* Chrome variants: figure hugs the contained card so the caption
12
+ naturally centers under it instead of under the full content column. */
13
+ .velu-image--chrome {
14
+ inline-size: fit-content;
15
+ margin-inline: auto;
16
+ }
17
+
18
+ .velu-image__img {
19
+ display: block;
20
+ inline-size: 100%;
21
+ block-size: auto;
22
+ border-radius: var(--radius-md);
23
+ object-fit: cover;
24
+ }
25
+ .velu-image__img--flush {
26
+ border-radius: 0;
27
+ }
28
+
29
+ .velu-image__caption {
30
+ margin-block-start: var(--s-1);
31
+ text-align: center;
32
+ font-size: var(--f-h6);
33
+ line-height: var(--lh-h6);
34
+ color: var(--muted-color);
35
+ }
36
+
37
+ /* ── Accent glow + noise grain (shared by window + frame) ────────────────── */
38
+ /* Big soft blob peeking from outside the container. The position uses
39
+ negative insets so the blob's center is outside; overflow:hidden on
40
+ the parent clips it to a quarter visible. */
41
+ .velu-image__glow {
42
+ position: absolute;
43
+ inset-block-start: calc(var(--s4) * -1);
44
+ inset-inline-start: calc(var(--s4) * -1);
45
+ inline-size: calc(var(--s5) * 5);
46
+ block-size: calc(var(--s5) * 5);
47
+ background: var(--accent-color);
48
+ border-radius: 50%;
49
+ filter: blur(var(--s3));
50
+ opacity: 0.95;
51
+ pointer-events: none;
52
+ z-index: 0;
53
+ }
54
+ .velu-image__noise {
55
+ position: absolute;
56
+ inset: 0;
57
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
58
+ opacity: 0.6;
59
+ mix-blend-mode: overlay;
60
+ pointer-events: none;
61
+ z-index: 0;
62
+ }
63
+
64
+ /* ── Liquid-glass surface (lucasromerodb pattern) ────────────────────────── */
65
+ /* Four-layer stack: effect (backdrop-blur + SVG displacement filter),
66
+ tint (translucent white), shine (inset highlights), content (children).
67
+ Stays usable across themes — tint+shine are universal optical constants
68
+ (white), not palette tokens. */
69
+ .velu-glass {
70
+ position: relative;
71
+ display: flex;
72
+ /* No overflow:hidden — each inner layer rounds itself via
73
+ `border-radius: inherit`, so we don't need to clip at the parent.
74
+ Skipping the clip lets children's box-shadows (e.g. the raised
75
+ screen inside the bezel) extend outside the glass surface. */
76
+ box-shadow:
77
+ 0 var(--s-1) var(--s-1) color-mix(in srgb, #000 20%, transparent),
78
+ 0 0 var(--s1) color-mix(in srgb, #000 10%, transparent);
79
+ }
80
+ /* Each layer rounds itself rather than relying on parent overflow:hidden —
81
+ Chrome doesn't reliably clip backdrop-filter / inset-box-shadow to a
82
+ rounded parent, which produces 90° corners poking through curves. */
83
+ .velu-glass__effect,
84
+ .velu-glass__tint,
85
+ .velu-glass__shine,
86
+ .velu-glass__content {
87
+ border-radius: inherit;
88
+ }
89
+ /* Displacement must be applied to the BACKDROP (what's behind the element),
90
+ not to the element itself. `filter: url()` would apply to the (empty)
91
+ effect div; `backdrop-filter: url()` refracts the actual backdrop.
92
+ Chromium-only; Safari/Firefox fall back to plain blur. */
93
+ .velu-glass__effect {
94
+ position: absolute;
95
+ inset: 0;
96
+ z-index: 0;
97
+ -webkit-backdrop-filter: blur(3px) url(#velu-glass-distortion);
98
+ backdrop-filter: blur(3px) url(#velu-glass-distortion);
99
+ isolation: isolate;
100
+ }
101
+ .velu-glass__tint {
102
+ position: absolute;
103
+ inset: 0;
104
+ z-index: 1;
105
+ background: rgba(255, 255, 255, 0.25);
106
+ }
107
+ .velu-glass__shine {
108
+ position: absolute;
109
+ inset: 0;
110
+ z-index: 2;
111
+ box-shadow:
112
+ inset 2px 2px 1px 0 rgba(255, 255, 255, 0.5),
113
+ inset -1px -1px 1px 1px rgba(255, 255, 255, 0.5);
114
+ }
115
+ .velu-glass__content {
116
+ position: relative;
117
+ z-index: 3;
118
+ inline-size: 100%;
119
+ }
120
+
121
+ /* ── Window-chrome variant ──────────────────────────────────────────────── */
122
+ /* Outer = grey card. Nested radii are properly concentric:
123
+ outer_radius ▸ surface_radius (= outer - outer_padding) ▸
124
+ inner_radius (= surface - surface_padding). Padding is kept small
125
+ enough that every level's curve stays positive. */
126
+ .velu-image__window {
127
+ position: relative;
128
+ padding: var(--s-1);
129
+ border-radius: var(--radius-lg);
130
+ overflow: hidden;
131
+ background: var(--surface-color);
132
+ max-inline-size: 36rem;
133
+ margin-inline: auto;
134
+ }
135
+ /* Glass surface (tray) that the chrome+image card sits on. Peeks out
136
+ around the inner card as a visible glass border.
137
+ radius = outer_radius - outer_padding (concentric with outer card). */
138
+ .velu-image__window-surface {
139
+ position: relative;
140
+ z-index: 1;
141
+ inline-size: 100%;
142
+ border-radius: calc(var(--radius-lg) - var(--s-1));
143
+ }
144
+ .velu-image__window-surface > .velu-glass__content {
145
+ padding: var(--s-2);
146
+ }
147
+ /* Inner card holds the chrome bar + image.
148
+ radius = surface_radius - surface_padding (concentric with surface).
149
+ A soft drop-shadow lifts it off the glass tray. */
150
+ .velu-image__window-inner {
151
+ position: relative;
152
+ inline-size: 100%;
153
+ border-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
154
+ overflow: hidden;
155
+ box-shadow:
156
+ 0 var(--s-2) var(--s-1) color-mix(in srgb, #000 18%, transparent),
157
+ 0 var(--s-4) var(--s-3) color-mix(in srgb, #000 12%, transparent);
158
+ }
159
+ /* Chrome bar sits ABOVE the image (in flow, no overlap). The image
160
+ renders below it. Chrome is glass-styled via the Glass wrapper.
161
+ Top corners match the window's radius; bottom stays flat where it
162
+ meets the image. Inner glass layers inherit via `border-radius:
163
+ inherit`. */
164
+ .velu-image__chrome {
165
+ border-start-start-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
166
+ border-start-end-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
167
+ /* The default `.velu-glass` drop-shadow casts a dark line onto the
168
+ image below — undesired here since the chrome meets the image
169
+ directly. The bezel keeps its shadow because it sits in a padded
170
+ grey card that absorbs it. */
171
+ box-shadow: none;
172
+ }
173
+ .velu-image__chrome > .velu-glass__content {
174
+ display: flex;
175
+ align-items: center;
176
+ gap: var(--s-1);
177
+ padding: var(--s-1);
178
+ }
179
+ .velu-image__dots {
180
+ display: inline-flex;
181
+ gap: var(--s-3);
182
+ padding-inline: var(--s-3);
183
+ }
184
+ .velu-image__dot {
185
+ inline-size: 0.75em;
186
+ block-size: 0.75em;
187
+ border-radius: 50%;
188
+ }
189
+ .velu-image__dot--red { background: #ff5f57; }
190
+ .velu-image__dot--yellow { background: #febc2e; }
191
+ .velu-image__dot--green { background: #28c840; }
192
+ /* URL bar is absolutely centered in the chrome regardless of the dots'
193
+ width on the left — the .velu-glass__content already has
194
+ position: relative, so this anchors to that. */
195
+ .velu-image__urlbar {
196
+ position: absolute;
197
+ inset-inline-start: 50%;
198
+ transform: translateX(-50%);
199
+ inline-size: 15%;
200
+ block-size: 0.5em;
201
+ border-radius: 999px;
202
+ background: color-mix(in srgb, var(--text-color) 14%, transparent);
203
+ }
204
+
205
+ /* ── Device-frame variant ───────────────────────────────────────────────── */
206
+ /* Outer = grey card. Concentric nesting: outer → bezel → screen, each
207
+ radius = parent_radius - padding so all corner curves align. */
208
+ .velu-image__frame {
209
+ position: relative;
210
+ display: flex;
211
+ justify-content: center;
212
+ padding: var(--s-1);
213
+ border-radius: var(--radius-lg);
214
+ overflow: hidden;
215
+ background: var(--surface-color);
216
+ max-inline-size: 18rem;
217
+ margin-inline: auto;
218
+ }
219
+ /* Bezel: concentric with outer card. */
220
+ .velu-image__frame-bezel {
221
+ position: relative;
222
+ z-index: 1;
223
+ inline-size: 13rem;
224
+ max-inline-size: 100%;
225
+ border-radius: calc(var(--radius-lg) - var(--s-1));
226
+ }
227
+ .velu-image__frame-bezel > .velu-glass__content {
228
+ padding: var(--s-2);
229
+ }
230
+ /* Screen: concentric with bezel. Soft drop-shadow lifts it off the bezel. */
231
+ .velu-image__frame-screen {
232
+ border-radius: calc(var(--radius-lg) - var(--s-1) - var(--s-2));
233
+ overflow: hidden;
234
+ box-shadow:
235
+ 0 var(--s-2) var(--s-1) color-mix(in srgb, #000 18%, transparent),
236
+ 0 var(--s-4) var(--s-3) color-mix(in srgb, #000 12%, transparent);
237
+ }