@octane-xplat/ui 0.3.0 → 0.5.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/README.md +46 -0
- package/dist/native/octane-client-build.json +1 -1
- package/dist/native/ui.js +55 -40
- package/dist/native/ui10.js +20 -11
- package/dist/native/ui11.js +42 -109
- package/dist/native/ui12.js +17 -84
- package/dist/native/ui13.js +11 -41
- package/dist/native/ui14.js +133 -76
- package/dist/native/ui15.js +43 -92
- package/dist/native/ui16.js +106 -45
- package/dist/native/ui17.js +29 -42
- package/dist/native/ui18.js +34 -53
- package/dist/native/ui19.js +246 -32
- package/dist/native/ui20.js +27 -16
- package/dist/native/ui21.js +376 -86
- package/dist/native/ui22.js +26 -114
- package/dist/native/ui23.js +49 -97
- package/dist/native/ui24.js +79 -53
- package/dist/native/ui25.js +138 -16
- package/dist/native/ui26.js +8 -23
- package/dist/native/ui27.js +60 -34
- package/dist/native/ui28.js +36 -142
- package/dist/native/ui29.js +101 -21
- package/dist/native/ui3.js +22 -1
- package/dist/native/ui30.js +14 -31
- package/dist/native/ui31.js +55 -36
- package/dist/native/ui32.js +49 -61
- package/dist/native/ui33.js +36 -28
- package/dist/native/ui34.js +51 -8
- package/dist/native/ui35.js +98 -20
- package/dist/native/ui36.js +9 -33
- package/dist/native/ui37.js +42 -15
- package/dist/native/ui38.js +186 -57
- package/dist/native/ui39.js +96 -26
- package/dist/native/ui4.js +22 -20
- package/dist/native/ui40.js +179 -8
- package/dist/native/ui41.js +52 -98
- package/dist/native/ui42.js +14 -145
- package/dist/native/ui43.js +16 -19
- package/dist/native/ui44.js +6 -6
- package/dist/native/ui45.js +38 -0
- package/dist/native/ui46.js +267 -0
- package/dist/native/ui47.js +28 -0
- package/dist/native/ui48.js +35 -0
- package/dist/native/ui49.js +41 -0
- package/dist/native/ui5.js +37 -77
- package/dist/native/ui50.js +72 -0
- package/dist/native/ui51.js +66 -0
- package/dist/native/ui52.js +64 -0
- package/dist/native/ui53.js +27 -0
- package/dist/native/ui54.js +13 -0
- package/dist/native/ui55.js +92 -0
- package/dist/native/ui56.js +34 -0
- package/dist/native/ui57.js +19 -0
- package/dist/native/ui58.js +156 -0
- package/dist/native/ui59.js +33 -0
- package/dist/native/ui6.js +94 -9
- package/dist/native/ui60.js +14 -0
- package/dist/native/ui61.js +22 -0
- package/dist/native/ui62.js +8 -0
- package/dist/native/ui63.js +108 -0
- package/dist/native/ui64.js +76 -0
- package/dist/native/ui7.js +42 -26
- package/dist/native/ui8.js +22 -12
- package/dist/native/ui9.js +18 -9
- package/dist/web/octane-client-build.json +1 -1
- package/dist/web/ui.js +53 -40
- package/dist/web/ui10.js +33 -186
- package/dist/web/ui11.js +28 -409
- package/dist/web/ui12.js +21 -68
- package/dist/web/ui13.js +265 -96
- package/dist/web/ui14.js +85 -95
- package/dist/web/ui15.js +368 -25
- package/dist/web/ui16.js +13 -101
- package/dist/web/ui17.js +246 -42
- package/dist/web/ui18.js +237 -28
- package/dist/web/ui19.js +36 -10
- package/dist/web/ui20.js +73 -102
- package/dist/web/ui21.js +102 -73
- package/dist/web/ui22.js +95 -55
- package/dist/web/ui23.js +94 -55
- package/dist/web/ui24.js +31 -15
- package/dist/web/ui25.js +105 -19
- package/dist/web/ui26.js +29 -23
- package/dist/web/ui27.js +12 -123
- package/dist/web/ui28.js +9 -110
- package/dist/web/ui29.js +52 -102
- package/dist/web/ui3.js +29 -18
- package/dist/web/ui30.js +32 -41
- package/dist/web/ui31.js +120 -39
- package/dist/web/ui32.js +41 -74
- package/dist/web/ui33.js +148 -32
- package/dist/web/ui34.js +145 -62
- package/dist/web/ui35.js +140 -7
- package/dist/web/ui36.js +86 -41
- package/dist/web/ui37.js +19 -44
- package/dist/web/ui38.js +5 -15
- package/dist/web/ui39.js +24 -86
- package/dist/web/ui4.js +46 -152
- package/dist/web/ui40.js +150 -37
- package/dist/web/ui41.js +36 -10
- package/dist/web/ui42.js +40 -7
- package/dist/web/ui43.js +73 -19
- package/dist/web/ui44.js +40 -6
- package/dist/web/ui45.js +62 -0
- package/dist/web/ui46.js +50 -0
- package/dist/web/ui47.js +9 -0
- package/dist/web/ui48.js +45 -0
- package/dist/web/ui49.js +13 -0
- package/dist/web/ui5.js +131 -27
- package/dist/web/ui50.js +63 -0
- package/dist/web/ui51.js +47 -0
- package/dist/web/ui52.js +19 -0
- package/dist/web/ui53.js +273 -0
- package/dist/web/ui54.js +42 -0
- package/dist/web/ui55.js +17 -0
- package/dist/web/ui56.js +9 -0
- package/dist/web/ui57.js +22 -0
- package/dist/web/ui58.js +8 -0
- package/dist/web/ui59.js +44 -0
- package/dist/web/ui6.js +81 -45
- package/dist/web/ui60.js +46 -0
- package/dist/web/ui7.js +43 -21
- package/dist/web/ui8.js +27 -21
- package/dist/web/ui9.js +29 -18
- package/package.json +17 -4
- package/src/theme/tokens.css +597 -141
- package/types/index.d.ts +112 -58
- package/types/props.d.ts +291 -16
package/src/theme/tokens.css
CHANGED
|
@@ -3,274 +3,730 @@
|
|
|
3
3
|
|
|
4
4
|
:root,
|
|
5
5
|
.ns-root {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
--color-primary: #171717;
|
|
7
|
+
/* Token names double as the future Tailwind theme keys: text-onprimary
|
|
8
|
+
resolves --color-onprimary, bg-danger resolves --color-danger. Keep
|
|
9
|
+
class-name segments and var-name segments identical. */
|
|
10
|
+
--color-onprimary: #fafafa;
|
|
11
|
+
--color-surface: #ffffff;
|
|
12
|
+
--color-surface-secondary: #fafafa;
|
|
13
|
+
--color-text: #0a0a0a;
|
|
14
|
+
--color-text-secondary: #666666;
|
|
15
|
+
--color-border: #eaeaea;
|
|
16
|
+
--color-border-strong: #d4d4d4;
|
|
17
|
+
--color-danger: #dc2626;
|
|
18
|
+
/* Family lists are deliberately generic. Apps override these with their
|
|
19
|
+
web @font-face family and the platform's registered native name. */
|
|
20
|
+
--font-sans: system-ui, sans-serif;
|
|
21
|
+
--font-mono: ui-monospace, monospace;
|
|
22
|
+
--space-4: 16px;
|
|
23
|
+
/* Corner scale — rounded-rectangle radii. Smooth (squircle) corners are
|
|
24
|
+
the default corner treatment: every class that consumes a token also
|
|
25
|
+
declares `corner-shape: squircle`. Full-radius circles opt out —
|
|
26
|
+
`corner-shape` would reshape them into squircle disks on iOS/Chromium. */
|
|
27
|
+
--radius-sm: 6px;
|
|
28
|
+
--radius-md: 8px;
|
|
29
|
+
--radius-lg: 12px;
|
|
30
|
+
--radius-xl: 16px;
|
|
11
31
|
}
|
|
12
32
|
|
|
13
33
|
.ns-dark,
|
|
14
34
|
.dark {
|
|
15
|
-
|
|
16
|
-
|
|
35
|
+
--color-primary: #ededed;
|
|
36
|
+
--color-onprimary: #171717;
|
|
37
|
+
--color-surface: #0a0a0a;
|
|
38
|
+
--color-surface-secondary: #141414;
|
|
39
|
+
--color-text: #ededed;
|
|
40
|
+
--color-text-secondary: #a1a1a1;
|
|
41
|
+
--color-border: #2e2e2e;
|
|
42
|
+
--color-border-strong: #4a4a4a;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* xplat-web-only:start — DOM normalization. NativeScript is the reference
|
|
46
|
+
box model: dip sizes are outer boxes, and a border paints from
|
|
47
|
+
width+color alone. The DOM's UA stylesheet diverges on both counts, so
|
|
48
|
+
normalize the deltas once instead of patching individual rules:
|
|
49
|
+
- border-box keeps padding/borders inside declared sizes (a 40dip box
|
|
50
|
+
is 40px, not 40px + padding + border).
|
|
51
|
+
- border-width 0 + border-style solid makes the DOM behave like native:
|
|
52
|
+
any shared border-*-width declaration paints on both targets — the
|
|
53
|
+
DOM's initial border-width is `medium`, so style alone would draw
|
|
54
|
+
phantom ~3px edges wherever a rule sets only one side's width.
|
|
55
|
+
- <img> is a leaf, not inline text (no baseline descender gap).
|
|
56
|
+
- .vx-app columns are flex like their native screen containers. */
|
|
57
|
+
*,
|
|
58
|
+
*::before,
|
|
59
|
+
*::after {
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
}
|
|
62
|
+
* {
|
|
63
|
+
border-width: 0;
|
|
64
|
+
border-style: solid;
|
|
65
|
+
}
|
|
66
|
+
img {
|
|
67
|
+
display: block;
|
|
17
68
|
}
|
|
69
|
+
.vx-app {
|
|
70
|
+
display: flex;
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
}
|
|
73
|
+
/* Native has no scroll chaining — a ScrollView/ListView just bounces
|
|
74
|
+
inside itself because the parent is never a scroller. contain is the
|
|
75
|
+
DOM equivalent: scroll stays in the leaf it started in. */
|
|
76
|
+
.vx-scroll,
|
|
77
|
+
.vx-list {
|
|
78
|
+
overscroll-behavior: contain;
|
|
79
|
+
}
|
|
80
|
+
/* xplat-web-only:end */
|
|
18
81
|
|
|
19
82
|
/* vocabulary base styles */
|
|
20
83
|
|
|
21
84
|
.vx-view,
|
|
22
85
|
.vx-row {
|
|
23
|
-
|
|
86
|
+
display: flex;
|
|
24
87
|
}
|
|
25
88
|
|
|
26
89
|
.vx-view {
|
|
27
|
-
|
|
90
|
+
flex-direction: column;
|
|
28
91
|
}
|
|
29
92
|
|
|
30
93
|
/* Safe-area values are zero when the viewport has no display cutout. */
|
|
31
94
|
.vx-safe-area {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
95
|
+
padding-top: constant(safe-area-inset-top);
|
|
96
|
+
padding-right: constant(safe-area-inset-right);
|
|
97
|
+
padding-bottom: constant(safe-area-inset-bottom);
|
|
98
|
+
padding-left: constant(safe-area-inset-left);
|
|
36
99
|
}
|
|
37
100
|
|
|
38
101
|
.vx-safe-area {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
102
|
+
padding-top: env(safe-area-inset-top);
|
|
103
|
+
padding-right: env(safe-area-inset-right);
|
|
104
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
105
|
+
padding-left: env(safe-area-inset-left);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.vx-keyboard-avoiding {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
}
|
|
112
|
+
/* xplat-web-only:start — the native Drawer leaf is the ui-drawer widget;
|
|
113
|
+
these rules style the web DOM drawer overlay */
|
|
114
|
+
.vx-drawer {
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: row;
|
|
117
|
+
position: relative;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
}
|
|
120
|
+
.vx-drawer-main {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex: 1;
|
|
123
|
+
min-width: 0;
|
|
124
|
+
}
|
|
125
|
+
.vx-drawer-backdrop {
|
|
126
|
+
position: absolute;
|
|
127
|
+
inset: 0;
|
|
128
|
+
background: rgba(0, 0, 0, 0.4);
|
|
43
129
|
}
|
|
44
|
-
|
|
45
|
-
.vx-keyboard-avoiding { display: flex; flex-direction: column; }
|
|
46
|
-
.vx-drawer { display: flex; flex-direction: row; position: relative; overflow: hidden; }
|
|
47
|
-
.vx-drawer-main { display: flex; flex: 1; min-width: 0; }
|
|
48
|
-
.vx-drawer-backdrop { position: absolute; inset: 0; background: rgba(15, 23, 42, 0.35); }
|
|
49
130
|
.vx-drawer-panel {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
131
|
+
position: absolute;
|
|
132
|
+
z-index: 1;
|
|
133
|
+
top: 0;
|
|
134
|
+
bottom: 0;
|
|
135
|
+
left: 0;
|
|
136
|
+
width: 280px;
|
|
137
|
+
overflow: auto;
|
|
138
|
+
background: var(--color-surface);
|
|
139
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
|
140
|
+
}
|
|
141
|
+
/* xplat-web-only:end */
|
|
60
142
|
|
|
61
143
|
.vx-row {
|
|
62
|
-
|
|
144
|
+
flex-direction: row;
|
|
63
145
|
}
|
|
64
146
|
|
|
65
147
|
.vx-grid,
|
|
66
148
|
.vx-stack {
|
|
67
|
-
|
|
149
|
+
display: grid;
|
|
68
150
|
}
|
|
69
151
|
|
|
70
152
|
.vx-stack > * {
|
|
71
|
-
|
|
153
|
+
grid-area: 1 / 1;
|
|
72
154
|
}
|
|
73
155
|
|
|
156
|
+
/* xplat-web-only:start — Absolute's native leaf is absolutelayout; CSS
|
|
157
|
+
positioning is web-only (children position via left/top props) */
|
|
74
158
|
.vx-absolute {
|
|
75
|
-
|
|
159
|
+
position: relative;
|
|
76
160
|
}
|
|
77
161
|
|
|
78
162
|
.vx-absolute > * {
|
|
79
|
-
|
|
163
|
+
position: absolute;
|
|
80
164
|
}
|
|
165
|
+
/* xplat-web-only:end */
|
|
81
166
|
|
|
82
167
|
.vx-spacer {
|
|
83
|
-
|
|
168
|
+
flex-grow: 1;
|
|
84
169
|
}
|
|
85
170
|
|
|
86
171
|
.vx-pressable {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
172
|
+
display: flex;
|
|
173
|
+
flex-direction: column;
|
|
174
|
+
user-select: none;
|
|
90
175
|
}
|
|
91
176
|
|
|
92
177
|
/* web press feedback */
|
|
93
178
|
.vx-pressable:active {
|
|
94
|
-
|
|
179
|
+
opacity: 0.85;
|
|
95
180
|
}
|
|
96
181
|
.vx-pressable-no-feedback:active {
|
|
97
|
-
|
|
182
|
+
opacity: 1;
|
|
98
183
|
}
|
|
99
184
|
/* native press feedback (NS pseudo — dead rule on web, and vice versa) */
|
|
100
185
|
.vx-pressable:pressed {
|
|
101
|
-
|
|
186
|
+
opacity: 0.85;
|
|
102
187
|
}
|
|
103
188
|
|
|
189
|
+
/* xplat-web-only:start — overlays/toasts/popovers render through RootLayout
|
|
190
|
+
or showModal on native; these portal/positioning rules are web-only */
|
|
104
191
|
.vx-overlay {
|
|
105
|
-
|
|
192
|
+
z-index: 1000;
|
|
106
193
|
}
|
|
107
194
|
|
|
108
195
|
.vx-overlay-shade {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
196
|
+
position: absolute;
|
|
197
|
+
inset: 0;
|
|
198
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
112
199
|
}
|
|
113
200
|
|
|
114
201
|
.vx-overlay-content {
|
|
115
|
-
|
|
116
|
-
|
|
202
|
+
position: relative;
|
|
203
|
+
z-index: 1;
|
|
117
204
|
}
|
|
118
205
|
|
|
119
206
|
.vx-popover {
|
|
120
|
-
|
|
207
|
+
z-index: 1001;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.vx-popover-backdrop {
|
|
211
|
+
background-color: transparent;
|
|
121
212
|
}
|
|
122
213
|
|
|
123
214
|
.vx-toast-host {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
215
|
+
position: fixed;
|
|
216
|
+
z-index: 1002;
|
|
217
|
+
left: 0;
|
|
218
|
+
right: 0;
|
|
219
|
+
display: flex;
|
|
220
|
+
justify-content: center;
|
|
221
|
+
pointer-events: none;
|
|
131
222
|
}
|
|
132
223
|
|
|
133
|
-
.vx-toast-top {
|
|
134
|
-
|
|
224
|
+
.vx-toast-top {
|
|
225
|
+
top: 24px;
|
|
226
|
+
}
|
|
227
|
+
.vx-toast-bottom {
|
|
228
|
+
bottom: 24px;
|
|
229
|
+
}
|
|
135
230
|
|
|
136
231
|
.vx-toast {
|
|
137
|
-
|
|
232
|
+
pointer-events: auto;
|
|
138
233
|
}
|
|
234
|
+
/* xplat-web-only:end */
|
|
139
235
|
|
|
140
236
|
/* auto-grow owns height — the manual resize handle has no native analog */
|
|
237
|
+
/* xplat-web-only:start */
|
|
141
238
|
.vx-textarea {
|
|
142
|
-
|
|
239
|
+
resize: none;
|
|
143
240
|
}
|
|
241
|
+
/* xplat-web-only:end */
|
|
144
242
|
|
|
145
243
|
/* Modal is a real top-layer dialog on web. Presentation variants keep their
|
|
146
244
|
sizing and anchoring in tokens so both the component and openModal share it. */
|
|
245
|
+
/* xplat-web-only:start */
|
|
147
246
|
.vx-modal {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
247
|
+
width: 480px;
|
|
248
|
+
max-width: 90%;
|
|
249
|
+
max-height: 90%;
|
|
250
|
+
padding: 24px;
|
|
251
|
+
border: 1px solid var(--color-border);
|
|
252
|
+
border-radius: var(--radius-xl);
|
|
253
|
+
corner-shape: squircle;
|
|
254
|
+
color: var(--color-text);
|
|
255
|
+
background-color: var(--color-surface);
|
|
156
256
|
}
|
|
157
257
|
|
|
158
258
|
.vx-modal::backdrop {
|
|
159
|
-
|
|
259
|
+
background: rgba(0, 0, 0, 0.4);
|
|
160
260
|
}
|
|
161
261
|
|
|
162
262
|
.vx-modal--dialog {
|
|
163
|
-
|
|
164
|
-
|
|
263
|
+
width: 480px;
|
|
264
|
+
max-width: 90%;
|
|
165
265
|
}
|
|
166
266
|
|
|
167
267
|
.vx-modal--sheet {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
268
|
+
width: 100%;
|
|
269
|
+
max-width: none;
|
|
270
|
+
max-height: 85%;
|
|
271
|
+
position: fixed;
|
|
272
|
+
inset: auto 0 0;
|
|
273
|
+
margin: 0;
|
|
274
|
+
border-top-left-radius: var(--radius-xl);
|
|
275
|
+
border-top-right-radius: var(--radius-xl);
|
|
276
|
+
corner-shape: squircle;
|
|
175
277
|
}
|
|
176
278
|
|
|
177
279
|
.vx-modal--fullscreen {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
280
|
+
width: 100%;
|
|
281
|
+
max-width: none;
|
|
282
|
+
height: 100%;
|
|
283
|
+
max-height: none;
|
|
284
|
+
margin: 0;
|
|
285
|
+
border-radius: 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Modal ROUTES (pushRoute presentation:'modal') — the URL stays underneath,
|
|
289
|
+
so the overlay is a fixed layer over the shell rather than a dialog. */
|
|
290
|
+
.vx-modalroute {
|
|
291
|
+
position: fixed;
|
|
292
|
+
inset: 0;
|
|
293
|
+
z-index: 1000;
|
|
294
|
+
display: flex;
|
|
295
|
+
align-items: stretch;
|
|
296
|
+
justify-content: stretch;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.vx-modalroute-shade {
|
|
300
|
+
position: absolute;
|
|
301
|
+
inset: 0;
|
|
302
|
+
border: 0;
|
|
303
|
+
padding: 0;
|
|
304
|
+
background: rgba(0, 0, 0, 0.4);
|
|
305
|
+
cursor: default;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.vx-modalroute-body {
|
|
309
|
+
position: relative;
|
|
310
|
+
flex: 1;
|
|
311
|
+
min-width: 0;
|
|
312
|
+
overflow: auto;
|
|
313
|
+
color: var(--color-text);
|
|
314
|
+
background-color: var(--color-surface);
|
|
315
|
+
}
|
|
316
|
+
/* xplat-web-only:end */
|
|
317
|
+
|
|
318
|
+
/* xplat-web-only:start — placeholder pseudos are DOM-only. Muted like
|
|
319
|
+
real placeholder text, not currentColor (a full-opacity hint reads as
|
|
320
|
+
a typed value); --placeholder-color is the per-field prop escape. */
|
|
186
321
|
input::placeholder,
|
|
187
322
|
textarea::placeholder {
|
|
188
|
-
|
|
323
|
+
color: var(--placeholder-color, var(--color-text-secondary));
|
|
324
|
+
}
|
|
325
|
+
/* xplat-web-only:end */
|
|
326
|
+
|
|
327
|
+
/* xplat-web-only:start — form controls don't inherit the body font in the
|
|
328
|
+
DOM (UA stylesheet wins); without this they render in the UA face. */
|
|
329
|
+
input,
|
|
330
|
+
textarea,
|
|
331
|
+
select,
|
|
332
|
+
button {
|
|
333
|
+
font-family: inherit;
|
|
189
334
|
}
|
|
335
|
+
/* xplat-web-only:end */
|
|
190
336
|
|
|
337
|
+
/* xplat-web-only:start — native ActivityIndicator is the NS widget */
|
|
191
338
|
.vx-spinner {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
339
|
+
display: inline-block;
|
|
340
|
+
width: 1em;
|
|
341
|
+
height: 1em;
|
|
342
|
+
box-sizing: border-box;
|
|
343
|
+
border: 2px solid currentColor;
|
|
344
|
+
border-right-color: transparent;
|
|
345
|
+
border-radius: 50%;
|
|
346
|
+
animation: vx-spinner-turn 0.8s linear infinite;
|
|
200
347
|
}
|
|
201
348
|
|
|
202
349
|
@keyframes vx-spinner-turn {
|
|
203
|
-
|
|
350
|
+
to {
|
|
351
|
+
transform: rotate(360deg);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/* xplat-web-only:end */
|
|
355
|
+
|
|
356
|
+
.vx-h1 {
|
|
357
|
+
font-size: 2em;
|
|
358
|
+
font-weight: bold;
|
|
359
|
+
}
|
|
360
|
+
.vx-h2 {
|
|
361
|
+
font-size: 1.5em;
|
|
362
|
+
font-weight: bold;
|
|
363
|
+
}
|
|
364
|
+
.vx-h3 {
|
|
365
|
+
font-size: 1.17em;
|
|
366
|
+
font-weight: bold;
|
|
367
|
+
}
|
|
368
|
+
.vx-h4 {
|
|
369
|
+
font-size: 1em;
|
|
370
|
+
font-weight: bold;
|
|
371
|
+
}
|
|
372
|
+
.vx-h5 {
|
|
373
|
+
font-size: 0.83em;
|
|
374
|
+
font-weight: bold;
|
|
375
|
+
}
|
|
376
|
+
.vx-h6 {
|
|
377
|
+
font-size: 0.67em;
|
|
378
|
+
font-weight: bold;
|
|
204
379
|
}
|
|
205
380
|
|
|
206
|
-
|
|
207
|
-
.
|
|
208
|
-
|
|
209
|
-
.vx-
|
|
210
|
-
|
|
211
|
-
|
|
381
|
+
/* xplat-web-only:start — glass material is real on iOS 26+; web approximates
|
|
382
|
+
with backdrop-filter. The class also lands on native elements (inert) so
|
|
383
|
+
apps can style their own fallback — but these rules are DOM-only. */
|
|
384
|
+
.vx-glass {
|
|
385
|
+
background-color: color-mix(in srgb, var(--color-surface) 30%, transparent);
|
|
386
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
387
|
+
}
|
|
212
388
|
|
|
213
|
-
|
|
389
|
+
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
390
|
+
.vx-glass {
|
|
391
|
+
background-color: color-mix(in srgb, var(--color-surface) 20%, transparent);
|
|
392
|
+
-webkit-backdrop-filter: blur(20px) saturate(1.6);
|
|
393
|
+
backdrop-filter: blur(20px) saturate(1.6);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
214
396
|
|
|
215
|
-
.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
397
|
+
.vx-glass--clear {
|
|
398
|
+
background-color: color-mix(in srgb, var(--color-surface) 12%, transparent);
|
|
399
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
400
|
+
}
|
|
219
401
|
|
|
220
|
-
|
|
221
|
-
.
|
|
402
|
+
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
403
|
+
.vx-glass--clear {
|
|
404
|
+
-webkit-backdrop-filter: blur(8px) saturate(1.3);
|
|
405
|
+
backdrop-filter: blur(8px) saturate(1.3);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/* xplat-web-only:end */
|
|
222
409
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
.
|
|
226
|
-
.shadow-2 { box-shadow: 0 4px 12px rgba(0,0,0,0.25); }
|
|
410
|
+
/* utilities (hand-written subset — do NOT adopt Tailwind until
|
|
411
|
+
NativeScript/tailwind#226 is resolved; the plugin strips core-supported
|
|
412
|
+
props incl. gap, drops all @media, and rewrites invisible→collapse) */
|
|
227
413
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
414
|
+
/* flex-1 keeps Tailwind v4's exact emission (`flex: 1 1 0%`) — web gets
|
|
415
|
+
identical semantics, NS parses grow/shrink out of the shorthand and
|
|
416
|
+
ignores the basis token it can't model. */
|
|
417
|
+
.flex-1 {
|
|
418
|
+
flex: 1 1 0%;
|
|
419
|
+
}
|
|
420
|
+
/* Flex divergence guards — NS FlexboxLayout diverges from web flexbox on
|
|
421
|
+
* shrinkability: grow children never shrink below intrinsic width, fixed
|
|
422
|
+
* children get crushed, and auto-height rows re-measure grow children at
|
|
423
|
+
* zero height. min-w-0/min-h-0 let a grow child shrink; shrink-0 protects
|
|
424
|
+
* a fixed-size child. All three are plain CSS on web. */
|
|
425
|
+
.min-w-0 {
|
|
426
|
+
min-width: 0;
|
|
427
|
+
}
|
|
428
|
+
.min-h-0 {
|
|
429
|
+
min-height: 0;
|
|
430
|
+
}
|
|
431
|
+
.shrink-0 {
|
|
432
|
+
flex-shrink: 0;
|
|
433
|
+
}
|
|
434
|
+
.grow {
|
|
435
|
+
flex-grow: 1;
|
|
436
|
+
}
|
|
437
|
+
.items-center {
|
|
438
|
+
align-items: center;
|
|
439
|
+
}
|
|
440
|
+
.items-baseline {
|
|
441
|
+
align-items: baseline;
|
|
442
|
+
}
|
|
443
|
+
.items-start {
|
|
444
|
+
align-items: flex-start;
|
|
445
|
+
}
|
|
446
|
+
.justify-center {
|
|
447
|
+
justify-content: center;
|
|
234
448
|
}
|
|
449
|
+
.justify-between {
|
|
450
|
+
justify-content: space-between;
|
|
451
|
+
}
|
|
452
|
+
.flex-wrap {
|
|
453
|
+
flex-wrap: wrap;
|
|
454
|
+
}
|
|
455
|
+
.gap-4 {
|
|
456
|
+
gap: 16px;
|
|
457
|
+
}
|
|
458
|
+
.gap-3 {
|
|
459
|
+
gap: 12px;
|
|
460
|
+
}
|
|
461
|
+
.gap-6 {
|
|
462
|
+
gap: 24px;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.text-2xl {
|
|
466
|
+
font-size: 28px;
|
|
467
|
+
}
|
|
468
|
+
.text-xl {
|
|
469
|
+
font-size: 20px;
|
|
470
|
+
}
|
|
471
|
+
.text-lg {
|
|
472
|
+
font-size: 18px;
|
|
473
|
+
}
|
|
474
|
+
.text-sm {
|
|
475
|
+
font-size: 13px;
|
|
476
|
+
}
|
|
477
|
+
.font-bold {
|
|
478
|
+
font-weight: bold;
|
|
479
|
+
}
|
|
480
|
+
.font-semibold {
|
|
481
|
+
font-weight: 600;
|
|
482
|
+
}
|
|
483
|
+
.text-muted {
|
|
484
|
+
color: var(--color-text-secondary);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/* Corner utilities — consume the --radius-* scale and carry the smooth
|
|
488
|
+
(squircle) corner treatment. Circles stay circular: .rounded-full omits
|
|
489
|
+
corner-shape deliberately (squircle at full radius = a squircle disk,
|
|
490
|
+
not a circle). Opt out per element with `corner-shape: round`. */
|
|
491
|
+
.rounded-sm {
|
|
492
|
+
border-radius: var(--radius-sm);
|
|
493
|
+
corner-shape: squircle;
|
|
494
|
+
}
|
|
495
|
+
.rounded-md {
|
|
496
|
+
border-radius: var(--radius-md);
|
|
497
|
+
corner-shape: squircle;
|
|
498
|
+
}
|
|
499
|
+
.rounded-lg {
|
|
500
|
+
border-radius: var(--radius-lg);
|
|
501
|
+
corner-shape: squircle;
|
|
502
|
+
}
|
|
503
|
+
.rounded-xl {
|
|
504
|
+
border-radius: var(--radius-xl);
|
|
505
|
+
corner-shape: squircle;
|
|
506
|
+
}
|
|
507
|
+
.rounded-full {
|
|
508
|
+
border-radius: 9999px;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.bg-primary {
|
|
512
|
+
background-color: var(--color-primary);
|
|
513
|
+
}
|
|
514
|
+
.text-onprimary {
|
|
515
|
+
color: var(--color-onprimary);
|
|
516
|
+
}
|
|
517
|
+
.bg-danger {
|
|
518
|
+
background-color: var(--color-danger);
|
|
519
|
+
}
|
|
520
|
+
/* xplat-web-only:start — box-shadow is inert on native (elevation is
|
|
521
|
+
Android-only; no shared mapping yet) */
|
|
522
|
+
.shadow-2 {
|
|
523
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
|
524
|
+
}
|
|
525
|
+
/* xplat-web-only:end */
|
|
526
|
+
|
|
527
|
+
.btn {
|
|
528
|
+
padding-top: 10px;
|
|
529
|
+
padding-right: 16px;
|
|
530
|
+
padding-bottom: 10px;
|
|
531
|
+
padding-left: 16px;
|
|
532
|
+
border-radius: var(--radius-md);
|
|
533
|
+
corner-shape: squircle;
|
|
534
|
+
font-size: 14px;
|
|
535
|
+
font-weight: 500;
|
|
536
|
+
align-items: center;
|
|
537
|
+
justify-content: center;
|
|
538
|
+
text-decoration: none;
|
|
539
|
+
color: var(--color-text);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/* Secondary = the same control reading quiet: hairline border, surface
|
|
543
|
+
fill, body-text label. Reserve .bg-primary for the one decisive action
|
|
544
|
+
in a group. */
|
|
545
|
+
.btn-secondary {
|
|
546
|
+
background-color: var(--color-surface);
|
|
547
|
+
border-width: 1px;
|
|
548
|
+
border-color: var(--color-border);
|
|
549
|
+
color: var(--color-text);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/* xplat-web-only:start — controls that render as real <a>/<button> need
|
|
553
|
+
inline layout and UA resets; the pressable/nav-link leaves are divs on
|
|
554
|
+
web and don't hit these rules. */
|
|
555
|
+
a.btn {
|
|
556
|
+
display: inline-flex;
|
|
557
|
+
}
|
|
558
|
+
a.chip {
|
|
559
|
+
display: inline-flex;
|
|
560
|
+
align-items: center;
|
|
561
|
+
}
|
|
562
|
+
/* xplat-web-only:end */
|
|
563
|
+
|
|
564
|
+
/* Tab shell — web divs/buttons + the Android SwapTabs fallback (iOS draws
|
|
565
|
+
its own UITabBar and never sees these). */
|
|
566
|
+
.vx-tabbar {
|
|
567
|
+
border-bottom-width: 1px;
|
|
568
|
+
border-bottom-color: var(--color-border);
|
|
569
|
+
background-color: var(--color-surface);
|
|
570
|
+
}
|
|
571
|
+
.vx-tab {
|
|
572
|
+
align-items: center;
|
|
573
|
+
justify-content: center;
|
|
574
|
+
padding-top: 10px;
|
|
575
|
+
padding-right: 16px;
|
|
576
|
+
padding-bottom: 10px;
|
|
577
|
+
padding-left: 16px;
|
|
578
|
+
font-size: 14px;
|
|
579
|
+
color: var(--color-text-secondary);
|
|
580
|
+
}
|
|
581
|
+
.vx-tab.active {
|
|
582
|
+
color: var(--color-text);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* xplat-web-only:start — DOM defaults are display:block (div) and
|
|
586
|
+
inline-block (button); every flex-bearing vx- class needs an explicit
|
|
587
|
+
display or flex-direction/align-items/justify-content are inert. */
|
|
588
|
+
.vx-tabs {
|
|
589
|
+
display: flex;
|
|
590
|
+
flex-direction: column;
|
|
591
|
+
flex: 1;
|
|
592
|
+
min-height: 0;
|
|
593
|
+
}
|
|
594
|
+
.vx-tabbar {
|
|
595
|
+
display: flex;
|
|
596
|
+
flex-direction: row;
|
|
597
|
+
}
|
|
598
|
+
.vx-tabpane {
|
|
599
|
+
flex: 1;
|
|
600
|
+
min-height: 0;
|
|
601
|
+
display: flex;
|
|
602
|
+
flex-direction: column;
|
|
603
|
+
}
|
|
604
|
+
button.vx-tab {
|
|
605
|
+
display: flex;
|
|
606
|
+
background: none;
|
|
607
|
+
cursor: pointer;
|
|
608
|
+
}
|
|
609
|
+
/* xplat-web-only:end */
|
|
235
610
|
|
|
236
611
|
/* harness experiment styles */
|
|
237
612
|
|
|
238
613
|
.vx-app {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
614
|
+
padding: 24px;
|
|
615
|
+
background-color: var(--color-surface);
|
|
616
|
+
color: var(--color-text);
|
|
242
617
|
}
|
|
243
618
|
|
|
244
619
|
.vx-app .text-xl,
|
|
245
620
|
.vx-app Text,
|
|
246
621
|
.vx-app label {
|
|
247
|
-
|
|
622
|
+
color: var(--color-text);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* `.vx-app label` above (descendant + type) outranks single-class text
|
|
626
|
+
colors inside the app root — roles that must win escalate here. */
|
|
627
|
+
.vx-app .text-onprimary {
|
|
628
|
+
color: var(--color-onprimary);
|
|
629
|
+
}
|
|
630
|
+
.vx-app .text-muted {
|
|
631
|
+
color: var(--color-text-secondary);
|
|
632
|
+
}
|
|
633
|
+
.vx-app .vx-tab label {
|
|
634
|
+
color: var(--color-text-secondary);
|
|
635
|
+
}
|
|
636
|
+
.vx-app .vx-tab.active label {
|
|
637
|
+
color: var(--color-text);
|
|
638
|
+
}
|
|
639
|
+
.vx-app .chip-on label {
|
|
640
|
+
color: var(--color-onprimary);
|
|
641
|
+
}
|
|
642
|
+
/* Danger surfaces keep white text in both themes (on-primary flips). */
|
|
643
|
+
.bg-danger .text-onprimary {
|
|
644
|
+
color: #ffffff;
|
|
248
645
|
}
|
|
249
646
|
|
|
250
647
|
.input {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
648
|
+
padding-top: 10px;
|
|
649
|
+
padding-right: 12px;
|
|
650
|
+
padding-bottom: 10px;
|
|
651
|
+
padding-left: 12px;
|
|
652
|
+
border-width: 1px;
|
|
653
|
+
border-color: var(--color-border);
|
|
654
|
+
border-radius: var(--radius-md);
|
|
655
|
+
corner-shape: squircle;
|
|
656
|
+
font-size: 14px;
|
|
657
|
+
background-color: var(--color-surface);
|
|
658
|
+
color: var(--color-text);
|
|
659
|
+
/* NS TextField/TextView placeholder color (ignored on web — the
|
|
660
|
+
::placeholder pseudo above covers it there). */
|
|
661
|
+
placeholder-color: var(--color-text-secondary);
|
|
256
662
|
}
|
|
257
663
|
|
|
258
664
|
.chip {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
665
|
+
background-color: var(--color-surface);
|
|
666
|
+
color: var(--color-text);
|
|
667
|
+
padding-top: 5px;
|
|
668
|
+
padding-right: 12px;
|
|
669
|
+
padding-bottom: 5px;
|
|
670
|
+
padding-left: 12px;
|
|
671
|
+
border-width: 1px;
|
|
672
|
+
border-color: var(--color-border);
|
|
673
|
+
border-radius: var(--radius-md);
|
|
674
|
+
corner-shape: squircle;
|
|
675
|
+
font-size: 13px;
|
|
676
|
+
text-decoration: none;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/* Selected chip — the one filled element in a group of bordered peers. */
|
|
680
|
+
.chip-on {
|
|
681
|
+
background-color: var(--color-primary);
|
|
682
|
+
border-color: var(--color-primary);
|
|
683
|
+
color: var(--color-onprimary);
|
|
264
684
|
}
|
|
265
685
|
|
|
266
686
|
.cell {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
687
|
+
padding: 12px;
|
|
688
|
+
border-bottom-width: 1px;
|
|
689
|
+
border-bottom-color: var(--color-border);
|
|
270
690
|
}
|
|
271
691
|
|
|
272
692
|
.vx-list {
|
|
273
|
-
|
|
693
|
+
flex-grow: 1;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.gap-2 {
|
|
697
|
+
gap: 8px;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/* In-window sheet — bottom-anchored panel (matches the native RootLayout
|
|
701
|
+
sheet). Layer is pointer-events:none; backdrop + panel re-enable. */
|
|
702
|
+
.vx-sheet-layer {
|
|
703
|
+
position: fixed;
|
|
704
|
+
inset: 0;
|
|
705
|
+
z-index: 1100;
|
|
706
|
+
pointer-events: none;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.vx-sheet-backdrop {
|
|
710
|
+
position: absolute;
|
|
711
|
+
inset: 0;
|
|
712
|
+
background: rgba(0, 0, 0, 0.4);
|
|
713
|
+
pointer-events: auto;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.vx-sheet {
|
|
717
|
+
position: absolute;
|
|
718
|
+
left: 0;
|
|
719
|
+
right: 0;
|
|
720
|
+
bottom: 0;
|
|
721
|
+
max-height: 85%;
|
|
722
|
+
padding: 24px;
|
|
723
|
+
overflow: auto;
|
|
724
|
+
border-width: 1px;
|
|
725
|
+
border-color: var(--color-border);
|
|
726
|
+
border-top-left-radius: var(--radius-xl);
|
|
727
|
+
border-top-right-radius: var(--radius-xl);
|
|
728
|
+
corner-shape: squircle;
|
|
729
|
+
background-color: var(--color-surface);
|
|
730
|
+
color: var(--color-text);
|
|
731
|
+
pointer-events: auto;
|
|
274
732
|
}
|
|
275
|
-
|
|
276
|
-
.gap-2 { gap: 8px; }
|