@octane-xplat/ui 0.7.2 → 0.9.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.
Files changed (219) hide show
  1. package/README.md +4 -3
  2. package/dist/native/Button.tsrx.js +1 -1
  3. package/dist/native/CommandPalette.tsrx.js +7 -1
  4. package/dist/native/Grid.tsrx.js +31 -2
  5. package/dist/native/KeyboardAvoiding.mobile.tsrx.js +2 -2
  6. package/dist/native/Markdown.tsrx.js +233 -0
  7. package/dist/native/Overlay.tsrx.js +15 -26
  8. package/dist/native/Pagination.tsrx.js +15 -12
  9. package/dist/native/PinInput.tsrx.js +25 -7
  10. package/dist/native/Popover.tsrx.js +33 -23
  11. package/dist/native/Pressable.tsrx.js +4 -1
  12. package/dist/native/Screen.tsrx.js +1 -1
  13. package/dist/native/SearchInput.tsrx.js +6 -3
  14. package/dist/native/Sheet.tsrx.js +23 -38
  15. package/dist/native/Slider.tsrx.js +23 -14
  16. package/dist/native/Text.tsrx.js +16 -0
  17. package/dist/native/TextArea.tsrx.js +6 -3
  18. package/dist/native/TextInput.tsrx.js +6 -3
  19. package/dist/native/VirtualList.tsrx.js +305 -121
  20. package/dist/native/WebView.tsrx.js +17 -2
  21. package/dist/native/_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js +9 -0
  22. package/dist/native/escape-props.js +2 -0
  23. package/dist/native/grid-auto-placement.js +83 -0
  24. package/dist/native/grid-placement.js +20 -0
  25. package/dist/native/index.shared.js +7 -5
  26. package/dist/native/keyboard-inset.mobile.js +5 -5
  27. package/dist/native/navigation-request.js +26 -0
  28. package/dist/native/octane-client-build.json +1 -1
  29. package/dist/native/overlay-lifetime.mobile.js +65 -0
  30. package/dist/native/pan.tsrx.js +16 -2
  31. package/dist/native/route-table.js +90 -10
  32. package/dist/native/route.js +26 -16
  33. package/dist/native/shared.js +7 -5
  34. package/dist/native/sheet-service.js +2 -2
  35. package/dist/native/svg.mobile.js +1 -1
  36. package/dist/native/{tap-to-blur.mobile.js → tap-to-blur.ios.js} +20 -32
  37. package/dist/native/text-focus.js +10 -0
  38. package/dist/native/text-write.js +18 -11
  39. package/dist/native/toast-anchor.tsrx.js +1 -1
  40. package/dist/native/ui.js +7 -6
  41. package/dist/native/useMeasure.tsrx.js +17 -18
  42. package/dist/native/vendor/ui-svg/index.android.js +2 -2
  43. package/dist/native/vendor/ui-svg/index.ios.js +2 -2
  44. package/dist/native/vendor/ui-svg/{index.common.js → src/index.common.js} +1 -1
  45. package/dist/native/virtual-list-cell-pool.js +63 -0
  46. package/dist/web/Button.web.tsrx.js +1 -1
  47. package/dist/web/KeyboardAvoiding.web.tsrx.js +78 -0
  48. package/dist/web/Markdown.tsrx.js +98 -0
  49. package/dist/web/Overlay.web.tsrx.js +81 -32
  50. package/dist/web/PinInput.web.tsrx.js +24 -10
  51. package/dist/web/Pressable.web.tsrx.js +169 -53
  52. package/dist/web/SearchInput.web.tsrx.js +2 -2
  53. package/dist/web/Sheet.web.tsrx.js +20 -7
  54. package/dist/web/TextArea.web.tsrx.js +22 -17
  55. package/dist/web/TextInput.web.tsrx.js +2 -2
  56. package/dist/web/VirtualList.web.tsrx.js +220 -109
  57. package/dist/web/WebView.web.tsrx.js +3 -3
  58. package/dist/web/{index.shared.js → index.shared.web.js} +7 -5
  59. package/dist/web/modal-focus.web.js +95 -0
  60. package/dist/web/navigation-request.js +26 -0
  61. package/dist/web/octane-client-build.json +1 -1
  62. package/dist/web/pan.web.tsrx.js +27 -10
  63. package/dist/web/route-table.js +90 -10
  64. package/dist/web/route.web.js +88 -22
  65. package/dist/web/sheet-service.web.js +8 -0
  66. package/dist/web/theme/colorScheme.web.js +31 -4
  67. package/dist/web/ui.js +8 -6
  68. package/dist/web/virtual-list-cell-pool.js +63 -0
  69. package/package.json +21 -19
  70. package/platforms/android/native-api-usage.json +7 -7
  71. package/src/CommandPalette.tsrx +8 -1
  72. package/src/Grid.tsrx +32 -1
  73. package/src/Icon.macos.tsrx +1 -0
  74. package/src/KeyboardAvoiding.macos.tsrx +7 -1
  75. package/src/KeyboardAvoiding.mobile.tsrx +2 -2
  76. package/src/KeyboardAvoiding.tsrx +15 -0
  77. package/src/KeyboardAvoiding.web.tsrx +15 -0
  78. package/src/Markdown.tsrx +121 -0
  79. package/src/Meter.tsrx +1 -0
  80. package/src/Overlay.tsrx +15 -30
  81. package/src/Overlay.web.tsrx +12 -3
  82. package/src/Pagination.tsrx +6 -4
  83. package/src/PinInput.macos.tsrx +20 -6
  84. package/src/PinInput.mobile.test.ts +107 -0
  85. package/src/PinInput.tsrx +20 -6
  86. package/src/PinInput.web.test.tsrx +42 -0
  87. package/src/PinInput.web.tsrx +20 -6
  88. package/src/Popover.tsrx +36 -22
  89. package/src/Pressable.tsrx +1 -1
  90. package/src/Pressable.web.test.tsrx +36 -0
  91. package/src/Pressable.web.tsrx +37 -6
  92. package/src/RouteHost.macos.tsrx +1 -0
  93. package/src/Screen.tsrx +1 -1
  94. package/src/SearchInput.tsrx +4 -3
  95. package/src/SearchInput.web.tsrx +1 -1
  96. package/src/Sheet.tsrx +21 -42
  97. package/src/Sheet.web.tsrx +9 -1
  98. package/src/Slider.tsrx +13 -11
  99. package/src/Text.tsrx +36 -0
  100. package/src/TextArea.tsrx +6 -5
  101. package/src/TextArea.web.test.tsrx +39 -0
  102. package/src/TextArea.web.tsrx +7 -5
  103. package/src/TextInput.tsrx +6 -6
  104. package/src/TextInput.web.tsrx +1 -1
  105. package/src/VirtualList.macos.tsrx +93 -6
  106. package/src/VirtualList.mobile.test.tsrx +232 -0
  107. package/src/VirtualList.tsrx +285 -81
  108. package/src/VirtualList.web.tsrx +131 -25
  109. package/src/WebView.tsrx +20 -10
  110. package/src/WebView.web.tsrx +3 -0
  111. package/src/android/BottomNavigationView.android.tsrx +1 -0
  112. package/src/android/MaterialDialog.android.tsrx +1 -0
  113. package/src/anim.macos.tsrx +1 -0
  114. package/src/apply-native-modifiers.native.ts +4 -1
  115. package/src/child-array.ts +12 -7
  116. package/src/colorScheme.macos.ts +1 -0
  117. package/src/controls.macos.tsrx +4 -1
  118. package/src/data-driver.mobile.test.ts +120 -0
  119. package/src/data-lifecycle.mobile.test.ts +251 -0
  120. package/src/escape-props.ts +13 -1
  121. package/src/grid-auto-placement.ts +163 -0
  122. package/src/grid-placement.ts +29 -0
  123. package/src/hosted-unsupported.macos.tsrx +1 -0
  124. package/src/index.macos.ts +23 -3
  125. package/src/index.mobile.ts +0 -1
  126. package/src/index.shared.ts +22 -1
  127. package/src/index.shared.web.ts +291 -0
  128. package/src/index.web.ts +1 -1
  129. package/src/input-readiness.web.test.tsrx +82 -0
  130. package/src/ios/UITabBar.ios.tsrx +1 -0
  131. package/src/keyboard-inset.mobile.test.ts +62 -0
  132. package/src/keyboard-inset.mobile.ts +63 -43
  133. package/src/layout-shells.macos.tsrx +1 -5
  134. package/src/macos-extras.macos.tsrx +2 -0
  135. package/src/modal-focus.web.test.tsrx +63 -0
  136. package/src/modal-focus.web.ts +162 -0
  137. package/src/navigation-request.ts +37 -0
  138. package/src/overlay-lifecycle.mobile.test.ts +316 -0
  139. package/src/overlay-lifetime.mobile.ts +107 -0
  140. package/src/pan.mobile.test.tsrx +116 -0
  141. package/src/pan.tsrx +20 -2
  142. package/src/pan.web.tsrx +35 -7
  143. package/src/platformProps.ts +1 -0
  144. package/src/popover.mobile.test.ts +9 -1
  145. package/src/pressable-accessibility.mobile.test.ts +30 -0
  146. package/src/props.ts +163 -3
  147. package/src/refresh.ts +6 -1
  148. package/src/refresh.web.test.ts +77 -77
  149. package/src/route-android.test.ts +162 -0
  150. package/src/route-history.web.test.ts +80 -0
  151. package/src/route-navigation.web.test.ts +173 -0
  152. package/src/route-table.test.ts +188 -9
  153. package/src/route-table.ts +138 -10
  154. package/src/route.macos.ts +56 -13
  155. package/src/route.ts +54 -24
  156. package/src/route.web.ts +141 -24
  157. package/src/sheet-detents.ts +5 -3
  158. package/src/sheet-service.macos.ts +5 -1
  159. package/src/sheet-service.ts +1 -1
  160. package/src/sheet-service.web.ts +8 -0
  161. package/src/sheet-snap.test.ts +23 -23
  162. package/src/sheet-snap.ts +6 -1
  163. package/src/svg.mobile.ts +9 -3
  164. package/src/tap-to-blur.ios.ts +106 -0
  165. package/src/tap-to-blur.mobile.ts +21 -95
  166. package/src/text-focus.ts +12 -0
  167. package/src/text-input.mobile.test.ts +89 -0
  168. package/src/text-write.ts +21 -11
  169. package/src/theme/colorScheme.linux.ts +2 -4
  170. package/src/theme/colorScheme.web.ts +51 -4
  171. package/src/theme/theme-scheme.macos.ts +8 -2
  172. package/src/theme/tokens.css +1740 -1718
  173. package/src/translate.macos.ts +4 -1
  174. package/src/useMeasure.tsrx +20 -21
  175. package/src/virtual-list-cell-pool.test.ts +53 -0
  176. package/src/virtual-list-cell-pool.ts +81 -0
  177. package/src/virtual-list-layout.test.ts +11 -8
  178. package/src/virtual-list-layout.ts +4 -8
  179. package/src/windows.linux.ts +7 -2
  180. package/src/windows.macos.ts +1 -0
  181. package/types/generated/.tsrx-typegen-manifest.json +16 -3
  182. package/types/generated/Grid.d.ts +0 -1
  183. package/types/generated/KeyboardAvoiding.d.ts +3 -0
  184. package/types/generated/KeyboardAvoiding.web.d.ts +2 -0
  185. package/types/generated/Markdown.d.ts +59 -0
  186. package/types/generated/TextArea.d.ts +2 -2
  187. package/types/generated/TextArea.web.d.ts +1 -1
  188. package/types/generated/TextInput.d.ts +2 -3
  189. package/types/generated/VirtualList.d.ts +3 -3
  190. package/types/generated/escape-props.d.ts +4 -0
  191. package/types/generated/grid-auto-placement.d.ts +13 -0
  192. package/types/generated/grid-placement.d.ts +5 -0
  193. package/types/generated/index.mobile.d.ts +0 -1
  194. package/types/generated/index.shared.d.ts +6 -3
  195. package/types/generated/index.shared.web.d.ts +114 -0
  196. package/types/generated/index.web.d.ts +1 -1
  197. package/types/generated/modal-focus.web.d.ts +3 -0
  198. package/types/generated/navigation-request.d.ts +10 -0
  199. package/types/generated/overlay-lifetime.mobile.d.ts +10 -0
  200. package/types/generated/props.d.ts +111 -3
  201. package/types/generated/route-table.d.ts +14 -3
  202. package/types/generated/svg.mobile.d.ts +2 -2
  203. package/types/generated/tap-to-blur.ios.d.ts +13 -0
  204. package/types/generated/tap-to-blur.mobile.d.ts +1 -15
  205. package/types/generated/text-focus.d.ts +3 -0
  206. package/types/generated/text-write.d.ts +3 -1
  207. package/types/generated/theme/colorScheme.web.d.ts +5 -0
  208. package/types/generated/virtual-list-cell-pool.d.ts +18 -0
  209. package/types/index.macos.d.ts +15 -3
  210. /package/src/vendor/ui-svg/{index.android.ts → src/index.android.ts} +0 -0
  211. /package/src/vendor/ui-svg/{index.common.ts → src/index.common.ts} +0 -0
  212. /package/src/vendor/ui-svg/{index.ios.ts → src/index.ios.ts} +0 -0
  213. /package/src/vendor/ui-svg/{index.ts → src/index.ts} +0 -0
  214. /package/src/vendor/ui-svg/{typings → src/typings}/SVGKit.d.ts +0 -0
  215. /package/src/vendor/ui-svg/{typings → src/typings}/android.d.ts +0 -0
  216. /package/src/vendor/ui-svg/{typings → src/typings}/ios.d.ts +0 -0
  217. /package/types/generated/vendor/ui-svg/{index.common.d.ts → src/index.common.d.ts} +0 -0
  218. /package/types/generated/vendor/ui-svg/{index.d.ts → src/index.d.ts} +0 -0
  219. /package/types/generated/vendor/ui-svg/{index.ios.d.ts → src/index.ios.d.ts} +0 -0
@@ -1,48 +1,53 @@
1
1
  /* Design tokens — NS reads :root/.ns-root; web reads :root/.dark.
2
2
  Numbers are dip (native) = px (web) by leaf convention. */
3
3
 
4
- :root,
5
- .ns-root {
6
- --color-primary: #171717;
7
- /* Token names double as the future Tailwind theme keys: text-onprimary
4
+ @layer xplat.reset, xplat.base, xplat.tokens, xplat.components, xplat.recipes, xplat.utilities;
5
+
6
+ @layer xplat.tokens {
7
+ :root,
8
+ .ns-root {
9
+ --color-primary: #171717;
10
+ /* Token names double as the future Tailwind theme keys: text-onprimary
8
11
  resolves --color-onprimary, bg-danger resolves --color-danger. Keep
9
12
  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
13
+ --color-onprimary: #fafafa;
14
+ --color-surface: #ffffff;
15
+ --color-surface-secondary: #fafafa;
16
+ --color-text: #0a0a0a;
17
+ --color-text-secondary: #666666;
18
+ --color-border: #eaeaea;
19
+ --color-border-strong: #d4d4d4;
20
+ --color-danger: #dc2626;
21
+ /* Family lists are deliberately generic. Apps override these with their
19
22
  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
23
+ --font-sans: system-ui, sans-serif;
24
+ --font-mono: ui-monospace, monospace;
25
+ --space-4: 16px;
26
+ /* Corner scale — rounded-rectangle radii. Smooth (squircle) corners are
24
27
  the default corner treatment: every class that consumes a token also
25
28
  declares `corner-shape: squircle`. Full-radius circles opt out —
26
29
  `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;
31
- }
30
+ --radius-sm: 6px;
31
+ --radius-md: 8px;
32
+ --radius-lg: 12px;
33
+ --radius-xl: 16px;
34
+ }
32
35
 
33
- .ns-dark,
34
- .dark {
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;
36
+ .ns-dark,
37
+ .dark {
38
+ --color-primary: #ededed;
39
+ --color-onprimary: #171717;
40
+ --color-surface: #0a0a0a;
41
+ --color-surface-secondary: #141414;
42
+ --color-text: #ededed;
43
+ --color-text-secondary: #a1a1a1;
44
+ --color-border: #2e2e2e;
45
+ --color-border-strong: #4a4a4a;
46
+ }
43
47
  }
44
48
 
45
- /* xplat-web-only:start — DOM normalization. NativeScript is the reference
49
+ @layer xplat.components {
50
+ /* xplat-web-only:start — DOM normalization. NativeScript is the reference
46
51
  box model: dip sizes are outer boxes, and a border paints from
47
52
  width+color alone. The DOM's UA stylesheet diverges on both counts, so
48
53
  normalize the deltas once instead of patching individual rules:
@@ -54,31 +59,31 @@
54
59
  phantom ~3px edges wherever a rule sets only one side's width.
55
60
  - <img> is a leaf, not inline text (no baseline descender gap).
56
61
  - .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;
68
- }
69
- .vx-app {
70
- display: flex;
71
- flex-direction: column;
72
- }
73
- /* Native has no scroll chaining — a ScrollView just bounces inside
62
+ *,
63
+ *::before,
64
+ *::after {
65
+ box-sizing: border-box;
66
+ }
67
+ * {
68
+ border-width: 0;
69
+ border-style: solid;
70
+ }
71
+ img {
72
+ display: block;
73
+ }
74
+ .vx-app {
75
+ display: flex;
76
+ flex-direction: column;
77
+ }
78
+ /* Native has no scroll chaining — a ScrollView just bounces inside
74
79
  itself because the parent is never a scroller. contain is the DOM
75
80
  equivalent: scroll stays in the leaf it started in. */
76
- .vx-scroll {
77
- overscroll-behavior: contain;
78
- }
79
- /* xplat-web-only:end */
81
+ .vx-scroll {
82
+ overscroll-behavior: contain;
83
+ }
84
+ /* xplat-web-only:end */
80
85
 
81
- /* Shared normalization — NS FlexboxLayout defaults flex children to
86
+ /* Shared normalization — NS FlexboxLayout defaults flex children to
82
87
  shrink:1 with NO min-content floor (its shrink pass clamps at
83
88
  effectiveMinWidth/Height = the explicit min-* prop, default 0), so an
84
89
  overflowing column crushes intrinsic children toward zero where the
@@ -86,1849 +91,1866 @@ img {
86
91
  reason — do the same on both engines. `*` is zero-specificity: flex-1
87
92
  (the shorthand sets shrink:1) and .shrink still opt back in, and
88
93
  truncation patterns pair them with min-w-0/min-h-0. */
89
- * {
90
- flex-shrink: 0;
91
- }
94
+ * {
95
+ flex-shrink: 0;
96
+ }
92
97
 
93
- /* vocabulary base styles */
98
+ /* vocabulary base styles */
94
99
 
95
- .vx-view,
96
- .vx-row {
97
- display: flex;
98
- }
100
+ .vx-view,
101
+ .vx-row {
102
+ display: flex;
103
+ }
99
104
 
100
- .vx-view {
101
- flex-direction: column;
102
- }
105
+ .vx-view {
106
+ flex-direction: column;
107
+ }
103
108
 
104
- /* Safe-area values are zero when the viewport has no display cutout. */
105
- .vx-safe-area {
106
- padding-top: constant(safe-area-inset-top);
107
- padding-right: constant(safe-area-inset-right);
108
- padding-bottom: constant(safe-area-inset-bottom);
109
- padding-left: constant(safe-area-inset-left);
110
- }
109
+ /* Safe-area values are zero when the viewport has no display cutout. */
110
+ .vx-safe-area {
111
+ padding-top: constant(safe-area-inset-top);
112
+ padding-right: constant(safe-area-inset-right);
113
+ padding-bottom: constant(safe-area-inset-bottom);
114
+ padding-left: constant(safe-area-inset-left);
115
+ }
111
116
 
112
- .vx-safe-area {
113
- padding-top: env(safe-area-inset-top);
114
- padding-right: env(safe-area-inset-right);
115
- padding-bottom: env(safe-area-inset-bottom);
116
- padding-left: env(safe-area-inset-left);
117
- }
117
+ .vx-safe-area {
118
+ padding-top: env(safe-area-inset-top);
119
+ padding-right: env(safe-area-inset-right);
120
+ padding-bottom: env(safe-area-inset-bottom);
121
+ padding-left: env(safe-area-inset-left);
122
+ }
118
123
 
119
- .vx-keyboard-avoiding {
120
- display: flex;
121
- flex-direction: column;
122
- }
123
- /* Self-drawn drawer — shared geometry. Native grid children overlay the
124
+ .vx-keyboard-avoiding {
125
+ display: flex;
126
+ flex-direction: column;
127
+ }
128
+ /* Self-drawn drawer — shared geometry. Native grid children overlay the
124
129
  cell (panel pins left via horizontalAlignment in the leaf); DOM
125
130
  positioning extras are web-only below. */
126
- .vx-drawer-backdrop {
127
- background-color: rgba(0, 0, 0, 0.4);
128
- }
129
- .vx-drawer-panel {
130
- width: 280px;
131
- background-color: var(--color-surface);
132
- }
133
- /* xplat-web-only:start */
134
- .vx-drawer {
135
- display: flex;
136
- flex-direction: row;
137
- position: relative;
138
- overflow: hidden;
139
- }
140
- .vx-drawer-main {
141
- display: flex;
142
- flex: 1;
143
- min-width: 0;
144
- }
145
- .vx-drawer-backdrop {
146
- position: absolute;
147
- inset: 0;
148
- }
149
- .vx-drawer-panel {
150
- position: absolute;
151
- z-index: 1;
152
- top: 0;
153
- bottom: 0;
154
- left: 0;
155
- overflow: auto;
156
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
157
- }
158
- /* xplat-web-only:end */
131
+ .vx-drawer-backdrop {
132
+ background-color: rgba(0, 0, 0, 0.4);
133
+ }
134
+ .vx-drawer-panel {
135
+ width: 280px;
136
+ background-color: var(--color-surface);
137
+ }
138
+ /* xplat-web-only:start */
139
+ .vx-drawer {
140
+ display: flex;
141
+ flex-direction: row;
142
+ position: relative;
143
+ overflow: hidden;
144
+ }
145
+ .vx-drawer-main {
146
+ display: flex;
147
+ flex: 1;
148
+ min-width: 0;
149
+ }
150
+ .vx-drawer-backdrop {
151
+ position: absolute;
152
+ inset: 0;
153
+ }
154
+ .vx-drawer-panel {
155
+ position: absolute;
156
+ z-index: 1;
157
+ top: 0;
158
+ bottom: 0;
159
+ left: 0;
160
+ overflow: auto;
161
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
162
+ }
163
+ /* xplat-web-only:end */
159
164
 
160
- .vx-row {
161
- flex-direction: row;
162
- }
165
+ .vx-row {
166
+ flex-direction: row;
167
+ }
163
168
 
164
- .vx-grid,
165
- .vx-stack {
166
- display: grid;
167
- }
169
+ .vx-grid,
170
+ .vx-stack {
171
+ display: grid;
172
+ }
168
173
 
169
- .vx-stack > * {
170
- grid-area: 1 / 1;
171
- }
174
+ .vx-stack > * {
175
+ grid-area: 1 / 1;
176
+ horizontal-align: left;
177
+ vertical-align: top;
178
+ }
172
179
 
173
- /* xplat-web-only:start — Absolute's native leaf is absolutelayout; CSS
180
+ /* xplat-web-only:start — Absolute's native leaf is absolutelayout; CSS
174
181
  positioning is web-only (children position via left/top props) */
175
- .vx-absolute {
176
- position: relative;
177
- }
182
+ .vx-absolute {
183
+ position: relative;
184
+ }
178
185
 
179
- .vx-absolute > * {
180
- position: absolute;
181
- }
182
- /* xplat-web-only:end */
186
+ .vx-absolute > * {
187
+ position: absolute;
188
+ }
189
+ /* xplat-web-only:end */
183
190
 
184
- .vx-spacer {
185
- flex-grow: 1;
186
- }
191
+ .vx-spacer {
192
+ flex-grow: 1;
193
+ }
187
194
 
188
- .vx-pressable {
189
- display: flex;
190
- flex-direction: column;
191
- user-select: none;
192
- }
195
+ .vx-pressable {
196
+ display: flex;
197
+ flex-direction: column;
198
+ user-select: none;
199
+ }
193
200
 
194
- /* web press feedback */
195
- .vx-pressable:active {
196
- opacity: 0.85;
197
- }
198
- /* native press feedback (NS pseudo — dead rule on web, and vice versa) */
199
- .vx-pressable:pressed {
200
- opacity: 0.85;
201
- }
201
+ /* web press feedback */
202
+ .vx-pressable:active {
203
+ opacity: 0.85;
204
+ }
205
+ /* native press feedback (NS pseudo — dead rule on web, and vice versa) */
206
+ .vx-pressable:pressed {
207
+ opacity: 0.85;
208
+ }
202
209
 
203
- /* xplat-web-only:start — overlays/toasts/popovers render through RootLayout
210
+ /* xplat-web-only:start — overlays/toasts/popovers render through RootLayout
204
211
  or showModal on native; these portal/positioning rules are web-only */
205
- .vx-overlay {
206
- z-index: 1000;
207
- }
212
+ .vx-overlay {
213
+ z-index: 1000;
214
+ }
208
215
 
209
- .vx-overlay-shade {
210
- position: absolute;
211
- inset: 0;
212
- background-color: rgba(0, 0, 0, 0.4);
213
- }
216
+ .vx-overlay-shade {
217
+ position: absolute;
218
+ inset: 0;
219
+ background-color: rgba(0, 0, 0, 0.4);
220
+ }
214
221
 
215
- .vx-overlay-content {
216
- position: relative;
217
- z-index: 1;
218
- }
222
+ .vx-overlay-content {
223
+ position: relative;
224
+ z-index: 1;
225
+ }
219
226
 
220
- .vx-popover {
221
- z-index: 1001;
222
- }
227
+ .vx-popover {
228
+ z-index: 1001;
229
+ }
223
230
 
224
- .vx-popover-backdrop {
225
- background-color: transparent;
226
- }
231
+ .vx-popover-backdrop {
232
+ background-color: transparent;
233
+ }
227
234
 
228
- .vx-tooltip {
229
- padding: 4px 8px;
230
- border-width: 1px;
231
- border-color: var(--color-border);
232
- border-radius: var(--radius-sm);
233
- corner-shape: squircle;
234
- background-color: var(--color-surface);
235
- color: var(--color-text);
236
- font-size: 12px;
237
- line-height: 16px;
238
- }
235
+ .vx-tooltip {
236
+ padding: 4px 8px;
237
+ border-width: 1px;
238
+ border-color: var(--color-border);
239
+ border-radius: var(--radius-sm);
240
+ corner-shape: squircle;
241
+ background-color: var(--color-surface);
242
+ color: var(--color-text);
243
+ font-size: 12px;
244
+ line-height: 16px;
245
+ }
239
246
 
240
- .vx-toast-host {
241
- position: fixed;
242
- z-index: 1002;
243
- left: 0;
244
- right: 0;
245
- display: flex;
246
- justify-content: center;
247
- pointer-events: none;
248
- }
247
+ .vx-toast-host {
248
+ position: fixed;
249
+ z-index: 1002;
250
+ left: 0;
251
+ right: 0;
252
+ display: flex;
253
+ justify-content: center;
254
+ pointer-events: none;
255
+ }
249
256
 
250
- .vx-toast-top {
251
- top: 24px;
252
- }
253
- .vx-toast-bottom {
254
- bottom: 24px;
255
- }
257
+ .vx-toast-top {
258
+ top: 24px;
259
+ }
260
+ .vx-toast-bottom {
261
+ bottom: 24px;
262
+ }
256
263
 
257
- .vx-toast {
258
- pointer-events: auto;
259
- }
260
- /* xplat-web-only:end */
264
+ .vx-toast {
265
+ pointer-events: auto;
266
+ }
267
+ /* xplat-web-only:end */
261
268
 
262
- /* Normalized OS-widget reset — TextInput/TextArea are real
269
+ /* Normalized OS-widget reset — TextInput/TextArea are real
263
270
  <input>/<textarea>/TextField/TextView; the leaves emit vx-input/
264
271
  vx-textarea unconditionally so the platform chrome is stripped before
265
272
  app styles apply. On Android the transparent background replaces the
266
273
  default EditText drawable (kills the Material underline); zero padding
267
274
  removes native insets. Legitimate semantics — IME, secure entry,
268
275
  selection, autofill, caret, focus — stay platform-native by design. */
269
- .vx-input,
270
- .vx-textarea {
271
- padding: 0;
272
- margin: 0;
273
- border-width: 0;
274
- background-color: transparent;
275
- color: var(--color-text);
276
- font-size: 14px;
277
- /* NS placeholder color; web uses the ::placeholder pseudo below. */
278
- placeholder-color: var(--color-text-secondary);
279
- }
280
- /* xplat-web-only:start — DOM-only reset: element UA chrome and the
276
+ .vx-input,
277
+ .vx-textarea {
278
+ padding: 0;
279
+ margin: 0;
280
+ border-width: 0;
281
+ background-color: transparent;
282
+ color: var(--color-text);
283
+ font-size: 14px;
284
+ /* NS placeholder color; web uses the ::placeholder pseudo below. */
285
+ placeholder-color: var(--color-text-secondary);
286
+ }
287
+ /* xplat-web-only:start — DOM-only reset: element UA chrome and the
281
288
  focus outline (normalized to the same thin ring on both). */
282
- .vx-input,
283
- .vx-textarea {
284
- appearance: none;
285
- -webkit-appearance: none;
286
- outline: none;
287
- }
288
- .vx-input:focus,
289
- .vx-textarea:focus {
290
- outline: 1px solid var(--color-primary);
291
- outline-offset: -1px;
292
- }
293
- /* xplat-web-only:end */
289
+ .vx-input,
290
+ .vx-textarea {
291
+ appearance: none;
292
+ -webkit-appearance: none;
293
+ outline: none;
294
+ }
295
+ .vx-input:focus,
296
+ .vx-textarea:focus {
297
+ outline: 1px solid var(--color-primary);
298
+ outline-offset: -1px;
299
+ }
300
+ /* xplat-web-only:end */
294
301
 
295
- /* auto-grow owns height — the manual resize handle has no native analog */
296
- /* xplat-web-only:start */
297
- .vx-textarea {
298
- resize: none;
299
- }
300
- /* xplat-web-only:end */
302
+ /* auto-grow owns height — the manual resize handle has no native analog */
303
+ /* xplat-web-only:start */
304
+ .vx-textarea {
305
+ resize: none;
306
+ }
307
+ /* xplat-web-only:end */
301
308
 
302
- /* WebView chrome reset — the web leaf is a real <iframe>: strip the UA's
309
+ /* WebView chrome reset — the web leaf is a real <iframe>: strip the UA's
303
310
  inset border and the inline-element baseline gap. WKWebView and
304
311
  android.webkit.WebView ship chrome-free, so there is no native reset. */
305
- /* xplat-web-only:start */
306
- .vx-webview {
307
- display: block;
308
- border-width: 0;
309
- }
310
- /* xplat-web-only:end */
312
+ /* xplat-web-only:start */
313
+ .vx-webview {
314
+ display: block;
315
+ border-width: 0;
316
+ }
317
+ /* xplat-web-only:end */
311
318
 
312
- /* Video — surface-hosted leaf: the engine surface fills the frame and the
319
+ /* Video — surface-hosted leaf: the engine surface fills the frame and the
313
320
  self-drawn transport layers stack on top in the same grid cell
314
321
  (vx-stack). NS-side rules only — DOM resets sit in the web block. */
315
- .vx-video {
316
- background-color: #000;
317
- }
322
+ .vx-video {
323
+ background-color: #000;
324
+ }
318
325
 
319
- .vx-video-centerbtn {
320
- width: 56px;
321
- height: 56px;
322
- border-radius: 28px;
323
- background-color: rgba(0, 0, 0, 0.55);
324
- align-items: center;
325
- justify-content: center;
326
- }
326
+ .vx-video > * {
327
+ horizontal-align: stretch;
328
+ vertical-align: stretch;
329
+ }
327
330
 
328
- .vx-video-bar {
329
- background-color: rgba(0, 0, 0, 0.45);
330
- padding: 4px 8px;
331
- }
331
+ .vx-video-centerbtn {
332
+ width: 56px;
333
+ height: 56px;
334
+ border-radius: 28px;
335
+ background-color: rgba(0, 0, 0, 0.55);
336
+ align-items: center;
337
+ justify-content: center;
338
+ }
332
339
 
333
- .vx-video-btn {
334
- padding: 4px;
335
- }
340
+ .vx-video-bar {
341
+ background-color: rgba(0, 0, 0, 0.45);
342
+ padding: 4px 8px;
343
+ }
336
344
 
337
- /* Full-frame tap layer — hit target only, never paints. */
338
- .vx-video-tap {
339
- background-color: transparent;
340
- }
345
+ .vx-video-btn {
346
+ padding: 4px;
347
+ }
341
348
 
342
- .vx-video-time {
343
- color: #fff;
344
- font-size: 11px;
345
- min-width: 34px;
346
- text-align: center;
347
- }
349
+ /* Full-frame tap layer — hit target only, never paints. */
350
+ .vx-video-tap {
351
+ background-color: transparent;
352
+ }
353
+
354
+ .vx-video-time {
355
+ color: #fff;
356
+ font-size: 11px;
357
+ min-width: 34px;
358
+ text-align: center;
359
+ }
348
360
 
349
- /* xplat-web-only:start — the <video> element is inline and overlays would
361
+ /* xplat-web-only:start — the <video> element is inline and overlays would
350
362
  otherwise swallow pointer events; native layers are hit-transparent by
351
363
  default, so none of this exists on NS. */
352
- .vx-video-surface {
353
- display: block;
354
- width: 100%;
355
- height: 100%;
356
- }
364
+ .vx-video-surface {
365
+ display: block;
366
+ width: 100%;
367
+ height: 100%;
368
+ }
357
369
 
358
- .vx-video-poster,
359
- .vx-video-barwrap {
360
- pointer-events: none;
361
- }
370
+ .vx-video-poster,
371
+ .vx-video-barwrap {
372
+ pointer-events: none;
373
+ }
362
374
 
363
- .vx-video-barwrap > * {
364
- pointer-events: auto;
365
- }
375
+ .vx-video-barwrap > * {
376
+ pointer-events: auto;
377
+ }
366
378
 
367
- .vx-video-poster {
368
- object-fit: cover;
369
- }
379
+ .vx-video-poster {
380
+ object-fit: cover;
381
+ }
370
382
 
371
- /* xplat-web-only:end */
383
+ /* xplat-web-only:end */
372
384
 
373
- /* xplat-web-only:start */
374
- /* Modal ROUTES (pushRoute presentation:'modal') — the URL stays underneath,
385
+ /* xplat-web-only:start */
386
+ /* Modal ROUTES (pushRoute presentation:'modal') — the URL stays underneath,
375
387
  so the overlay is a fixed layer over the shell rather than a dialog. */
376
- .vx-modalroute {
377
- position: fixed;
378
- inset: 0;
379
- z-index: 1000;
380
- display: flex;
381
- align-items: stretch;
382
- justify-content: stretch;
383
- }
388
+ .vx-modalroute {
389
+ position: fixed;
390
+ inset: 0;
391
+ z-index: 1000;
392
+ display: flex;
393
+ align-items: stretch;
394
+ justify-content: stretch;
395
+ }
384
396
 
385
- .vx-modalroute-shade {
386
- position: absolute;
387
- inset: 0;
388
- border: 0;
389
- padding: 0;
390
- background: rgba(0, 0, 0, 0.4);
391
- cursor: default;
392
- }
397
+ .vx-modalroute-shade {
398
+ position: absolute;
399
+ inset: 0;
400
+ border: 0;
401
+ padding: 0;
402
+ background: rgba(0, 0, 0, 0.4);
403
+ cursor: default;
404
+ }
393
405
 
394
- .vx-modalroute-body {
395
- position: relative;
396
- flex: 1;
397
- min-width: 0;
398
- overflow: auto;
399
- color: var(--color-text);
400
- background-color: var(--color-surface);
401
- }
402
- /* xplat-web-only:end */
406
+ .vx-modalroute-body {
407
+ position: relative;
408
+ flex: 1;
409
+ min-width: 0;
410
+ overflow: auto;
411
+ color: var(--color-text);
412
+ background-color: var(--color-surface);
413
+ }
414
+ /* xplat-web-only:end */
403
415
 
404
- /* xplat-web-only:start — placeholder pseudos are DOM-only. Muted like
416
+ /* xplat-web-only:start — placeholder pseudos are DOM-only. Muted like
405
417
  real placeholder text, not currentColor (a full-opacity hint reads as
406
418
  a typed value); --placeholder-color is the per-field prop escape. */
407
- input::placeholder,
408
- textarea::placeholder {
409
- color: var(--placeholder-color, var(--color-text-secondary));
410
- }
411
- /* xplat-web-only:end */
419
+ input::placeholder,
420
+ textarea::placeholder {
421
+ color: var(--placeholder-color, var(--color-text-secondary));
422
+ }
423
+ /* xplat-web-only:end */
412
424
 
413
- /* xplat-web-only:start — form controls don't inherit the body font in the
425
+ /* xplat-web-only:start — form controls don't inherit the body font in the
414
426
  DOM (UA stylesheet wins); without this they render in the UA face. */
415
- input,
416
- textarea,
417
- select,
418
- button {
419
- font-family: inherit;
420
- }
421
- /* xplat-web-only:end */
427
+ input,
428
+ textarea,
429
+ select,
430
+ button {
431
+ font-family: inherit;
432
+ }
433
+ /* xplat-web-only:end */
422
434
 
423
- /* Self-drawn spinner — the border ring is shared (native rotates the
435
+ /* Self-drawn spinner — the border ring is shared (native rotates the
424
436
  view via `animate`; DOM spins via the keyframes below). */
425
- .vx-spinner {
426
- width: 24px;
427
- height: 24px;
428
- border-width: 2px;
429
- border-color: var(--color-text-secondary);
430
- border-right-color: transparent;
431
- border-radius: 50%;
432
- }
433
- /* Pull-to-refresh — the overlay strip hosting the spinner. Its height is
437
+ .vx-spinner {
438
+ width: 24px;
439
+ height: 24px;
440
+ border-width: 2px;
441
+ border-color: var(--color-text-secondary);
442
+ border-right-color: transparent;
443
+ border-radius: 50%;
444
+ }
445
+ /* Pull-to-refresh — the overlay strip hosting the spinner. Its height is
434
446
  the default `refreshThreshold` (64px web / 64dip native); the leaf
435
447
  translates it in/out — reveal math lives in refresh.*.ts. */
436
- .vx-refresh {
437
- display: flex;
438
- flex-direction: column;
439
- align-items: center;
440
- justify-content: center;
441
- height: 64px;
442
- }
443
- /* xplat-web-only:start — the DOM leaf needs explicit clipping plus the
448
+ .vx-refresh {
449
+ display: flex;
450
+ flex-direction: column;
451
+ align-items: center;
452
+ justify-content: center;
453
+ height: 64px;
454
+ }
455
+ /* xplat-web-only:start — the DOM leaf needs explicit clipping plus the
444
456
  absolute overlay; native clips via the wrapper grid
445
457
  (clipsToBounds/ViewGroup) and stacks children in one cell. */
446
- .vx-refresh-wrap {
447
- position: relative;
448
- overflow: hidden;
449
- display: flex;
450
- flex-direction: column;
451
- }
452
- .vx-refresh-wrap > .vx-scroll {
453
- flex: 1;
454
- min-height: 0;
455
- }
456
- .vx-refresh {
457
- position: absolute;
458
- top: 0;
459
- left: 0;
460
- right: 0;
461
- }
462
- /* xplat-web-only:end */
463
- /* xplat-web-only:start — DOM needs an inline-block box + CSS animation;
458
+ .vx-refresh-wrap {
459
+ position: relative;
460
+ overflow: hidden;
461
+ display: flex;
462
+ flex-direction: column;
463
+ }
464
+ .vx-refresh-wrap > .vx-scroll {
465
+ flex: 1;
466
+ min-height: 0;
467
+ }
468
+ .vx-refresh {
469
+ position: absolute;
470
+ top: 0;
471
+ left: 0;
472
+ right: 0;
473
+ }
474
+ /* xplat-web-only:end */
475
+ /* xplat-web-only:start — DOM needs an inline-block box + CSS animation;
464
476
  the native leaf drives view.animate instead */
465
- .vx-spinner {
466
- display: inline-block;
467
- box-sizing: border-box;
468
- animation: vx-spinner-turn 0.8s linear infinite;
469
- }
477
+ .vx-spinner {
478
+ display: inline-block;
479
+ box-sizing: border-box;
480
+ animation: vx-spinner-turn 0.8s linear infinite;
481
+ }
470
482
 
471
- @keyframes vx-spinner-turn {
472
- to {
473
- transform: rotate(360deg);
483
+ @keyframes vx-spinner-turn {
484
+ to {
485
+ transform: rotate(360deg);
486
+ }
474
487
  }
475
- }
476
- /* xplat-web-only:end */
488
+ /* xplat-web-only:end */
477
489
 
478
- /* Self-drawn switch — flex track + thumb. `--on` flips justify-content,
490
+ /* Self-drawn switch — flex track + thumb. `--on` flips justify-content,
479
491
  which slides the thumb; identical geometry on web and native. */
480
- .vx-switch {
481
- width: 48px;
482
- height: 28px;
483
- padding-left: 3px;
484
- padding-right: 3px;
485
- border-radius: 14px;
486
- background-color: var(--color-border);
487
- justify-content: flex-start;
488
- align-items: center;
489
- }
490
- .vx-switch--on {
491
- background-color: var(--color-primary);
492
- justify-content: flex-end;
493
- }
494
- .vx-switch-thumb {
495
- width: 22px;
496
- height: 22px;
497
- border-radius: 50%;
498
- background-color: var(--color-surface);
499
- }
500
- /* xplat-web-only:start — the DOM host is a div; flex + cursor live here */
501
- .vx-switch {
502
- display: flex;
503
- cursor: pointer;
504
- }
505
- /* xplat-web-only:end */
492
+ .vx-switch {
493
+ width: 48px;
494
+ height: 28px;
495
+ padding-left: 3px;
496
+ padding-right: 3px;
497
+ border-radius: 14px;
498
+ background-color: var(--color-border);
499
+ justify-content: flex-start;
500
+ align-items: center;
501
+ }
502
+ .vx-switch--on {
503
+ background-color: var(--color-primary);
504
+ justify-content: flex-end;
505
+ }
506
+ .vx-switch-thumb {
507
+ width: 22px;
508
+ height: 22px;
509
+ border-radius: 50%;
510
+ background-color: var(--color-surface);
511
+ }
512
+ /* xplat-web-only:start — the DOM host is a div; flex + cursor live here */
513
+ .vx-switch {
514
+ display: flex;
515
+ cursor: pointer;
516
+ }
517
+ /* xplat-web-only:end */
506
518
 
507
- /* Self-drawn slider — host is a grid (native) / relative div (web);
519
+ /* Self-drawn slider — host is a grid (native) / relative div (web);
508
520
  track + fill + thumb overlay the same box. */
509
- .vx-slider {
510
- height: 28px;
511
- }
512
- .vx-slider-track {
513
- height: 4px;
514
- border-radius: 2px;
515
- background-color: var(--color-border);
516
- vertical-align: middle;
517
- }
518
- .vx-slider-fill {
519
- height: 4px;
520
- border-radius: 2px;
521
- background-color: var(--color-primary);
522
- vertical-align: middle;
523
- }
524
- .vx-slider-thumb {
525
- width: 20px;
526
- height: 20px;
527
- border-radius: 50%;
528
- background-color: var(--color-primary);
529
- vertical-align: middle;
530
- }
531
- /* xplat-web-only:start — absolute positioning is DOM-only; native grid
521
+ .vx-slider {
522
+ height: 28px;
523
+ }
524
+ .vx-slider-track {
525
+ height: 4px;
526
+ border-radius: 2px;
527
+ background-color: var(--color-border);
528
+ vertical-align: middle;
529
+ }
530
+ .vx-slider-fill {
531
+ height: 4px;
532
+ border-radius: 2px;
533
+ background-color: var(--color-primary);
534
+ vertical-align: middle;
535
+ }
536
+ .vx-slider-thumb {
537
+ width: 20px;
538
+ height: 20px;
539
+ border-radius: 50%;
540
+ background-color: var(--color-primary);
541
+ vertical-align: middle;
542
+ }
543
+ /* xplat-web-only:start — absolute positioning is DOM-only; native grid
532
544
  children center via vertical-align and pin via margin-left. */
533
- .vx-slider {
534
- position: relative;
535
- }
536
- .vx-slider-track,
537
- .vx-slider-fill {
538
- position: absolute;
539
- top: 50%;
540
- left: 0;
541
- right: 0;
542
- transform: translateY(-50%);
543
- }
544
- .vx-slider-thumb {
545
- position: absolute;
546
- top: 50%;
547
- transform: translate(-50%, -50%);
548
- }
549
- /* xplat-web-only:end */
545
+ .vx-slider {
546
+ position: relative;
547
+ }
548
+ .vx-slider-track,
549
+ .vx-slider-fill {
550
+ position: absolute;
551
+ top: 50%;
552
+ left: 0;
553
+ right: 0;
554
+ transform: translateY(-50%);
555
+ }
556
+ .vx-slider-thumb {
557
+ position: absolute;
558
+ top: 50%;
559
+ transform: translate(-50%, -50%);
560
+ }
561
+ /* xplat-web-only:end */
550
562
 
551
- /* xplat-web-only:start — Pager's web leaf is a mandatory scroll-snap row:
563
+ /* xplat-web-only:start — Pager's web leaf is a mandatory scroll-snap row:
552
564
  one page per item at 100% host width so scrollLeft / clientWidth is a
553
565
  clean page index. Native pages are sized by the widget itself, so the
554
566
  whole block is DOM-only. Scrollbar hidden (native has none); contain
555
567
  keeps the swipe from chaining to a parent scroller, matching .vx-scroll. */
556
- .vx-pager {
557
- display: flex;
558
- flex-direction: row;
559
- overflow-x: auto;
560
- overflow-y: hidden;
561
- scroll-snap-type: x mandatory;
562
- overscroll-behavior-x: contain;
563
- scrollbar-width: none;
564
- touch-action: pan-x;
565
- }
566
- .vx-pager::-webkit-scrollbar {
567
- display: none;
568
- }
569
- .vx-pager-page {
570
- flex: 0 0 100%;
571
- min-width: 0;
572
- display: flex;
573
- flex-direction: column;
574
- scroll-snap-align: start;
575
- }
576
- /* xplat-web-only:end */
568
+ .vx-pager {
569
+ display: flex;
570
+ flex-direction: row;
571
+ overflow-x: auto;
572
+ overflow-y: hidden;
573
+ scroll-snap-type: x mandatory;
574
+ overscroll-behavior-x: contain;
575
+ scrollbar-width: none;
576
+ touch-action: pan-x;
577
+ }
578
+ .vx-pager::-webkit-scrollbar {
579
+ display: none;
580
+ }
581
+ .vx-pager-page {
582
+ flex: 0 0 100%;
583
+ min-width: 0;
584
+ display: flex;
585
+ flex-direction: column;
586
+ scroll-snap-align: start;
587
+ }
588
+ /* xplat-web-only:end */
577
589
 
578
- /* Phase-1 self-drawn components — functional geometry + token chrome only;
590
+ /* Phase-1 self-drawn components — functional geometry + token chrome only;
579
591
  visual design is the app's job. */
580
592
 
581
- /* Button — row-direction pressable (vx-pressable already supplies display
593
+ /* Button — row-direction pressable (vx-pressable already supplies display
582
594
  and press feedback). */
583
- .vx-button {
584
- flex-direction: row;
585
- align-items: center;
586
- justify-content: center;
587
- }
595
+ .vx-button {
596
+ flex-direction: row;
597
+ align-items: center;
598
+ justify-content: center;
599
+ }
588
600
 
589
- /* Collapsible/Accordion — column stacks. */
590
- .vx-collapsible,
591
- .vx-collapsible-content,
592
- .vx-accordion,
593
- .vx-accordion-item {
594
- flex-direction: column;
595
- }
601
+ /* Collapsible/Accordion — column stacks. */
602
+ .vx-collapsible,
603
+ .vx-collapsible-content,
604
+ .vx-accordion,
605
+ .vx-accordion-item {
606
+ flex-direction: column;
607
+ }
596
608
 
597
- /* Checkbox — 20px box + check glyph; --on fills with primary. */
598
- .vx-checkbox-row {
599
- flex-direction: row;
600
- align-items: center;
601
- }
602
- .vx-checkbox {
603
- width: 20px;
604
- height: 20px;
605
- border-width: 1px;
606
- border-color: var(--color-border);
607
- border-radius: var(--radius-sm);
608
- align-items: center;
609
- justify-content: center;
610
- }
611
- .vx-checkbox--on .vx-checkbox {
612
- background-color: var(--color-primary);
613
- border-color: var(--color-primary);
614
- }
615
- .vx-checkbox-check {
616
- font-size: 14px;
617
- line-height: 20px;
618
- color: var(--color-onprimary);
619
- }
620
- .vx-checkbox-label {
621
- margin-left: 8px;
622
- }
623
- .vx-checkbox--disabled {
624
- opacity: 0.5;
625
- }
609
+ /* Checkbox — 20px box + check glyph; --on fills with primary. */
610
+ .vx-checkbox-row {
611
+ flex-direction: row;
612
+ align-items: center;
613
+ }
614
+ .vx-checkbox {
615
+ width: 20px;
616
+ height: 20px;
617
+ border-width: 1px;
618
+ border-color: var(--color-border);
619
+ border-radius: var(--radius-sm);
620
+ align-items: center;
621
+ justify-content: center;
622
+ }
623
+ .vx-checkbox--on .vx-checkbox {
624
+ background-color: var(--color-primary);
625
+ border-color: var(--color-primary);
626
+ }
627
+ .vx-checkbox-check {
628
+ font-size: 14px;
629
+ line-height: 20px;
630
+ color: var(--color-onprimary);
631
+ }
632
+ .vx-checkbox--on .vx-checkbox-check {
633
+ color: var(--color-onprimary);
634
+ }
635
+ .vx-checkbox-label {
636
+ margin-left: 8px;
637
+ }
638
+ .vx-checkbox--disabled {
639
+ opacity: 0.5;
640
+ }
626
641
 
627
- /* Radio group — column/row of dot options. */
628
- .vx-radio-group {
629
- flex-direction: column;
630
- }
631
- .vx-radio-group--row {
632
- flex-direction: row;
633
- }
634
- .vx-radio-row {
635
- flex-direction: row;
636
- align-items: center;
637
- }
638
- .vx-radio {
639
- width: 18px;
640
- height: 18px;
641
- border-width: 1px;
642
- border-color: var(--color-border);
643
- border-radius: 50%;
644
- align-items: center;
645
- justify-content: center;
646
- }
647
- .vx-radio--on .vx-radio {
648
- border-color: var(--color-primary);
649
- }
650
- .vx-radio-dot {
651
- width: 8px;
652
- height: 8px;
653
- border-radius: 50%;
654
- background-color: var(--color-primary);
655
- }
656
- .vx-radio-label {
657
- margin-left: 8px;
658
- }
659
- .vx-radio--disabled {
660
- opacity: 0.5;
661
- }
642
+ /* Radio group — column/row of dot options. */
643
+ .vx-radio-group {
644
+ flex-direction: column;
645
+ }
646
+ .vx-radio-group--row {
647
+ flex-direction: row;
648
+ }
649
+ .vx-radio-row {
650
+ flex-direction: row;
651
+ align-items: center;
652
+ }
653
+ .vx-radio {
654
+ width: 18px;
655
+ height: 18px;
656
+ border-width: 1px;
657
+ border-color: var(--color-border);
658
+ border-radius: 50%;
659
+ align-items: center;
660
+ justify-content: center;
661
+ }
662
+ .vx-radio--on .vx-radio {
663
+ border-color: var(--color-primary);
664
+ }
665
+ .vx-radio-dot {
666
+ width: 8px;
667
+ height: 8px;
668
+ border-radius: 50%;
669
+ background-color: var(--color-primary);
670
+ }
671
+ .vx-radio-label {
672
+ margin-left: 8px;
673
+ }
674
+ .vx-radio--disabled {
675
+ opacity: 0.5;
676
+ }
662
677
 
663
- /* SegmentedControl — equal-width segments in a rounded track; `--on`
678
+ /* SegmentedControl — equal-width segments in a rounded track; `--on`
664
679
  raises the selected one. flex-basis is dead on NS (the shorthand parser
665
680
  drops it and the standalone property isn't registered), so width:0 +
666
681
  flex-grow:1 is what distributes segments equally on both engines. */
667
- .vx-segmented {
668
- flex-direction: row;
669
- padding: 3px;
670
- border-width: 1px;
671
- border-color: var(--color-border);
672
- border-radius: var(--radius-md);
673
- corner-shape: squircle;
674
- background-color: var(--color-surface-secondary);
675
- color: var(--color-text);
676
- }
677
- .vx-segment {
678
- flex-grow: 1;
679
- width: 0;
680
- flex-direction: row;
681
- align-items: center;
682
- justify-content: center;
683
- padding-top: 6px;
684
- padding-bottom: 6px;
685
- padding-left: 10px;
686
- padding-right: 10px;
687
- border-radius: var(--radius-sm);
688
- }
689
- .vx-segment--on {
690
- background-color: var(--color-surface);
691
- }
692
- .vx-segment-label {
693
- font-size: 13px;
694
- color: var(--color-text-secondary);
695
- }
696
- .vx-segment--on .vx-segment-label {
697
- color: var(--color-text);
698
- font-weight: 600;
699
- }
700
- .vx-segmented--disabled,
701
- .vx-segment--disabled {
702
- opacity: 0.5;
703
- }
704
- /* xplat-web-only:start — the DOM hosts are divs; flex + cursor live here */
705
- .vx-segmented {
706
- display: flex;
707
- }
708
- .vx-segment {
709
- display: flex;
710
- cursor: pointer;
711
- user-select: none;
712
- }
713
- .vx-segment:focus {
714
- outline: 1px solid var(--color-primary);
715
- outline-offset: -1px;
716
- }
717
- .vx-segment--disabled {
718
- cursor: default;
719
- }
720
- /* xplat-web-only:end */
682
+ .vx-segmented {
683
+ flex-direction: row;
684
+ padding: 3px;
685
+ border-width: 1px;
686
+ border-color: var(--color-border);
687
+ border-radius: var(--radius-md);
688
+ corner-shape: squircle;
689
+ background-color: var(--color-surface-secondary);
690
+ color: var(--color-text);
691
+ }
692
+ .vx-segment {
693
+ flex-grow: 1;
694
+ width: 0;
695
+ flex-direction: row;
696
+ align-items: center;
697
+ justify-content: center;
698
+ padding-top: 6px;
699
+ padding-bottom: 6px;
700
+ padding-left: 10px;
701
+ padding-right: 10px;
702
+ border-radius: var(--radius-sm);
703
+ }
704
+ .vx-segment--on {
705
+ background-color: var(--color-surface);
706
+ }
707
+ .vx-segment-label {
708
+ font-size: 13px;
709
+ color: var(--color-text-secondary);
710
+ }
711
+ .vx-segment--on .vx-segment-label {
712
+ color: var(--color-text);
713
+ font-weight: 600;
714
+ }
715
+ .vx-segmented--disabled,
716
+ .vx-segment--disabled {
717
+ opacity: 0.5;
718
+ }
719
+ /* xplat-web-only:start — the DOM hosts are divs; flex + cursor live here */
720
+ .vx-segmented {
721
+ display: flex;
722
+ }
723
+ .vx-segment {
724
+ display: flex;
725
+ cursor: pointer;
726
+ user-select: none;
727
+ }
728
+ .vx-segment:focus {
729
+ outline: 1px solid var(--color-primary);
730
+ outline-offset: -1px;
731
+ }
732
+ .vx-segment--disabled {
733
+ cursor: default;
734
+ }
735
+ /* xplat-web-only:end */
721
736
 
722
- /* SearchInput — chrome-reset field: bordered shell, leading glyph, the
737
+ /* SearchInput — chrome-reset field: bordered shell, leading glyph, the
723
738
  field owns the remaining width, self-drawn clear. The web leaf hides the
724
739
  engine's own search-cancel button so native's (hand-drawn) matches. */
725
- .vx-search {
726
- flex-direction: row;
727
- align-items: center;
728
- padding-top: 6px;
729
- padding-bottom: 6px;
730
- padding-left: 10px;
731
- padding-right: 6px;
732
- border-width: 1px;
733
- border-color: var(--color-border);
734
- border-radius: var(--radius-md);
735
- corner-shape: squircle;
736
- background-color: var(--color-surface);
737
- color: var(--color-text);
738
- }
739
- .vx-search-icon {
740
- color: var(--color-text-secondary);
741
- }
742
- .vx-search-field {
743
- flex-grow: 1;
744
- padding-left: 8px;
745
- padding-right: 8px;
746
- font-size: 14px;
747
- color: var(--color-text);
748
- }
749
- .vx-search-clear {
750
- width: 24px;
751
- height: 24px;
752
- border-radius: 9999px;
753
- align-items: center;
754
- justify-content: center;
755
- background-color: var(--color-surface-secondary);
756
- color: var(--color-text-secondary);
757
- }
758
- .vx-search--disabled {
759
- opacity: 0.5;
760
- }
761
- /* xplat-web-only:start — the DOM host/field/buttons need display + the UA
740
+ .vx-search {
741
+ flex-direction: row;
742
+ align-items: center;
743
+ padding-top: 6px;
744
+ padding-bottom: 6px;
745
+ padding-left: 10px;
746
+ padding-right: 6px;
747
+ border-width: 1px;
748
+ border-color: var(--color-border);
749
+ border-radius: var(--radius-md);
750
+ corner-shape: squircle;
751
+ background-color: var(--color-surface);
752
+ color: var(--color-text);
753
+ }
754
+ .vx-search-icon {
755
+ color: var(--color-text-secondary);
756
+ }
757
+ .vx-search-field {
758
+ flex-grow: 1;
759
+ padding-left: 8px;
760
+ padding-right: 8px;
761
+ font-size: 14px;
762
+ color: var(--color-text);
763
+ }
764
+ .vx-search-clear {
765
+ width: 24px;
766
+ height: 24px;
767
+ border-radius: 9999px;
768
+ align-items: center;
769
+ justify-content: center;
770
+ background-color: var(--color-surface-secondary);
771
+ color: var(--color-text-secondary);
772
+ }
773
+ .vx-search--disabled {
774
+ opacity: 0.5;
775
+ }
776
+ /* xplat-web-only:start — the DOM host/field/buttons need display + the UA
762
777
  reset; native's flexbox children carry these from the class already. */
763
- .vx-search {
764
- display: flex;
765
- }
766
- .vx-search-icon {
767
- display: inline-flex;
768
- flex: none;
769
- }
770
- .vx-search-field {
771
- min-width: 0;
772
- border: 0;
773
- background: none;
774
- outline: none;
775
- appearance: none;
776
- -webkit-appearance: none;
777
- }
778
- input.vx-search-field::-webkit-search-cancel-button {
779
- display: none;
780
- }
781
- .vx-search-clear {
782
- display: inline-flex;
783
- border: 0;
784
- padding: 0;
785
- cursor: pointer;
786
- }
787
- /* xplat-web-only:end */
788
-
789
- /* Anchored menus — panel + item rows (the Popover leaf positions it). */
790
- .vx-menu {
791
- min-width: 140px;
792
- padding: 4px;
793
- border-width: 1px;
794
- border-color: var(--color-border);
795
- border-radius: var(--radius-md);
796
- corner-shape: squircle;
797
- background-color: var(--color-surface);
798
- color: var(--color-text);
799
- }
800
- .vx-menu-list {
801
- flex-direction: column;
802
- }
803
- .vx-menu-item {
804
- flex-direction: row;
805
- align-items: center;
806
- padding: 6px 10px;
807
- border-radius: var(--radius-sm);
808
- }
809
- .vx-menu-item--disabled {
810
- opacity: 0.5;
811
- }
812
- /* xplat-web-only:start — hover affordance and cursor are DOM-only */
813
- .vx-menu-item {
814
- cursor: pointer;
815
- }
816
- .vx-menu-item:hover {
817
- background-color: var(--color-border);
818
- }
819
- /* xplat-web-only:end */
820
-
821
- /* Badge — inline chip row. */
822
- .vx-badge {
823
- flex-direction: row;
824
- align-items: center;
825
- border-radius: var(--radius-sm);
826
- padding: 1px 6px;
827
- font-size: 12px;
828
- line-height: 16px;
829
- }
830
- /* xplat-web-only:start — span is inline by default; needs inline-flex */
831
- .vx-badge {
832
- display: inline-flex;
833
- }
834
- /* xplat-web-only:end */
778
+ .vx-search {
779
+ display: flex;
780
+ }
781
+ .vx-search-icon {
782
+ display: inline-flex;
783
+ flex: none;
784
+ }
785
+ .vx-search-field {
786
+ min-width: 0;
787
+ border: 0;
788
+ background: none;
789
+ outline: none;
790
+ appearance: none;
791
+ -webkit-appearance: none;
792
+ }
793
+ input.vx-search-field::-webkit-search-cancel-button {
794
+ display: none;
795
+ }
796
+ .vx-search-clear {
797
+ display: inline-flex;
798
+ border: 0;
799
+ padding: 0;
800
+ cursor: pointer;
801
+ }
802
+ /* xplat-web-only:end */
803
+
804
+ /* Anchored menus — panel + item rows (the Popover leaf positions it). */
805
+ .vx-menu {
806
+ min-width: 140px;
807
+ padding: 4px;
808
+ border-width: 1px;
809
+ border-color: var(--color-border);
810
+ border-radius: var(--radius-md);
811
+ corner-shape: squircle;
812
+ background-color: var(--color-surface);
813
+ color: var(--color-text);
814
+ }
815
+ .vx-menu-list {
816
+ flex-direction: column;
817
+ }
818
+ .vx-menu-item {
819
+ flex-direction: row;
820
+ align-items: center;
821
+ padding: 6px 10px;
822
+ border-radius: var(--radius-sm);
823
+ }
824
+ .vx-menu-item--disabled {
825
+ opacity: 0.5;
826
+ }
827
+ /* xplat-web-only:start — hover affordance and cursor are DOM-only */
828
+ .vx-menu-item {
829
+ cursor: pointer;
830
+ }
831
+ .vx-menu-item:hover {
832
+ background-color: var(--color-border);
833
+ }
834
+ /* xplat-web-only:end */
835
+
836
+ /* Badge — inline chip row. */
837
+ .vx-badge {
838
+ flex-direction: row;
839
+ align-items: center;
840
+ border-radius: var(--radius-sm);
841
+ padding: 1px 6px;
842
+ font-size: 12px;
843
+ line-height: 16px;
844
+ }
845
+ /* xplat-web-only:start — span is inline by default; needs inline-flex */
846
+ .vx-badge {
847
+ display: inline-flex;
848
+ }
849
+ /* xplat-web-only:end */
835
850
 
836
- /* Separator — hairline rule; --h spans width, --v spans height. */
837
- .vx-separator--h {
838
- height: 1px;
839
- background-color: var(--color-border);
840
- }
841
- .vx-separator--v {
842
- width: 1px;
843
- align-self: stretch;
844
- background-color: var(--color-border);
845
- }
851
+ /* Separator — hairline rule; --h spans width, --v spans height. */
852
+ .vx-separator--h {
853
+ height: 1px;
854
+ background-color: var(--color-border);
855
+ }
856
+ .vx-separator--v {
857
+ width: 1px;
858
+ align-self: stretch;
859
+ background-color: var(--color-border);
860
+ }
846
861
 
847
- /* Skeleton — placeholder block. */
848
- .vx-skeleton {
849
- background-color: var(--color-border);
850
- border-radius: var(--radius-md);
851
- corner-shape: squircle;
852
- }
862
+ /* Skeleton — placeholder block. */
863
+ .vx-skeleton {
864
+ background-color: var(--color-border);
865
+ border-radius: var(--radius-md);
866
+ corner-shape: squircle;
867
+ }
853
868
 
854
- /* Avatar — centered fallback inside the circle. */
855
- .vx-avatar {
856
- align-items: center;
857
- justify-content: center;
858
- background-color: var(--color-border);
859
- }
860
- .vx-avatar-group {
861
- flex-direction: row;
862
- align-items: center;
863
- }
864
- /* xplat-web-only:start — display:flex for the div-based flex classes */
865
- .vx-collapsible,
866
- .vx-collapsible-content,
867
- .vx-accordion,
868
- .vx-accordion-item,
869
- .vx-checkbox-row,
870
- .vx-checkbox,
871
- .vx-radio-group,
872
- .vx-radio-row,
873
- .vx-radio,
874
- .vx-menu-list,
875
- .vx-menu-item,
876
- .vx-contextmenu,
877
- .vx-avatar,
878
- .vx-avatar-group,
879
- .vx-avatar-group-item {
880
- display: flex;
881
- }
882
- .vx-checkbox-row,
883
- .vx-radio-row {
884
- cursor: pointer;
885
- }
886
- .vx-checkbox-check {
887
- display: block;
888
- }
889
- /* xplat-web-only:end */
869
+ /* Avatar — centered fallback inside the circle. */
870
+ .vx-avatar {
871
+ align-items: center;
872
+ justify-content: center;
873
+ background-color: var(--color-border);
874
+ }
875
+ .vx-avatar-group {
876
+ flex-direction: row;
877
+ align-items: center;
878
+ }
879
+ /* xplat-web-only:start — display:flex for the div-based flex classes */
880
+ .vx-collapsible,
881
+ .vx-collapsible-content,
882
+ .vx-accordion,
883
+ .vx-accordion-item,
884
+ .vx-checkbox-row,
885
+ .vx-checkbox,
886
+ .vx-radio-group,
887
+ .vx-radio-row,
888
+ .vx-radio,
889
+ .vx-menu-list,
890
+ .vx-menu-item,
891
+ .vx-contextmenu,
892
+ .vx-avatar,
893
+ .vx-avatar-group,
894
+ .vx-avatar-group-item {
895
+ display: flex;
896
+ }
897
+ .vx-checkbox-row,
898
+ .vx-radio-row {
899
+ cursor: pointer;
900
+ }
901
+ .vx-checkbox-check {
902
+ display: block;
903
+ }
904
+ /* xplat-web-only:end */
890
905
 
891
- /* Phase-2 form field layer. */
906
+ /* Phase-2 form field layer. */
892
907
 
893
- /* FormField — label / control / message column. */
894
- .vx-field {
895
- flex-direction: column;
896
- }
897
- .vx-field-label {
898
- font-size: 13px;
899
- line-height: 20px;
900
- font-weight: 500;
901
- margin-bottom: 4px;
902
- }
903
- .vx-field-required {
904
- color: var(--color-danger);
905
- }
906
- .vx-field-msg {
907
- font-size: 12px;
908
- line-height: 16px;
909
- margin-top: 4px;
910
- color: var(--color-text-secondary);
911
- }
912
- .vx-field-msg--error {
913
- color: var(--color-danger);
914
- }
915
- .vx-fieldgroup {
916
- flex-direction: column;
917
- }
918
- .vx-fieldgroup-label {
919
- font-size: 13px;
920
- line-height: 20px;
921
- font-weight: 600;
922
- margin-bottom: 8px;
923
- }
908
+ /* FormField — label / control / message column. */
909
+ .vx-field {
910
+ flex-direction: column;
911
+ }
912
+ .vx-field-label {
913
+ font-size: 13px;
914
+ line-height: 20px;
915
+ font-weight: 500;
916
+ margin-bottom: 4px;
917
+ }
918
+ .vx-field-required {
919
+ color: var(--color-danger);
920
+ }
921
+ .vx-field-msg {
922
+ font-size: 12px;
923
+ line-height: 16px;
924
+ margin-top: 4px;
925
+ color: var(--color-text-secondary);
926
+ }
927
+ .vx-field-msg--error {
928
+ color: var(--color-danger);
929
+ }
930
+ .vx-fieldgroup {
931
+ flex-direction: column;
932
+ }
933
+ .vx-fieldgroup-label {
934
+ font-size: 13px;
935
+ line-height: 20px;
936
+ font-weight: 600;
937
+ margin-bottom: 8px;
938
+ }
924
939
 
925
- /* ListItem — a reusable shared row; callers own the surrounding list chrome. */
926
- .vx-listitem {
927
- flex-direction: row;
928
- align-items: center;
929
- width: 100%;
930
- padding: 10px 12px;
931
- gap: 12px;
932
- }
933
- .vx-listitem-leading,
934
- .vx-listitem-trailing {
935
- flex-shrink: 0;
936
- align-items: center;
937
- }
938
- .vx-listitem-content {
939
- flex-direction: column;
940
- flex-grow: 1;
941
- }
942
- .vx-listitem-title {
943
- font-weight: 500;
944
- }
945
- .vx-listitem-supporting {
946
- font-size: 12px;
947
- line-height: 16px;
948
- color: var(--color-text-secondary);
949
- }
940
+ /* ListItem — a reusable shared row; callers own the surrounding list chrome. */
941
+ .vx-listitem {
942
+ flex-direction: row;
943
+ align-items: center;
944
+ width: 100%;
945
+ padding: 10px 12px;
946
+ gap: 12px;
947
+ }
948
+ .vx-listitem-leading,
949
+ .vx-listitem-trailing {
950
+ flex-shrink: 0;
951
+ align-items: center;
952
+ }
953
+ .vx-listitem-content {
954
+ flex-direction: column;
955
+ flex-grow: 1;
956
+ }
957
+ .vx-listitem-title {
958
+ font-weight: 500;
959
+ }
960
+ .vx-listitem-supporting {
961
+ font-size: 12px;
962
+ line-height: 16px;
963
+ color: var(--color-text-secondary);
964
+ }
950
965
 
951
- /* InputNumber — steppers flanking a centered field. */
952
- .vx-inputnumber {
953
- flex-direction: row;
954
- align-items: center;
955
- }
956
- .vx-inputnumber-step {
957
- padding: 4px 10px;
958
- border-width: 1px;
959
- border-color: var(--color-border);
960
- border-radius: var(--radius-sm);
961
- align-items: center;
962
- justify-content: center;
963
- }
964
- .vx-inputnumber-field {
965
- width: 64px;
966
- margin-left: 8px;
967
- margin-right: 8px;
968
- text-align: center;
969
- }
970
- .vx-inputnumber--disabled {
971
- opacity: 0.5;
972
- }
966
+ /* InputNumber — steppers flanking a centered field. */
967
+ .vx-inputnumber {
968
+ flex-direction: row;
969
+ align-items: center;
970
+ }
971
+ .vx-inputnumber-step {
972
+ padding: 4px 10px;
973
+ border-width: 1px;
974
+ border-color: var(--color-border);
975
+ border-radius: var(--radius-sm);
976
+ align-items: center;
977
+ justify-content: center;
978
+ }
979
+ .vx-inputnumber-field {
980
+ width: 64px;
981
+ margin-left: 8px;
982
+ margin-right: 8px;
983
+ text-align: center;
984
+ }
985
+ .vx-inputnumber--disabled {
986
+ opacity: 0.5;
987
+ }
973
988
 
974
- /* PinInput — row of single cells. */
975
- .vx-pininput {
976
- flex-direction: row;
977
- }
978
- .vx-pininput-cell {
979
- width: 40px;
980
- margin-right: 8px;
981
- text-align: center;
982
- }
983
- .vx-pininput--disabled {
984
- opacity: 0.5;
985
- }
989
+ /* PinInput — row of single cells. */
990
+ .vx-pininput {
991
+ flex-direction: row;
992
+ }
993
+ .vx-pininput-cell {
994
+ width: 40px;
995
+ margin-right: 8px;
996
+ text-align: center;
997
+ }
998
+ .vx-pininput--disabled {
999
+ opacity: 0.5;
1000
+ }
986
1001
 
987
- /* Select — trigger row + check column inside the anchored listbox. */
988
- .vx-select-trigger {
989
- flex-direction: row;
990
- align-items: center;
991
- justify-content: space-between;
992
- padding: 6px 10px;
993
- border-width: 1px;
994
- border-color: var(--color-border);
995
- border-radius: var(--radius-sm);
996
- }
997
- .vx-select--disabled {
998
- opacity: 0.5;
999
- }
1000
- .vx-select-placeholder {
1001
- color: var(--color-text-secondary);
1002
- }
1003
- .vx-select-caret {
1004
- margin-left: 8px;
1005
- }
1006
- .vx-select-check {
1007
- width: 16px;
1008
- }
1009
- .vx-select-search {
1010
- margin-bottom: 4px;
1011
- }
1002
+ /* Select — trigger row + check column inside the anchored listbox. */
1003
+ .vx-select-trigger {
1004
+ flex-direction: row;
1005
+ align-items: center;
1006
+ justify-content: space-between;
1007
+ padding: 6px 10px;
1008
+ border-width: 1px;
1009
+ border-color: var(--color-border);
1010
+ border-radius: var(--radius-sm);
1011
+ }
1012
+ .vx-select--disabled {
1013
+ opacity: 0.5;
1014
+ }
1015
+ .vx-select-placeholder {
1016
+ color: var(--color-text-secondary);
1017
+ }
1018
+ .vx-select-caret {
1019
+ margin-left: 8px;
1020
+ }
1021
+ .vx-select-check {
1022
+ width: 16px;
1023
+ }
1024
+ .vx-select-search {
1025
+ margin-bottom: 4px;
1026
+ }
1012
1027
 
1013
- /* InputTags — wrapped chip row + trailing field. */
1014
- .vx-inputtags {
1015
- flex-direction: row;
1016
- flex-wrap: wrap;
1017
- align-items: center;
1018
- }
1019
- .vx-inputtags-tag {
1020
- flex-direction: row;
1021
- align-items: center;
1022
- padding: 1px 6px;
1023
- margin-right: 4px;
1024
- margin-bottom: 4px;
1025
- border-radius: var(--radius-sm);
1026
- background-color: var(--color-border);
1027
- }
1028
- .vx-inputtags-remove {
1029
- margin-left: 4px;
1030
- }
1031
- .vx-inputtags-field {
1032
- flex: 1;
1033
- min-width: 80px;
1034
- }
1035
- .vx-inputtags--disabled {
1036
- opacity: 0.5;
1037
- }
1028
+ /* InputTags — wrapped chip row + trailing field. */
1029
+ .vx-inputtags {
1030
+ flex-direction: row;
1031
+ flex-wrap: wrap;
1032
+ align-items: center;
1033
+ }
1034
+ .vx-inputtags-tag {
1035
+ flex-direction: row;
1036
+ align-items: center;
1037
+ padding: 1px 6px;
1038
+ margin-right: 4px;
1039
+ margin-bottom: 4px;
1040
+ border-radius: var(--radius-sm);
1041
+ background-color: var(--color-border);
1042
+ }
1043
+ .vx-inputtags-remove {
1044
+ margin-left: 4px;
1045
+ }
1046
+ .vx-inputtags-field {
1047
+ flex: 1;
1048
+ min-width: 80px;
1049
+ }
1050
+ .vx-inputtags--disabled {
1051
+ opacity: 0.5;
1052
+ }
1038
1053
 
1039
- /* InputRating — glyph row; --on lights the cell. */
1040
- .vx-rating {
1041
- flex-direction: row;
1042
- }
1043
- .vx-rating-cell {
1044
- padding: 2px;
1045
- }
1046
- .vx-rating-glyph {
1047
- font-size: 20px;
1048
- line-height: 24px;
1049
- color: var(--color-border);
1050
- }
1051
- .vx-rating-cell--on .vx-rating-glyph {
1052
- color: var(--color-primary);
1053
- }
1054
- .vx-rating--disabled {
1055
- opacity: 0.5;
1056
- }
1054
+ /* InputRating — glyph row; --on lights the cell. */
1055
+ .vx-rating {
1056
+ flex-direction: row;
1057
+ }
1058
+ .vx-rating-cell {
1059
+ padding: 2px;
1060
+ }
1061
+ .vx-rating-glyph {
1062
+ font-size: 20px;
1063
+ line-height: 24px;
1064
+ color: var(--color-border);
1065
+ }
1066
+ .vx-rating-cell--on .vx-rating-glyph {
1067
+ color: var(--color-primary);
1068
+ }
1069
+ .vx-rating--disabled {
1070
+ opacity: 0.5;
1071
+ }
1057
1072
 
1058
- /* CheckboxGroup — column/row of checkboxes. */
1059
- .vx-checkbox-group {
1060
- flex-direction: column;
1061
- }
1062
- .vx-checkbox-group--row {
1063
- flex-direction: row;
1064
- }
1065
- /* xplat-web-only:start — display:flex for the div/span flex classes */
1066
- .vx-field,
1067
- .vx-fieldgroup,
1068
- .vx-listitem,
1069
- .vx-listitem-leading,
1070
- .vx-listitem-content,
1071
- .vx-listitem-trailing,
1072
- .vx-inputnumber,
1073
- .vx-inputnumber-step,
1074
- .vx-select-trigger,
1075
- .vx-select-list,
1076
- .vx-inputtags,
1077
- .vx-inputtags-tag,
1078
- .vx-rating,
1079
- .vx-rating-cell,
1080
- .vx-checkbox-group {
1081
- display: flex;
1082
- }
1083
- .vx-rating-cell {
1084
- cursor: pointer;
1085
- }
1086
- /* xplat-web-only:end */
1073
+ /* CheckboxGroup — column/row of checkboxes. */
1074
+ .vx-checkbox-group {
1075
+ flex-direction: column;
1076
+ }
1077
+ .vx-checkbox-group--row {
1078
+ flex-direction: row;
1079
+ }
1080
+ /* xplat-web-only:start — display:flex for the div/span flex classes */
1081
+ .vx-field,
1082
+ .vx-fieldgroup,
1083
+ .vx-listitem,
1084
+ .vx-listitem-leading,
1085
+ .vx-listitem-content,
1086
+ .vx-listitem-trailing,
1087
+ .vx-inputnumber,
1088
+ .vx-inputnumber-step,
1089
+ .vx-select-trigger,
1090
+ .vx-select-list,
1091
+ .vx-inputtags,
1092
+ .vx-inputtags-tag,
1093
+ .vx-rating,
1094
+ .vx-rating-cell,
1095
+ .vx-checkbox-group {
1096
+ display: flex;
1097
+ }
1098
+ .vx-rating-cell {
1099
+ cursor: pointer;
1100
+ }
1101
+ /* xplat-web-only:end */
1087
1102
 
1088
- /* Phase-3 navigation & wayfinding. */
1103
+ /* Phase-3 navigation & wayfinding. */
1089
1104
 
1090
- /* Breadcrumb — inline trail. */
1091
- .vx-breadcrumb {
1092
- flex-direction: row;
1093
- align-items: center;
1094
- flex-wrap: wrap;
1095
- }
1096
- .vx-breadcrumb-item {
1097
- flex-direction: row;
1098
- align-items: center;
1099
- }
1100
- .vx-breadcrumb-sep {
1101
- margin-left: 6px;
1102
- margin-right: 6px;
1103
- color: var(--color-text-secondary);
1104
- }
1105
- .vx-breadcrumb-link {
1106
- color: var(--color-primary);
1107
- }
1105
+ /* Breadcrumb — inline trail. */
1106
+ .vx-breadcrumb {
1107
+ flex-direction: row;
1108
+ align-items: center;
1109
+ flex-wrap: wrap;
1110
+ }
1111
+ .vx-breadcrumb-item {
1112
+ flex-direction: row;
1113
+ align-items: center;
1114
+ }
1115
+ .vx-breadcrumb-sep {
1116
+ margin-left: 6px;
1117
+ margin-right: 6px;
1118
+ color: var(--color-text-secondary);
1119
+ }
1120
+ .vx-breadcrumb-link {
1121
+ color: var(--color-primary);
1122
+ }
1108
1123
 
1109
- /* Pagination — windowed page buttons. */
1110
- .vx-pagination {
1111
- flex-direction: row;
1112
- align-items: center;
1113
- }
1114
- .vx-page-btn {
1115
- min-width: 28px;
1116
- height: 28px;
1117
- padding: 0 6px;
1118
- margin-right: 4px;
1119
- border-width: 1px;
1120
- border-color: var(--color-border);
1121
- border-radius: var(--radius-sm);
1122
- align-items: center;
1123
- justify-content: center;
1124
- }
1125
- .vx-page-btn--on {
1126
- background-color: var(--color-primary);
1127
- border-color: var(--color-primary);
1128
- color: var(--color-onprimary);
1129
- }
1130
- .vx-page-ellipsis {
1131
- margin-right: 4px;
1132
- color: var(--color-text-secondary);
1133
- }
1134
- .vx-pagination--disabled {
1135
- opacity: 0.5;
1136
- }
1124
+ /* Pagination — windowed page buttons. */
1125
+ .vx-pagination {
1126
+ flex-direction: row;
1127
+ align-items: center;
1128
+ }
1129
+ .vx-page-btn {
1130
+ min-width: 28px;
1131
+ height: 28px;
1132
+ padding: 0 6px;
1133
+ margin-right: 4px;
1134
+ border-width: 1px;
1135
+ border-color: var(--color-border);
1136
+ border-radius: var(--radius-sm);
1137
+ align-items: center;
1138
+ justify-content: center;
1139
+ }
1140
+ .vx-page-btn--on {
1141
+ background-color: var(--color-primary);
1142
+ border-color: var(--color-primary);
1143
+ color: var(--color-onprimary);
1144
+ }
1145
+ .vx-page-ellipsis {
1146
+ margin-right: 4px;
1147
+ color: var(--color-text-secondary);
1148
+ }
1149
+ .vx-pagination--disabled {
1150
+ opacity: 0.5;
1151
+ }
1137
1152
 
1138
- /* Stepper — dot + label cells joined by a hairline. */
1139
- .vx-stepper {
1140
- flex-direction: row;
1141
- align-items: center;
1142
- }
1143
- .vx-stepper-cell {
1144
- flex-direction: row;
1145
- align-items: center;
1146
- }
1147
- .vx-stepper-line {
1148
- width: 24px;
1149
- height: 1px;
1150
- background-color: var(--color-border);
1151
- }
1152
- .vx-stepper-line--done {
1153
- background-color: var(--color-primary);
1154
- }
1155
- .vx-step {
1156
- flex-direction: row;
1157
- align-items: center;
1158
- }
1159
- .vx-step-dot {
1160
- width: 24px;
1161
- height: 24px;
1162
- border-width: 1px;
1163
- border-color: var(--color-border);
1164
- border-radius: 50%;
1165
- text-align: center;
1166
- font-size: 12px;
1167
- line-height: 22px;
1168
- color: var(--color-text-secondary);
1169
- }
1170
- .vx-step--on .vx-step-dot {
1171
- border-color: var(--color-primary);
1172
- color: var(--color-primary);
1173
- }
1174
- .vx-step--done .vx-step-dot {
1175
- background-color: var(--color-primary);
1176
- border-color: var(--color-primary);
1177
- color: var(--color-onprimary);
1178
- }
1179
- .vx-step-label {
1180
- margin-left: 6px;
1181
- }
1182
- .vx-step--disabled {
1183
- opacity: 0.5;
1184
- }
1153
+ /* Stepper — dot + label cells joined by a hairline. */
1154
+ .vx-stepper {
1155
+ flex-direction: row;
1156
+ align-items: center;
1157
+ }
1158
+ .vx-stepper-cell {
1159
+ flex-direction: row;
1160
+ align-items: center;
1161
+ }
1162
+ .vx-stepper-line {
1163
+ width: 24px;
1164
+ height: 1px;
1165
+ background-color: var(--color-border);
1166
+ }
1167
+ .vx-stepper-line--done {
1168
+ background-color: var(--color-primary);
1169
+ }
1170
+ .vx-step {
1171
+ flex-direction: row;
1172
+ align-items: center;
1173
+ }
1174
+ .vx-step-dot {
1175
+ width: 24px;
1176
+ height: 24px;
1177
+ border-width: 1px;
1178
+ border-color: var(--color-border);
1179
+ border-radius: 50%;
1180
+ text-align: center;
1181
+ font-size: 12px;
1182
+ line-height: 22px;
1183
+ color: var(--color-text-secondary);
1184
+ }
1185
+ .vx-step--on .vx-step-dot {
1186
+ border-color: var(--color-primary);
1187
+ color: var(--color-primary);
1188
+ }
1189
+ .vx-step--done .vx-step-dot {
1190
+ background-color: var(--color-primary);
1191
+ border-color: var(--color-primary);
1192
+ color: var(--color-onprimary);
1193
+ }
1194
+ .vx-step-label {
1195
+ margin-left: 6px;
1196
+ }
1197
+ .vx-step--disabled {
1198
+ opacity: 0.5;
1199
+ }
1185
1200
 
1186
- /* NavigationMenu — row/column of nav items. */
1187
- .vx-navmenu,
1188
- .vx-navmenu--row {
1189
- flex-direction: row;
1190
- align-items: center;
1191
- }
1192
- .vx-navmenu--col {
1193
- flex-direction: column;
1194
- align-items: flex-start;
1195
- }
1196
- .vx-navmenu-item {
1197
- flex-direction: row;
1198
- align-items: center;
1199
- padding: 6px 10px;
1200
- border-radius: var(--radius-sm);
1201
- }
1202
- .vx-navmenu-item--on {
1203
- background-color: var(--color-border);
1204
- }
1205
- .vx-navmenu-item--disabled {
1206
- opacity: 0.5;
1207
- }
1208
- .vx-navmenu-label {
1209
- margin-left: 4px;
1210
- }
1201
+ /* NavigationMenu — row/column of nav items. */
1202
+ .vx-navmenu,
1203
+ .vx-navmenu--row {
1204
+ flex-direction: row;
1205
+ align-items: center;
1206
+ }
1207
+ .vx-navmenu--col {
1208
+ flex-direction: column;
1209
+ align-items: flex-start;
1210
+ }
1211
+ .vx-navmenu-item {
1212
+ flex-direction: row;
1213
+ align-items: center;
1214
+ padding: 6px 10px;
1215
+ border-radius: var(--radius-sm);
1216
+ }
1217
+ .vx-navmenu-item--on {
1218
+ background-color: var(--color-border);
1219
+ }
1220
+ .vx-navmenu-item--disabled {
1221
+ opacity: 0.5;
1222
+ }
1223
+ .vx-navmenu-label {
1224
+ margin-left: 4px;
1225
+ }
1211
1226
 
1212
- /* CommandPalette — panel centered by the overlay layer. */
1213
- .vx-commandpalette {
1214
- width: 360px;
1215
- /* vw, not %: the overlay-content wrapper shrink-wraps the palette, so a
1216
- * plain % would resolve against the palette's own width (clamping an
1217
- * explicit style.width to 90% of itself). */
1218
- max-width: 90vw;
1219
- padding: 8px;
1220
- border-width: 1px;
1221
- border-color: var(--color-border);
1222
- border-radius: var(--radius-md);
1223
- corner-shape: squircle;
1224
- background-color: var(--color-surface);
1225
- color: var(--color-text);
1226
- }
1227
- .vx-cmdk-search {
1228
- margin-bottom: 4px;
1229
- }
1230
- /* xplat-web-only:start — display, centering, and cursor are DOM-only */
1231
- .vx-breadcrumb,
1232
- .vx-breadcrumb-item,
1233
- .vx-pagination,
1234
- .vx-page-btn,
1235
- .vx-stepper,
1236
- .vx-stepper-cell,
1237
- .vx-step,
1238
- .vx-navmenu,
1239
- .vx-navmenu-item {
1240
- display: flex;
1241
- }
1242
- .vx-cmdk-layer .vx-overlay-content {
1243
- position: absolute;
1244
- top: 15%;
1245
- left: 50%;
1246
- transform: translateX(-50%);
1247
- }
1248
- .vx-page-btn,
1249
- .vx-breadcrumb-link,
1250
- .vx-step,
1251
- .vx-navmenu-item {
1252
- cursor: pointer;
1253
- }
1254
- /* xplat-web-only:end */
1227
+ /* CommandPalette — panel centered by the overlay layer. */
1228
+ .vx-commandpalette {
1229
+ width: 360px;
1230
+ padding: 8px;
1231
+ border-width: 1px;
1232
+ border-color: var(--color-border);
1233
+ border-radius: var(--radius-md);
1234
+ corner-shape: squircle;
1235
+ background-color: var(--color-surface);
1236
+ color: var(--color-text);
1237
+ }
1238
+ /* xplat-web-only:start — native uses Screen.mainScreen.widthDIPs */
1239
+ .vx-commandpalette {
1240
+ /* The overlay-content wrapper shrink-wraps the palette, so % would resolve
1241
+ * against the palette itself instead of the available viewport. */
1242
+ max-width: 90vw;
1243
+ }
1244
+ /* xplat-web-only:end */
1245
+ .vx-cmdk-search {
1246
+ margin-bottom: 4px;
1247
+ }
1248
+ /* xplat-web-only:start — display, centering, and cursor are DOM-only */
1249
+ .vx-breadcrumb,
1250
+ .vx-breadcrumb-item,
1251
+ .vx-pagination,
1252
+ .vx-page-btn,
1253
+ .vx-stepper,
1254
+ .vx-stepper-cell,
1255
+ .vx-step,
1256
+ .vx-navmenu,
1257
+ .vx-navmenu-item {
1258
+ display: flex;
1259
+ }
1260
+ .vx-cmdk-layer .vx-overlay-content {
1261
+ position: absolute;
1262
+ top: 15%;
1263
+ left: 50%;
1264
+ transform: translateX(-50%);
1265
+ }
1266
+ .vx-page-btn,
1267
+ .vx-breadcrumb-link,
1268
+ .vx-step,
1269
+ .vx-navmenu-item {
1270
+ cursor: pointer;
1271
+ }
1272
+ /* xplat-web-only:end */
1255
1273
 
1256
- /* Phase-4 data display. */
1274
+ /* Phase-4 data display. */
1257
1275
 
1258
- /* Table — header + bounded rows (unvirtualized by design). */
1259
- .vx-table {
1260
- flex-direction: column;
1261
- border-width: 1px;
1262
- border-color: var(--color-border);
1263
- border-radius: var(--radius-md);
1264
- }
1265
- .vx-table-head,
1266
- .vx-table-row {
1267
- flex-direction: row;
1268
- }
1269
- .vx-table-th {
1270
- font-weight: 600;
1271
- }
1272
- .vx-table-cell {
1273
- padding: 8px 12px;
1274
- }
1275
- .vx-table-head {
1276
- border-bottom-width: 1px;
1277
- border-bottom-color: var(--color-border);
1278
- }
1279
- .vx-table-empty {
1280
- padding: 16px;
1281
- align-items: center;
1282
- }
1276
+ /* Table — header + bounded rows (unvirtualized by design). */
1277
+ .vx-table {
1278
+ flex-direction: column;
1279
+ border-width: 1px;
1280
+ border-color: var(--color-border);
1281
+ border-radius: var(--radius-md);
1282
+ }
1283
+ .vx-table-head,
1284
+ .vx-table-row {
1285
+ flex-direction: row;
1286
+ }
1287
+ .vx-table-th {
1288
+ font-weight: 600;
1289
+ }
1290
+ .vx-table-cell {
1291
+ padding: 8px 12px;
1292
+ }
1293
+ .vx-table-head {
1294
+ border-bottom-width: 1px;
1295
+ border-bottom-color: var(--color-border);
1296
+ }
1297
+ .vx-table-empty {
1298
+ padding: 16px;
1299
+ align-items: center;
1300
+ }
1283
1301
 
1284
- /* Timeline — dot + connector rail beside content. */
1285
- .vx-timeline {
1286
- flex-direction: column;
1287
- }
1288
- .vx-timeline-item {
1289
- flex-direction: row;
1290
- }
1291
- .vx-timeline-rail {
1292
- flex-direction: column;
1293
- align-items: center;
1294
- width: 20px;
1295
- }
1296
- .vx-timeline-dot {
1297
- width: 10px;
1298
- height: 10px;
1299
- margin-top: 5px;
1300
- border-radius: 50%;
1301
- background-color: var(--color-primary);
1302
- align-items: center;
1303
- justify-content: center;
1304
- }
1305
- .vx-timeline-line {
1306
- width: 1px;
1307
- flex: 1;
1308
- background-color: var(--color-border);
1309
- }
1310
- .vx-timeline-body {
1311
- flex-direction: column;
1312
- padding-left: 10px;
1313
- padding-bottom: 16px;
1314
- }
1315
- .vx-timeline-time {
1316
- font-size: 12px;
1317
- line-height: 16px;
1318
- color: var(--color-text-secondary);
1319
- }
1320
- .vx-timeline-title {
1321
- font-weight: 600;
1322
- }
1323
- .vx-timeline-desc {
1324
- color: var(--color-text-secondary);
1325
- }
1302
+ /* Timeline — dot + connector rail beside content. */
1303
+ .vx-timeline {
1304
+ flex-direction: column;
1305
+ }
1306
+ .vx-timeline-item {
1307
+ flex-direction: row;
1308
+ }
1309
+ .vx-timeline-rail {
1310
+ flex-direction: column;
1311
+ align-items: center;
1312
+ width: 20px;
1313
+ }
1314
+ .vx-timeline-dot {
1315
+ width: 10px;
1316
+ height: 10px;
1317
+ margin-top: 5px;
1318
+ border-radius: 50%;
1319
+ background-color: var(--color-primary);
1320
+ align-items: center;
1321
+ justify-content: center;
1322
+ }
1323
+ .vx-timeline-line {
1324
+ width: 1px;
1325
+ flex: 1;
1326
+ background-color: var(--color-border);
1327
+ }
1328
+ .vx-timeline-body {
1329
+ flex-direction: column;
1330
+ padding-left: 10px;
1331
+ padding-bottom: 16px;
1332
+ }
1333
+ .vx-timeline-time {
1334
+ font-size: 12px;
1335
+ line-height: 16px;
1336
+ color: var(--color-text-secondary);
1337
+ }
1338
+ .vx-timeline-title {
1339
+ font-weight: 600;
1340
+ }
1341
+ .vx-timeline-desc {
1342
+ color: var(--color-text-secondary);
1343
+ }
1326
1344
 
1327
- /* Tree — indented collapsible rows. */
1328
- .vx-tree {
1329
- flex-direction: column;
1330
- }
1331
- .vx-tree-node {
1332
- flex-direction: column;
1333
- }
1334
- .vx-tree-row {
1335
- flex-direction: row;
1336
- align-items: center;
1337
- padding: 4px 0;
1338
- }
1339
- .vx-tree-row--disabled {
1340
- opacity: 0.5;
1341
- }
1342
- .vx-tree-caret {
1343
- width: 14px;
1344
- margin-left: 4px;
1345
- }
1345
+ /* Tree — indented collapsible rows. */
1346
+ .vx-tree {
1347
+ flex-direction: column;
1348
+ }
1349
+ .vx-tree-node {
1350
+ flex-direction: column;
1351
+ }
1352
+ .vx-tree-row {
1353
+ flex-direction: row;
1354
+ align-items: center;
1355
+ padding: 4px 0;
1356
+ }
1357
+ .vx-tree-row--disabled {
1358
+ opacity: 0.5;
1359
+ }
1360
+ .vx-tree-caret {
1361
+ width: 14px;
1362
+ margin-left: 4px;
1363
+ }
1346
1364
 
1347
- /* Alert — icon + body callout; tone picks border/text colors. */
1348
- .vx-alert {
1349
- flex-direction: row;
1350
- padding: 10px 12px;
1351
- border-width: 1px;
1352
- border-radius: var(--radius-md);
1353
- corner-shape: squircle;
1354
- border-color: var(--color-border);
1355
- }
1356
- .vx-alert-body {
1357
- flex-direction: column;
1358
- flex: 1;
1359
- margin-left: 8px;
1360
- }
1361
- .vx-alert-title {
1362
- font-weight: 600;
1363
- }
1364
- .vx-alert--error {
1365
- border-color: var(--color-danger);
1366
- }
1367
- .vx-alert--warning {
1368
- border-color: var(--color-warning, #d9a400);
1369
- }
1370
- .vx-alert--success {
1371
- border-color: var(--color-success, #2e9e5b);
1372
- }
1373
- .vx-alert--info {
1374
- border-color: var(--color-primary);
1375
- }
1365
+ /* Alert — icon + body callout; tone picks border/text colors. */
1366
+ .vx-alert {
1367
+ flex-direction: row;
1368
+ padding: 10px 12px;
1369
+ border-width: 1px;
1370
+ border-radius: var(--radius-md);
1371
+ corner-shape: squircle;
1372
+ border-color: var(--color-border);
1373
+ }
1374
+ .vx-alert-body {
1375
+ flex-direction: column;
1376
+ flex: 1;
1377
+ margin-left: 8px;
1378
+ }
1379
+ .vx-alert-title {
1380
+ font-weight: 600;
1381
+ }
1382
+ .vx-alert--error {
1383
+ border-color: var(--color-danger);
1384
+ }
1385
+ .vx-alert--warning {
1386
+ border-color: var(--color-warning, #d9a400);
1387
+ }
1388
+ .vx-alert--success {
1389
+ border-color: var(--color-success, #2e9e5b);
1390
+ }
1391
+ .vx-alert--info {
1392
+ border-color: var(--color-primary);
1393
+ }
1376
1394
 
1377
- /* Card — sectioned container. */
1378
- .vx-card {
1379
- flex-direction: column;
1380
- border-width: 1px;
1381
- border-color: var(--color-border);
1382
- border-radius: var(--radius-md);
1383
- corner-shape: squircle;
1384
- background-color: var(--color-surface);
1385
- }
1386
- .vx-card-header {
1387
- padding: 12px;
1388
- border-bottom-width: 1px;
1389
- border-bottom-color: var(--color-border);
1390
- }
1391
- .vx-card-body {
1392
- padding: 12px;
1393
- flex-direction: column;
1394
- }
1395
- .vx-card-footer {
1396
- padding: 12px;
1397
- border-top-width: 1px;
1398
- border-top-color: var(--color-border);
1399
- }
1395
+ /* Card — sectioned container. */
1396
+ .vx-card {
1397
+ flex-direction: column;
1398
+ border-width: 1px;
1399
+ border-color: var(--color-border);
1400
+ border-radius: var(--radius-md);
1401
+ corner-shape: squircle;
1402
+ background-color: var(--color-surface);
1403
+ }
1404
+ .vx-card-header {
1405
+ padding: 12px;
1406
+ border-bottom-width: 1px;
1407
+ border-bottom-color: var(--color-border);
1408
+ }
1409
+ .vx-card-body {
1410
+ padding: 12px;
1411
+ flex-direction: column;
1412
+ }
1413
+ .vx-card-footer {
1414
+ padding: 12px;
1415
+ border-top-width: 1px;
1416
+ border-top-color: var(--color-border);
1417
+ }
1400
1418
 
1401
- /* Chip — pressable pill with selected + remove states. */
1402
- .vx-chip {
1403
- flex-direction: row;
1404
- align-items: center;
1405
- padding: 3px 10px;
1406
- border-width: 1px;
1407
- border-color: var(--color-border);
1408
- border-radius: var(--radius-md);
1409
- corner-shape: squircle;
1410
- font-size: 13px;
1411
- }
1412
- .vx-chip--on {
1413
- background-color: var(--color-primary);
1414
- border-color: var(--color-primary);
1415
- color: var(--color-onprimary);
1416
- }
1417
- .vx-chip--disabled {
1418
- opacity: 0.5;
1419
- }
1420
- .vx-chip-remove {
1421
- margin-left: 6px;
1422
- }
1419
+ /* Chip — pressable pill with selected + remove states. */
1420
+ .vx-chip {
1421
+ flex-direction: row;
1422
+ align-items: center;
1423
+ padding: 3px 10px;
1424
+ border-width: 1px;
1425
+ border-color: var(--color-border);
1426
+ border-radius: var(--radius-md);
1427
+ corner-shape: squircle;
1428
+ font-size: 13px;
1429
+ }
1430
+ .vx-chip--on {
1431
+ background-color: var(--color-primary);
1432
+ border-color: var(--color-primary);
1433
+ color: var(--color-onprimary);
1434
+ }
1435
+ .vx-chip--disabled {
1436
+ opacity: 0.5;
1437
+ }
1438
+ .vx-chip-remove {
1439
+ margin-left: 6px;
1440
+ }
1423
1441
 
1424
- /* Kbd — keycap glyph. */
1425
- .vx-kbd {
1426
- padding: 1px 5px;
1427
- border-width: 1px;
1428
- border-color: var(--color-border);
1429
- border-bottom-width: 2px;
1430
- border-radius: var(--radius-sm);
1431
- font-size: 12px;
1432
- }
1442
+ /* Kbd — keycap glyph. */
1443
+ .vx-kbd {
1444
+ padding: 1px 5px;
1445
+ border-width: 1px;
1446
+ border-color: var(--color-border);
1447
+ border-bottom-width: 2px;
1448
+ border-radius: var(--radius-sm);
1449
+ font-size: 12px;
1450
+ }
1433
1451
 
1434
- /* Empty — centered placeholder. */
1435
- .vx-empty {
1436
- flex-direction: column;
1437
- align-items: center;
1438
- padding: 24px;
1439
- }
1440
- .vx-empty-title {
1441
- font-weight: 600;
1442
- margin-top: 8px;
1443
- }
1444
- .vx-empty-desc {
1445
- color: var(--color-text-secondary);
1446
- margin-top: 2px;
1447
- }
1452
+ /* Empty — centered placeholder. */
1453
+ .vx-empty {
1454
+ flex-direction: column;
1455
+ align-items: center;
1456
+ padding: 24px;
1457
+ }
1458
+ .vx-empty-title {
1459
+ font-weight: 600;
1460
+ margin-top: 8px;
1461
+ }
1462
+ .vx-empty-desc {
1463
+ color: var(--color-text-secondary);
1464
+ margin-top: 2px;
1465
+ }
1448
1466
 
1449
- /* Banner — notice strip. */
1450
- .vx-banner {
1451
- flex-direction: row;
1452
- align-items: center;
1453
- padding: 8px 12px;
1454
- border-width: 1px;
1455
- border-color: var(--color-border);
1456
- border-radius: var(--radius-md);
1457
- background-color: var(--color-surface);
1458
- }
1459
- .vx-banner-body {
1460
- flex-direction: column;
1461
- flex: 1;
1462
- margin-left: 8px;
1463
- }
1464
- .vx-banner-dismiss {
1465
- margin-left: 8px;
1466
- }
1467
+ /* Banner — notice strip. */
1468
+ .vx-banner {
1469
+ flex-direction: row;
1470
+ align-items: center;
1471
+ padding: 8px 12px;
1472
+ border-width: 1px;
1473
+ border-color: var(--color-border);
1474
+ border-radius: var(--radius-md);
1475
+ background-color: var(--color-surface);
1476
+ }
1477
+ .vx-banner-body {
1478
+ flex-direction: column;
1479
+ flex: 1;
1480
+ margin-left: 8px;
1481
+ }
1482
+ .vx-banner-dismiss {
1483
+ margin-left: 8px;
1484
+ }
1467
1485
 
1468
- /* User — avatar + text column. */
1469
- .vx-user {
1470
- flex-direction: row;
1471
- align-items: center;
1472
- }
1473
- .vx-user-body {
1474
- flex-direction: column;
1475
- margin-left: 10px;
1476
- }
1477
- .vx-user-name {
1478
- font-weight: 600;
1479
- }
1480
- .vx-user-desc {
1481
- font-size: 12px;
1482
- line-height: 16px;
1483
- color: var(--color-text-secondary);
1484
- }
1486
+ /* User — avatar + text column. */
1487
+ .vx-user {
1488
+ flex-direction: row;
1489
+ align-items: center;
1490
+ }
1491
+ .vx-user-body {
1492
+ flex-direction: column;
1493
+ margin-left: 10px;
1494
+ }
1495
+ .vx-user-name {
1496
+ font-weight: 600;
1497
+ }
1498
+ .vx-user-desc {
1499
+ font-size: 12px;
1500
+ line-height: 16px;
1501
+ color: var(--color-text-secondary);
1502
+ }
1485
1503
 
1486
- /* ProgressGroup — stacked labeled meters. */
1487
- .vx-progressgroup {
1488
- flex-direction: column;
1489
- }
1490
- .vx-progressgroup-row {
1491
- flex-direction: row;
1492
- align-items: center;
1493
- }
1494
- .vx-progressgroup-label {
1495
- margin-left: 8px;
1496
- }
1497
- /* xplat-web-only:start — display:flex for the div-based flex classes */
1498
- .vx-table,
1499
- .vx-table-head,
1500
- .vx-table-row,
1501
- .vx-timeline,
1502
- .vx-timeline-item,
1503
- .vx-timeline-rail,
1504
- .vx-timeline-body,
1505
- .vx-tree,
1506
- .vx-tree-node,
1507
- .vx-tree-row,
1508
- .vx-alert,
1509
- .vx-alert-body,
1510
- .vx-card,
1511
- .vx-card-body,
1512
- .vx-banner,
1513
- .vx-banner-body,
1514
- .vx-user,
1515
- .vx-user-body,
1516
- .vx-progressgroup,
1517
- .vx-progressgroup-row {
1518
- display: flex;
1519
- }
1520
- .vx-table-row--pressable,
1521
- .vx-tree-row,
1522
- .vx-user--pressable,
1523
- .vx-banner-dismiss,
1524
- .vx-chip-remove {
1525
- cursor: pointer;
1526
- }
1527
- /* xplat-web-only:end */
1504
+ /* ProgressGroup — stacked labeled meters. */
1505
+ .vx-progressgroup {
1506
+ flex-direction: column;
1507
+ }
1508
+ .vx-progressgroup-row {
1509
+ flex-direction: row;
1510
+ align-items: center;
1511
+ }
1512
+ .vx-progressgroup-label {
1513
+ margin-left: 8px;
1514
+ }
1515
+ /* xplat-web-only:start — display:flex for the div-based flex classes */
1516
+ .vx-table,
1517
+ .vx-table-head,
1518
+ .vx-table-row,
1519
+ .vx-timeline,
1520
+ .vx-timeline-item,
1521
+ .vx-timeline-rail,
1522
+ .vx-timeline-body,
1523
+ .vx-tree,
1524
+ .vx-tree-node,
1525
+ .vx-tree-row,
1526
+ .vx-alert,
1527
+ .vx-alert-body,
1528
+ .vx-card,
1529
+ .vx-card-body,
1530
+ .vx-banner,
1531
+ .vx-banner-body,
1532
+ .vx-user,
1533
+ .vx-user-body,
1534
+ .vx-progressgroup,
1535
+ .vx-progressgroup-row {
1536
+ display: flex;
1537
+ }
1538
+ .vx-table-row--pressable,
1539
+ .vx-tree-row,
1540
+ .vx-user--pressable,
1541
+ .vx-banner-dismiss,
1542
+ .vx-chip-remove {
1543
+ cursor: pointer;
1544
+ }
1545
+ /* xplat-web-only:end */
1528
1546
 
1529
- .vx-heading {
1530
- margin: 0;
1531
- }
1532
- /* NativeScript reads the numeric prefix of font-size; keep this scale in px. */
1533
- .vx-h1 {
1534
- font-size: 32px;
1535
- font-weight: bold;
1536
- }
1537
- .vx-h2 {
1538
- font-size: 24px;
1539
- font-weight: bold;
1540
- }
1541
- .vx-h3 {
1542
- font-size: 18.72px;
1543
- font-weight: bold;
1544
- }
1545
- .vx-h4 {
1546
- font-size: 16px;
1547
- font-weight: bold;
1548
- }
1549
- .vx-h5 {
1550
- font-size: 13.28px;
1551
- font-weight: bold;
1552
- }
1553
- .vx-h6 {
1554
- font-size: 10.72px;
1555
- font-weight: bold;
1556
- }
1547
+ .vx-heading {
1548
+ margin: 0;
1549
+ }
1550
+ /* NativeScript reads the numeric prefix of font-size; keep this scale in px. */
1551
+ .vx-h1 {
1552
+ font-size: 32px;
1553
+ font-weight: bold;
1554
+ }
1555
+ .vx-h2 {
1556
+ font-size: 24px;
1557
+ font-weight: bold;
1558
+ }
1559
+ .vx-h3 {
1560
+ font-size: 18.72px;
1561
+ font-weight: bold;
1562
+ }
1563
+ .vx-h4 {
1564
+ font-size: 16px;
1565
+ font-weight: bold;
1566
+ }
1567
+ .vx-h5 {
1568
+ font-size: 13.28px;
1569
+ font-weight: bold;
1570
+ }
1571
+ .vx-h6 {
1572
+ font-size: 10.72px;
1573
+ font-weight: bold;
1574
+ }
1557
1575
 
1558
- /* utilities (hand-written subset — do NOT adopt Tailwind until
1576
+ /* utilities (hand-written subset — do NOT adopt Tailwind until
1559
1577
  NativeScript/tailwind#226 is resolved; the plugin strips core-supported
1560
1578
  props incl. gap, drops all @media, and rewrites invisible→collapse) */
1561
1579
 
1562
- /* flex-1 keeps Tailwind v4's exact emission (`flex: 1 1 0%`) — web gets
1580
+ /* flex-1 keeps Tailwind v4's exact emission (`flex: 1 1 0%`) — web gets
1563
1581
  identical semantics, NS parses grow/shrink out of the shorthand and
1564
1582
  ignores the basis token it can't model. */
1565
- .flex-1 {
1566
- flex: 1 1 0%;
1567
- }
1568
- /* Flex divergence guards — NS FlexboxLayout diverges from web flexbox on
1583
+ .flex-1 {
1584
+ flex: 1 1 0%;
1585
+ }
1586
+ /* Flex divergence guards — NS FlexboxLayout diverges from web flexbox on
1569
1587
  * shrinkability: grow children never shrink below intrinsic width, fixed
1570
1588
  * children get crushed, and auto-height rows re-measure grow children at
1571
1589
  * zero height. The shared `* { flex-shrink: 0 }` default (RN semantics)
1572
1590
  * makes .shrink the opt-in for a shrinkable child; min-w-0/min-h-0 lower
1573
1591
  * the min-content floor so it can actually shrink, and shrink-0 remains
1574
1592
  * for local overrides. All four are plain CSS on web. */
1575
- .min-w-0 {
1576
- min-width: 0;
1577
- }
1578
- .min-h-0 {
1579
- min-height: 0;
1580
- }
1581
- .shrink {
1582
- flex-shrink: 1;
1583
- }
1584
- .shrink-0 {
1585
- flex-shrink: 0;
1586
- }
1587
- .grow {
1588
- flex-grow: 1;
1589
- }
1590
- .items-center {
1591
- align-items: center;
1592
- }
1593
- .items-baseline {
1594
- align-items: baseline;
1595
- }
1596
- .items-start {
1597
- align-items: flex-start;
1598
- }
1599
- .justify-center {
1600
- justify-content: center;
1601
- }
1602
- .justify-between {
1603
- justify-content: space-between;
1604
- }
1605
- .flex-wrap {
1606
- flex-wrap: wrap;
1607
- }
1608
- .gap-4 {
1609
- gap: 16px;
1610
- }
1611
- .gap-3 {
1612
- gap: 12px;
1613
- }
1614
- .gap-6 {
1615
- gap: 24px;
1616
- }
1593
+ .min-w-0 {
1594
+ min-width: 0;
1595
+ }
1596
+ .min-h-0 {
1597
+ min-height: 0;
1598
+ }
1599
+ .shrink {
1600
+ flex-shrink: 1;
1601
+ }
1602
+ .shrink-0 {
1603
+ flex-shrink: 0;
1604
+ }
1605
+ .grow {
1606
+ flex-grow: 1;
1607
+ }
1608
+ .items-center {
1609
+ align-items: center;
1610
+ }
1611
+ .items-baseline {
1612
+ align-items: baseline;
1613
+ }
1614
+ .items-start {
1615
+ align-items: flex-start;
1616
+ }
1617
+ .justify-center {
1618
+ justify-content: center;
1619
+ }
1620
+ .justify-between {
1621
+ justify-content: space-between;
1622
+ }
1623
+ .flex-wrap {
1624
+ flex-wrap: wrap;
1625
+ }
1626
+ .gap-4 {
1627
+ gap: 16px;
1628
+ }
1629
+ .gap-3 {
1630
+ gap: 12px;
1631
+ }
1632
+ .gap-6 {
1633
+ gap: 24px;
1634
+ }
1617
1635
 
1618
- /* line-height is REQUIRED on native: without it a Label's measured height
1619
- misses the font's line metrics, and Android clips the descenders/bottom
1620
- of the glyphs (observed: section-labels 21–29px bounds for 12sp text at
1621
- 3.25x). Tailwind pairs every text-* size with a leading, same here. */
1622
- .text-2xl {
1623
- font-size: 28px;
1624
- line-height: 36px;
1625
- }
1626
- .text-xl {
1627
- font-size: 20px;
1628
- line-height: 28px;
1629
- }
1630
- .text-lg {
1631
- font-size: 18px;
1632
- line-height: 28px;
1633
- }
1634
- .text-sm {
1635
- font-size: 13px;
1636
- line-height: 20px;
1637
- }
1638
- .font-bold {
1639
- font-weight: bold;
1640
- }
1641
- .font-semibold {
1642
- font-weight: 600;
1643
- }
1644
- .text-muted {
1645
- color: var(--color-text-secondary);
1646
- }
1636
+ /* Native Labels keep their natural font height even with line-height set, so
1637
+ min-height aligns utilities whose natural metrics fall below their leading.
1638
+ Tailwind pairs every text-* size with a leading, same here. */
1639
+ .text-2xl {
1640
+ font-size: 28px;
1641
+ line-height: 36px;
1642
+ min-height: 36px;
1643
+ }
1644
+ .text-xl {
1645
+ font-size: 20px;
1646
+ line-height: 28px;
1647
+ min-height: 28px;
1648
+ }
1649
+ .text-lg {
1650
+ font-size: 18px;
1651
+ line-height: 28px;
1652
+ min-height: 28px;
1653
+ }
1654
+ .text-sm {
1655
+ font-size: 13px;
1656
+ line-height: 20px;
1657
+ min-height: 20px;
1658
+ }
1659
+ .font-bold {
1660
+ font-weight: bold;
1661
+ }
1662
+ .font-semibold {
1663
+ font-weight: 600;
1664
+ }
1665
+ .text-muted {
1666
+ color: var(--color-text-secondary);
1667
+ }
1647
1668
 
1648
- /* Corner utilities — consume the --radius-* scale and carry the smooth
1669
+ /* Corner utilities — consume the --radius-* scale and carry the smooth
1649
1670
  (squircle) corner treatment. Circles stay circular: .rounded-full omits
1650
1671
  corner-shape deliberately (squircle at full radius = a squircle disk,
1651
1672
  not a circle). Opt out per element with `corner-shape: round`. */
1652
- .rounded-sm {
1653
- border-radius: var(--radius-sm);
1654
- corner-shape: squircle;
1655
- }
1656
- .rounded-md {
1657
- border-radius: var(--radius-md);
1658
- corner-shape: squircle;
1659
- }
1660
- .rounded-lg {
1661
- border-radius: var(--radius-lg);
1662
- corner-shape: squircle;
1663
- }
1664
- .rounded-xl {
1665
- border-radius: var(--radius-xl);
1666
- corner-shape: squircle;
1667
- }
1668
- .rounded-full {
1669
- border-radius: 9999px;
1670
- }
1673
+ .rounded-sm {
1674
+ border-radius: var(--radius-sm);
1675
+ corner-shape: squircle;
1676
+ }
1677
+ .rounded-md {
1678
+ border-radius: var(--radius-md);
1679
+ corner-shape: squircle;
1680
+ }
1681
+ .rounded-lg {
1682
+ border-radius: var(--radius-lg);
1683
+ corner-shape: squircle;
1684
+ }
1685
+ .rounded-xl {
1686
+ border-radius: var(--radius-xl);
1687
+ corner-shape: squircle;
1688
+ }
1689
+ .rounded-full {
1690
+ border-radius: 9999px;
1691
+ }
1671
1692
 
1672
- .bg-primary {
1673
- background-color: var(--color-primary);
1674
- }
1675
- .text-onprimary {
1676
- color: var(--color-onprimary);
1677
- }
1678
- .bg-danger {
1679
- background-color: var(--color-danger);
1680
- }
1681
- /* xplat-web-only:start — box-shadow is inert on native (elevation is
1693
+ .bg-primary {
1694
+ background-color: var(--color-primary);
1695
+ }
1696
+ .text-onprimary {
1697
+ color: var(--color-onprimary);
1698
+ }
1699
+ .bg-danger {
1700
+ background-color: var(--color-danger);
1701
+ }
1702
+ /* xplat-web-only:start — box-shadow is inert on native (elevation is
1682
1703
  Android-only; no shared mapping yet) */
1683
- .shadow-2 {
1684
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
1685
- }
1686
- /* xplat-web-only:end */
1687
-
1688
- .btn {
1689
- padding-top: 10px;
1690
- padding-right: 16px;
1691
- padding-bottom: 10px;
1692
- padding-left: 16px;
1693
- border-radius: var(--radius-md);
1694
- corner-shape: squircle;
1695
- font-size: 14px;
1696
- font-weight: 500;
1697
- align-items: center;
1698
- justify-content: center;
1699
- text-decoration: none;
1700
- color: var(--color-text);
1701
- }
1704
+ .shadow-2 {
1705
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
1706
+ }
1707
+ /* xplat-web-only:end */
1708
+
1709
+ .btn {
1710
+ padding-top: 10px;
1711
+ padding-right: 16px;
1712
+ padding-bottom: 10px;
1713
+ padding-left: 16px;
1714
+ border-radius: var(--radius-md);
1715
+ corner-shape: squircle;
1716
+ font-size: 14px;
1717
+ font-weight: 500;
1718
+ align-items: center;
1719
+ justify-content: center;
1720
+ text-decoration: none;
1721
+ color: var(--color-text);
1722
+ }
1702
1723
 
1703
- /* Secondary = the same control reading quiet: hairline border, surface
1724
+ /* Secondary = the same control reading quiet: hairline border, surface
1704
1725
  fill, body-text label. Reserve .bg-primary for the one decisive action
1705
1726
  in a group. */
1706
- .btn-secondary {
1707
- background-color: var(--color-surface);
1708
- border-width: 1px;
1709
- border-color: var(--color-border);
1710
- color: var(--color-text);
1711
- }
1727
+ .btn-secondary {
1728
+ background-color: var(--color-surface);
1729
+ border-width: 1px;
1730
+ border-color: var(--color-border);
1731
+ color: var(--color-text);
1732
+ }
1712
1733
 
1713
- /* xplat-web-only:start — controls that render as real <a>/<button> need
1734
+ /* xplat-web-only:start — controls that render as real <a>/<button> need
1714
1735
  inline layout and UA resets; the pressable/nav-link leaves are divs on
1715
1736
  web and don't hit these rules. */
1716
- a.btn {
1717
- display: inline-flex;
1718
- }
1719
- a.chip {
1720
- display: inline-flex;
1721
- align-items: center;
1722
- }
1723
- /* xplat-web-only:end */
1737
+ a.btn {
1738
+ display: inline-flex;
1739
+ }
1740
+ a.chip {
1741
+ display: inline-flex;
1742
+ align-items: center;
1743
+ }
1744
+ /* xplat-web-only:end */
1724
1745
 
1725
- /* Tab shell — web divs/buttons + the Android SwapTabs fallback (iOS draws
1746
+ /* Tab shell — web divs/buttons + the Android SwapTabs fallback (iOS draws
1726
1747
  its own UITabBar and never sees these). */
1727
- .vx-tabbar {
1728
- border-bottom-width: 1px;
1729
- border-bottom-color: var(--color-border);
1730
- background-color: var(--color-surface);
1731
- }
1732
- .vx-tab {
1733
- align-items: center;
1734
- justify-content: center;
1735
- padding-top: 10px;
1736
- padding-right: 16px;
1737
- padding-bottom: 10px;
1738
- padding-left: 16px;
1739
- font-size: 14px;
1740
- color: var(--color-text-secondary);
1741
- }
1742
- .vx-tab.active {
1743
- color: var(--color-text);
1744
- }
1745
- .vx-tabicon {
1746
- margin-right: 6px;
1747
- }
1748
+ .vx-tabbar {
1749
+ border-bottom-width: 1px;
1750
+ border-bottom-color: var(--color-border);
1751
+ background-color: var(--color-surface);
1752
+ }
1753
+ .vx-tab {
1754
+ align-items: center;
1755
+ justify-content: center;
1756
+ padding-top: 10px;
1757
+ padding-right: 16px;
1758
+ padding-bottom: 10px;
1759
+ padding-left: 16px;
1760
+ font-size: 14px;
1761
+ color: var(--color-text-secondary);
1762
+ }
1763
+ .vx-tab.active {
1764
+ color: var(--color-text);
1765
+ }
1766
+ .vx-tabicon {
1767
+ margin-right: 6px;
1768
+ }
1748
1769
 
1749
- /* xplat-web-only:start — DOM defaults are display:block (div) and
1770
+ /* xplat-web-only:start — DOM defaults are display:block (div) and
1750
1771
  inline-block (button); every flex-bearing vx- class needs an explicit
1751
1772
  display or flex-direction/align-items/justify-content are inert. */
1752
- .vx-tabs {
1753
- display: flex;
1754
- flex-direction: column;
1755
- flex: 1;
1756
- min-height: 0;
1757
- }
1758
- .vx-tabbar {
1759
- display: flex;
1760
- flex-direction: row;
1761
- }
1762
- .vx-tabpane {
1763
- flex: 1;
1764
- min-height: 0;
1765
- display: flex;
1766
- flex-direction: column;
1767
- }
1768
- button.vx-tab {
1769
- display: flex;
1770
- background: none;
1771
- cursor: pointer;
1772
- }
1773
- /* xplat-web-only:end */
1773
+ .vx-tabs {
1774
+ display: flex;
1775
+ flex-direction: column;
1776
+ flex: 1;
1777
+ min-height: 0;
1778
+ }
1779
+ .vx-tabbar {
1780
+ display: flex;
1781
+ flex-direction: row;
1782
+ }
1783
+ .vx-tabpane {
1784
+ flex: 1;
1785
+ min-height: 0;
1786
+ display: flex;
1787
+ flex-direction: column;
1788
+ }
1789
+ button.vx-tab {
1790
+ display: flex;
1791
+ background: none;
1792
+ cursor: pointer;
1793
+ }
1794
+ /* xplat-web-only:end */
1774
1795
 
1775
- /* harness experiment styles */
1796
+ /* harness experiment styles */
1776
1797
 
1777
- .vx-app {
1778
- padding: 24px;
1779
- background-color: var(--color-surface);
1780
- color: var(--color-text);
1781
- }
1798
+ .vx-app {
1799
+ padding: 24px;
1800
+ background-color: var(--color-surface);
1801
+ color: var(--color-text);
1802
+ }
1782
1803
 
1783
- .vx-app .text-xl,
1784
- .vx-app Text,
1785
- .vx-app label {
1786
- color: var(--color-text);
1787
- }
1804
+ .vx-app .text-xl,
1805
+ .vx-app Text,
1806
+ .vx-app label {
1807
+ color: var(--color-text);
1808
+ }
1788
1809
 
1789
- /* `.vx-app label` above (descendant + type) outranks single-class text
1810
+ /* `.vx-app label` above (descendant + type) outranks single-class text
1790
1811
  colors inside the app root — roles that must win escalate here. */
1791
- .vx-app .text-onprimary {
1792
- color: var(--color-onprimary);
1793
- }
1794
- .vx-app .text-muted {
1795
- color: var(--color-text-secondary);
1796
- }
1797
- .vx-app .vx-tab label {
1798
- color: var(--color-text-secondary);
1799
- }
1800
- .vx-app .vx-tab.active label {
1801
- color: var(--color-text);
1802
- }
1803
- .vx-app .chip-on label {
1804
- color: var(--color-onprimary);
1805
- }
1806
- /* Danger surfaces keep white text in both themes (on-primary flips). */
1807
- .bg-danger .text-onprimary {
1808
- color: #ffffff;
1809
- }
1812
+ .vx-app .text-onprimary {
1813
+ color: var(--color-onprimary);
1814
+ }
1815
+ .vx-app .text-muted {
1816
+ color: var(--color-text-secondary);
1817
+ }
1818
+ .vx-app .vx-tab label {
1819
+ color: var(--color-text-secondary);
1820
+ }
1821
+ .vx-app .vx-tab.active label {
1822
+ color: var(--color-text);
1823
+ }
1824
+ .vx-app .chip-on label {
1825
+ color: var(--color-onprimary);
1826
+ }
1827
+ /* Danger surfaces keep white text in both themes (on-primary flips). */
1828
+ .bg-danger .text-onprimary {
1829
+ color: #ffffff;
1830
+ }
1810
1831
 
1811
- .input {
1812
- padding-top: 10px;
1813
- padding-right: 12px;
1814
- padding-bottom: 10px;
1815
- padding-left: 12px;
1816
- border-width: 1px;
1817
- border-color: var(--color-border);
1818
- border-radius: var(--radius-md);
1819
- corner-shape: squircle;
1820
- font-size: 14px;
1821
- background-color: var(--color-surface);
1822
- color: var(--color-text);
1823
- /* NS TextField/TextView placeholder color (ignored on web — the
1832
+ .input {
1833
+ padding-top: 10px;
1834
+ padding-right: 12px;
1835
+ padding-bottom: 10px;
1836
+ padding-left: 12px;
1837
+ border-width: 1px;
1838
+ border-color: var(--color-border);
1839
+ border-radius: var(--radius-md);
1840
+ corner-shape: squircle;
1841
+ font-size: 14px;
1842
+ background-color: var(--color-surface);
1843
+ color: var(--color-text);
1844
+ /* NS TextField/TextView placeholder color (ignored on web — the
1824
1845
  ::placeholder pseudo above covers it there). */
1825
- placeholder-color: var(--color-text-secondary);
1826
- }
1846
+ placeholder-color: var(--color-text-secondary);
1847
+ }
1827
1848
 
1828
- .chip {
1829
- background-color: var(--color-surface);
1830
- color: var(--color-text);
1831
- padding-top: 5px;
1832
- padding-right: 12px;
1833
- padding-bottom: 5px;
1834
- padding-left: 12px;
1835
- border-width: 1px;
1836
- border-color: var(--color-border);
1837
- border-radius: var(--radius-md);
1838
- corner-shape: squircle;
1839
- font-size: 13px;
1840
- text-decoration: none;
1841
- }
1849
+ .chip {
1850
+ background-color: var(--color-surface);
1851
+ color: var(--color-text);
1852
+ padding-top: 5px;
1853
+ padding-right: 12px;
1854
+ padding-bottom: 5px;
1855
+ padding-left: 12px;
1856
+ border-width: 1px;
1857
+ border-color: var(--color-border);
1858
+ border-radius: var(--radius-md);
1859
+ corner-shape: squircle;
1860
+ font-size: 13px;
1861
+ text-decoration: none;
1862
+ }
1842
1863
 
1843
- /* Selected chip — the one filled element in a group of bordered peers. */
1844
- .chip-on {
1845
- background-color: var(--color-primary);
1846
- border-color: var(--color-primary);
1847
- color: var(--color-onprimary);
1848
- }
1864
+ /* Selected chip — the one filled element in a group of bordered peers. */
1865
+ .chip-on {
1866
+ background-color: var(--color-primary);
1867
+ border-color: var(--color-primary);
1868
+ color: var(--color-onprimary);
1869
+ }
1849
1870
 
1850
- .cell {
1851
- padding: 12px;
1852
- border-bottom-width: 1px;
1853
- border-bottom-color: var(--color-border);
1854
- }
1871
+ .cell {
1872
+ padding: 12px;
1873
+ border-bottom-width: 1px;
1874
+ border-bottom-color: var(--color-border);
1875
+ }
1855
1876
 
1856
- .gap-2 {
1857
- gap: 8px;
1858
- }
1877
+ .gap-2 {
1878
+ gap: 8px;
1879
+ }
1859
1880
 
1860
- /* In-window sheet — bottom-anchored panel (matches the native RootLayout
1881
+ /* In-window sheet — bottom-anchored panel (matches the native RootLayout
1861
1882
  sheet). Layer is pointer-events:none; backdrop + panel re-enable. */
1862
- .vx-sheet-layer {
1863
- z-index: 1100;
1864
- }
1883
+ .vx-sheet-layer {
1884
+ z-index: 1100;
1885
+ }
1865
1886
 
1866
- .vx-sheet-backdrop {
1867
- background: rgba(0, 0, 0, 0.4);
1868
- }
1887
+ .vx-sheet-backdrop {
1888
+ background: rgba(0, 0, 0, 0.4);
1889
+ }
1869
1890
 
1870
- .vx-sheet {
1871
- max-height: 85%;
1872
- padding: 24px;
1873
- border-width: 1px;
1874
- border-color: var(--color-border);
1875
- border-top-left-radius: var(--radius-xl);
1876
- border-top-right-radius: var(--radius-xl);
1877
- corner-shape: squircle;
1878
- background-color: var(--color-surface);
1879
- color: var(--color-text);
1880
- }
1891
+ .vx-sheet {
1892
+ max-height: 85%;
1893
+ padding: 24px;
1894
+ border-width: 1px;
1895
+ border-color: var(--color-border);
1896
+ border-top-left-radius: var(--radius-xl);
1897
+ border-top-right-radius: var(--radius-xl);
1898
+ corner-shape: squircle;
1899
+ background-color: var(--color-surface);
1900
+ color: var(--color-text);
1901
+ }
1881
1902
 
1882
- /* xplat-web-only:start — DOM positioning + hit-testing for the sheet.
1903
+ /* xplat-web-only:start — DOM positioning + hit-testing for the sheet.
1883
1904
  The native leaf renders on RootLayout with its own geometry. */
1884
- .vx-sheet-layer {
1885
- position: fixed;
1886
- inset: 0;
1887
- pointer-events: none;
1888
- }
1905
+ .vx-sheet-layer {
1906
+ position: fixed;
1907
+ inset: 0;
1908
+ pointer-events: none;
1909
+ }
1889
1910
 
1890
- .vx-sheet-backdrop,
1891
- .vx-sheet {
1892
- position: absolute;
1893
- inset: 0;
1894
- pointer-events: auto;
1895
- }
1911
+ .vx-sheet-backdrop,
1912
+ .vx-sheet {
1913
+ position: absolute;
1914
+ inset: 0;
1915
+ pointer-events: auto;
1916
+ }
1896
1917
 
1897
- .vx-sheet {
1898
- top: auto;
1899
- bottom: 0;
1900
- overflow: auto;
1901
- }
1902
- /* xplat-web-only:end */
1918
+ .vx-sheet {
1919
+ top: auto;
1920
+ bottom: 0;
1921
+ overflow: auto;
1922
+ }
1923
+ /* xplat-web-only:end */
1903
1924
 
1904
- .vx-sheet-grabber {
1905
- display: flex;
1906
- align-items: center;
1907
- justify-content: center;
1908
- height: 28px;
1909
- }
1925
+ .vx-sheet-grabber {
1926
+ display: flex;
1927
+ align-items: center;
1928
+ justify-content: center;
1929
+ height: 28px;
1930
+ }
1910
1931
 
1911
- .vx-sheet-grip {
1912
- width: 36px;
1913
- height: 4px;
1914
- border-radius: 999px;
1915
- background-color: var(--color-text-secondary);
1916
- opacity: 0.4;
1917
- }
1932
+ .vx-sheet-grip {
1933
+ width: 36px;
1934
+ height: 4px;
1935
+ border-radius: 999px;
1936
+ background-color: var(--color-text-secondary);
1937
+ opacity: 0.4;
1938
+ }
1918
1939
 
1919
- /* xplat-web-only:start — detents size the panel to the largest fraction
1940
+ /* xplat-web-only:start — detents size the panel to the largest fraction
1920
1941
  (overriding the 85% cap) and overlay the grabber; native stacks it as a
1921
1942
  top-aligned grid child. touch-action keeps drags off native scroll. */
1922
- .vx-sheet-detents {
1923
- max-height: none;
1924
- }
1925
- .vx-sheet-detents > .vx-sheet-grabber {
1926
- position: absolute;
1927
- top: 0;
1928
- left: 0;
1929
- right: 0;
1930
- z-index: 2;
1931
- cursor: grab;
1932
- touch-action: none;
1943
+ .vx-sheet-detents {
1944
+ max-height: none;
1945
+ }
1946
+ .vx-sheet-detents > .vx-sheet-grabber {
1947
+ position: absolute;
1948
+ top: 0;
1949
+ left: 0;
1950
+ right: 0;
1951
+ z-index: 2;
1952
+ cursor: grab;
1953
+ touch-action: none;
1954
+ }
1955
+ /* xplat-web-only:end */
1933
1956
  }
1934
- /* xplat-web-only:end */