@evinvest/uikit 0.18.0 → 0.20.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 (277) hide show
  1. package/README.md +210 -13
  2. package/dist/chunk-3IPB4QQJ.js +306 -0
  3. package/dist/chunk-3IPB4QQJ.js.map +1 -0
  4. package/dist/components/accordion.js +136 -0
  5. package/dist/components/accordion.js.map +1 -0
  6. package/dist/components/alert-dialog.js +175 -0
  7. package/dist/components/alert-dialog.js.map +1 -0
  8. package/dist/components/alert.js +49 -0
  9. package/dist/components/alert.js.map +1 -0
  10. package/dist/components/avatar.js +54 -0
  11. package/dist/components/avatar.js.map +1 -0
  12. package/dist/components/badge.js +25 -0
  13. package/dist/components/badge.js.map +1 -0
  14. package/dist/components/band.js +75 -0
  15. package/dist/components/band.js.map +1 -0
  16. package/dist/components/breadcrumb.js +132 -0
  17. package/dist/components/breadcrumb.js.map +1 -0
  18. package/dist/components/button-group.js +61 -0
  19. package/dist/components/button-group.js.map +1 -0
  20. package/dist/components/button.js +55 -0
  21. package/dist/components/button.js.map +1 -0
  22. package/dist/components/calendar.js +226 -0
  23. package/dist/components/calendar.js.map +1 -0
  24. package/dist/components/card.js +98 -0
  25. package/dist/components/card.js.map +1 -0
  26. package/dist/components/carousel.js +262 -0
  27. package/dist/components/carousel.js.map +1 -0
  28. package/dist/components/chart.js +151 -0
  29. package/dist/components/chart.js.map +1 -0
  30. package/dist/components/checkbox.js +68 -0
  31. package/dist/components/checkbox.js.map +1 -0
  32. package/dist/components/collapsible.js +60 -0
  33. package/dist/components/collapsible.js.map +1 -0
  34. package/dist/components/command.js +274 -0
  35. package/dist/components/command.js.map +1 -0
  36. package/dist/components/container.js +18 -0
  37. package/dist/components/container.js.map +1 -0
  38. package/dist/components/context-menu.js +337 -0
  39. package/dist/components/context-menu.js.map +1 -0
  40. package/dist/components/date-time-picker.js +301 -0
  41. package/dist/components/date-time-picker.js.map +1 -0
  42. package/dist/components/dialog.js +207 -0
  43. package/dist/components/dialog.js.map +1 -0
  44. package/dist/components/drawer.js +351 -0
  45. package/dist/components/drawer.js.map +1 -0
  46. package/dist/components/dropdown-menu.js +356 -0
  47. package/dist/components/dropdown-menu.js.map +1 -0
  48. package/dist/components/empty.js +91 -0
  49. package/dist/components/empty.js.map +1 -0
  50. package/dist/components/field-context.js +30 -0
  51. package/dist/components/field-context.js.map +1 -0
  52. package/dist/components/field.js +216 -0
  53. package/dist/components/field.js.map +1 -0
  54. package/dist/components/footer.js +89 -0
  55. package/dist/components/footer.js.map +1 -0
  56. package/dist/components/form.js +101 -0
  57. package/dist/components/form.js.map +1 -0
  58. package/dist/components/hover-card.js +90 -0
  59. package/dist/components/hover-card.js.map +1 -0
  60. package/dist/components/info-tip.js +161 -0
  61. package/dist/components/info-tip.js.map +1 -0
  62. package/dist/components/input-group.js +124 -0
  63. package/dist/components/input-group.js.map +1 -0
  64. package/dist/components/input-otp.js +120 -0
  65. package/dist/components/input-otp.js.map +1 -0
  66. package/dist/components/input.js +26 -0
  67. package/dist/components/input.js.map +1 -0
  68. package/dist/components/item.js +167 -0
  69. package/dist/components/item.js.map +1 -0
  70. package/dist/components/kbd.js +29 -0
  71. package/dist/components/kbd.js.map +1 -0
  72. package/dist/components/label.js +11 -0
  73. package/dist/components/label.js.map +1 -0
  74. package/dist/components/logo.js +41 -0
  75. package/dist/components/logo.js.map +1 -0
  76. package/dist/components/menubar.js +330 -0
  77. package/dist/components/menubar.js.map +1 -0
  78. package/dist/components/native-select.js +62 -0
  79. package/dist/components/native-select.js.map +1 -0
  80. package/dist/components/navigation-menu.js +204 -0
  81. package/dist/components/navigation-menu.js.map +1 -0
  82. package/dist/components/pagination.js +138 -0
  83. package/dist/components/pagination.js.map +1 -0
  84. package/dist/components/popover.js +104 -0
  85. package/dist/components/popover.js.map +1 -0
  86. package/dist/components/progress.js +30 -0
  87. package/dist/components/progress.js.map +1 -0
  88. package/dist/components/radio-group.js +113 -0
  89. package/dist/components/radio-group.js.map +1 -0
  90. package/dist/components/resizable.js +193 -0
  91. package/dist/components/resizable.js.map +1 -0
  92. package/dist/components/scroll-area.js +40 -0
  93. package/dist/components/scroll-area.js.map +1 -0
  94. package/dist/components/section-descriptor.js +88 -0
  95. package/dist/components/section-descriptor.js.map +1 -0
  96. package/dist/components/select-chevron.js +25 -0
  97. package/dist/components/select-chevron.js.map +1 -0
  98. package/dist/components/select.js +288 -0
  99. package/dist/components/select.js.map +1 -0
  100. package/dist/components/separator.js +31 -0
  101. package/dist/components/separator.js.map +1 -0
  102. package/dist/components/sheet.js +200 -0
  103. package/dist/components/sheet.js.map +1 -0
  104. package/dist/components/sidebar.js +326 -0
  105. package/dist/components/sidebar.js.map +1 -0
  106. package/dist/components/skeleton.js +18 -0
  107. package/dist/components/skeleton.js.map +1 -0
  108. package/dist/components/slider.js +138 -0
  109. package/dist/components/slider.js.map +1 -0
  110. package/dist/components/sonner.js +319 -0
  111. package/dist/components/sonner.js.map +1 -0
  112. package/dist/components/spinner.js +23 -0
  113. package/dist/components/spinner.js.map +1 -0
  114. package/dist/components/status-screen.js +145 -0
  115. package/dist/components/status-screen.js.map +1 -0
  116. package/dist/components/switch.js +54 -0
  117. package/dist/components/switch.js.map +1 -0
  118. package/dist/components/table.js +124 -0
  119. package/dist/components/table.js.map +1 -0
  120. package/dist/components/tabs.js +133 -0
  121. package/dist/components/tabs.js.map +1 -0
  122. package/dist/components/terminal.js +206 -0
  123. package/dist/components/terminal.js.map +1 -0
  124. package/dist/components/textarea.js +22 -0
  125. package/dist/components/textarea.js.map +1 -0
  126. package/dist/components/toggle-group.js +105 -0
  127. package/dist/components/toggle-group.js.map +1 -0
  128. package/dist/components/toggle.js +53 -0
  129. package/dist/components/toggle.js.map +1 -0
  130. package/dist/components/tooltip.js +102 -0
  131. package/dist/components/tooltip.js.map +1 -0
  132. package/dist/generated/accent.js +27 -0
  133. package/dist/generated/accent.js.map +1 -0
  134. package/dist/generated/accordion.js +13 -0
  135. package/dist/generated/accordion.js.map +1 -0
  136. package/dist/generated/alert-dialog.js +15 -0
  137. package/dist/generated/alert-dialog.js.map +1 -0
  138. package/dist/generated/alert.js +16 -0
  139. package/dist/generated/alert.js.map +1 -0
  140. package/dist/generated/avatar.js +9 -0
  141. package/dist/generated/avatar.js.map +1 -0
  142. package/dist/generated/badge.js +13 -0
  143. package/dist/generated/badge.js.map +1 -0
  144. package/dist/generated/band.js +39 -0
  145. package/dist/generated/band.js.map +1 -0
  146. package/dist/generated/breadcrumb.js +15 -0
  147. package/dist/generated/breadcrumb.js.map +1 -0
  148. package/dist/generated/button-group.js +14 -0
  149. package/dist/generated/button-group.js.map +1 -0
  150. package/dist/generated/button.js +30 -0
  151. package/dist/generated/button.js.map +1 -0
  152. package/dist/generated/calendar.js +29 -0
  153. package/dist/generated/calendar.js.map +1 -0
  154. package/dist/generated/card.js +17 -0
  155. package/dist/generated/card.js.map +1 -0
  156. package/dist/generated/carousel.js +31 -0
  157. package/dist/generated/carousel.js.map +1 -0
  158. package/dist/generated/chart.js +9 -0
  159. package/dist/generated/chart.js.map +1 -0
  160. package/dist/generated/checkbox.js +7 -0
  161. package/dist/generated/checkbox.js.map +1 -0
  162. package/dist/generated/command.js +27 -0
  163. package/dist/generated/command.js.map +1 -0
  164. package/dist/generated/container.js +5 -0
  165. package/dist/generated/container.js.map +1 -0
  166. package/dist/generated/context-menu.js +19 -0
  167. package/dist/generated/context-menu.js.map +1 -0
  168. package/dist/generated/date-time-picker.js +15 -0
  169. package/dist/generated/date-time-picker.js.map +1 -0
  170. package/dist/generated/dialog.js +17 -0
  171. package/dist/generated/dialog.js.map +1 -0
  172. package/dist/generated/drawer.js +26 -0
  173. package/dist/generated/drawer.js.map +1 -0
  174. package/dist/generated/dropdown-menu.js +21 -0
  175. package/dist/generated/dropdown-menu.js.map +1 -0
  176. package/dist/generated/empty.js +20 -0
  177. package/dist/generated/empty.js.map +1 -0
  178. package/dist/generated/field.js +33 -0
  179. package/dist/generated/field.js.map +1 -0
  180. package/dist/generated/focus.js +5 -0
  181. package/dist/generated/focus.js.map +1 -0
  182. package/dist/generated/form.js +11 -0
  183. package/dist/generated/form.js.map +1 -0
  184. package/dist/generated/hover-card.js +5 -0
  185. package/dist/generated/hover-card.js.map +1 -0
  186. package/dist/generated/input-group.js +29 -0
  187. package/dist/generated/input-group.js.map +1 -0
  188. package/dist/generated/input-otp.js +15 -0
  189. package/dist/generated/input-otp.js.map +1 -0
  190. package/dist/generated/input.js +11 -0
  191. package/dist/generated/input.js.map +1 -0
  192. package/dist/generated/item.js +40 -0
  193. package/dist/generated/item.js.map +1 -0
  194. package/dist/generated/kbd.js +7 -0
  195. package/dist/generated/kbd.js.map +1 -0
  196. package/dist/generated/label.js +5 -0
  197. package/dist/generated/label.js.map +1 -0
  198. package/dist/generated/menubar.js +27 -0
  199. package/dist/generated/menubar.js.map +1 -0
  200. package/dist/generated/navigation-menu.js +19 -0
  201. package/dist/generated/navigation-menu.js.map +1 -0
  202. package/dist/generated/popover.js +5 -0
  203. package/dist/generated/popover.js.map +1 -0
  204. package/dist/generated/progress.js +7 -0
  205. package/dist/generated/progress.js.map +1 -0
  206. package/dist/generated/radio-group.js +7 -0
  207. package/dist/generated/radio-group.js.map +1 -0
  208. package/dist/generated/resizable.js +11 -0
  209. package/dist/generated/resizable.js.map +1 -0
  210. package/dist/generated/scroll-area.js +14 -0
  211. package/dist/generated/scroll-area.js.map +1 -0
  212. package/dist/generated/select.js +15 -0
  213. package/dist/generated/select.js.map +1 -0
  214. package/dist/generated/separator.js +10 -0
  215. package/dist/generated/separator.js.map +1 -0
  216. package/dist/generated/sheet.js +25 -0
  217. package/dist/generated/sheet.js.map +1 -0
  218. package/dist/generated/sidebar.js +41 -0
  219. package/dist/generated/sidebar.js.map +1 -0
  220. package/dist/generated/skeleton.js +5 -0
  221. package/dist/generated/skeleton.js.map +1 -0
  222. package/dist/generated/slider.js +11 -0
  223. package/dist/generated/slider.js.map +1 -0
  224. package/dist/generated/sonner.js +30 -0
  225. package/dist/generated/sonner.js.map +1 -0
  226. package/dist/generated/switch.js +7 -0
  227. package/dist/generated/switch.js.map +1 -0
  228. package/dist/generated/table.js +21 -0
  229. package/dist/generated/table.js.map +1 -0
  230. package/dist/generated/tabs.js +11 -0
  231. package/dist/generated/tabs.js.map +1 -0
  232. package/dist/generated/terminal.js +70 -0
  233. package/dist/generated/terminal.js.map +1 -0
  234. package/dist/generated/textarea.js +11 -0
  235. package/dist/generated/textarea.js.map +1 -0
  236. package/dist/generated/toggle.js +18 -0
  237. package/dist/generated/toggle.js.map +1 -0
  238. package/dist/generated/tooltip.js +5 -0
  239. package/dist/generated/tooltip.js.map +1 -0
  240. package/dist/index.d.ts +141 -34
  241. package/dist/index.js +73 -8623
  242. package/dist/lib/cn.js +12 -0
  243. package/dist/lib/cn.js.map +1 -0
  244. package/dist/lib/focus.js +5 -0
  245. package/dist/lib/focus.js.map +1 -0
  246. package/dist/palette-cli.js +41 -0
  247. package/dist/palette-cli.js.map +1 -0
  248. package/dist/palette.d.ts +80 -0
  249. package/dist/palette.js +25 -0
  250. package/dist/palette.js.map +1 -0
  251. package/dist/primitives/dismissable-layer.js +35 -0
  252. package/dist/primitives/dismissable-layer.js.map +1 -0
  253. package/dist/primitives/focus-scope.js +58 -0
  254. package/dist/primitives/focus-scope.js.map +1 -0
  255. package/dist/primitives/merge-refs.js +13 -0
  256. package/dist/primitives/merge-refs.js.map +1 -0
  257. package/dist/primitives/portal.js +19 -0
  258. package/dist/primitives/portal.js.map +1 -0
  259. package/dist/primitives/presence.js +45 -0
  260. package/dist/primitives/presence.js.map +1 -0
  261. package/dist/primitives/slot.js +39 -0
  262. package/dist/primitives/slot.js.map +1 -0
  263. package/dist/primitives/use-controllable-state.js +24 -0
  264. package/dist/primitives/use-controllable-state.js.map +1 -0
  265. package/dist/primitives/use-floating.js +128 -0
  266. package/dist/primitives/use-floating.js.map +1 -0
  267. package/dist/primitives/use-hover-intent.js +50 -0
  268. package/dist/primitives/use-hover-intent.js.map +1 -0
  269. package/dist/primitives/use-roving-focus.js +40 -0
  270. package/dist/primitives/use-roving-focus.js.map +1 -0
  271. package/dist/primitives/walk-elements.js +13 -0
  272. package/dist/primitives/walk-elements.js.map +1 -0
  273. package/package.json +15 -5
  274. package/styles/ev.css +73 -0
  275. package/styles/theme.css +470 -0
  276. package/styles/tokens.css +129 -90
  277. package/dist/index.js.map +0 -1
@@ -0,0 +1,470 @@
1
+ /* AUTO-GENERATED by `nix run .#gen` from the repo-root theme.css — do not edit. */
2
+ /*
3
+ * Toast (sonner) and Drawer enter/exit motion, shared by both ports. Imported by the token
4
+ * sheets so the lifecycle ships with the theme contract.
5
+ */
6
+
7
+ /* ── Toast stacking (Sonner-style), shared by both ports via data-stack ──────
8
+ Both `ev_lib::uikit` (Dioxus) and `@evinvest/uikit` (React) set data-stack on the
9
+ toaster and drive the same machine: toasts are absolutely stacked at the
10
+ pinned edge, pile up collapsed (front three peeking, each lifted + scaled by
11
+ --index and clamped to --front-height), and spread to --offset on hover /
12
+ keyboard focus (pure CSS — no JS state). Vars: the toaster sets --front-height
13
+ + --gap; each toast sets --index, --z-index, --offset, --initial-height. React
14
+ measures real heights; Dioxus can't (host-only), so it feeds a constant height
15
+ — collapse is exact, expansion is uniform. The enter is a keyframe (plays on
16
+ DOM insertion, so it works in both ports without a JS mount flip),
17
+ `data-state=closed` slides out, and the node is dropped on the transitionend. */
18
+
19
+ [data-slot="toaster"][data-stack] [data-slot="toast"] {
20
+ position: absolute;
21
+ --y: translateY(0);
22
+ opacity: 1;
23
+ z-index: var(--z-index, 1);
24
+ transform: var(--y) translateX(var(--swipe-x, 0px));
25
+ /* a plain easeOut (no overshoot) so a restack settles instead of bouncing
26
+ back past its slot */
27
+ transition:
28
+ transform 350ms cubic-bezier(0.21, 0.8, 0.4, 1),
29
+ opacity 350ms,
30
+ height 350ms;
31
+ /* the enter is a keyframe (plays on DOM insertion in both ports — no JS mount
32
+ flip, whose pre-paint timing the Dioxus effect couldn't drive). It animates
33
+ the independent `translate` property, NOT `transform`, so it *composes* with
34
+ the stack transform instead of overriding it: a toast pushed back mid-enter
35
+ keeps repositioning smoothly instead of fighting the keyframe (rapid-fire
36
+ lag). */
37
+ animation: ev-toast-enter 350ms cubic-bezier(0.21, 0.8, 0.4, 1) backwards;
38
+ }
39
+ @keyframes ev-toast-enter {
40
+ from {
41
+ opacity: 0;
42
+ translate: 0 var(--enter-y, 100%);
43
+ }
44
+ to {
45
+ opacity: 1;
46
+ translate: 0 0;
47
+ }
48
+ }
49
+
50
+ /* pin to the toaster's edge; set the lift sign + the off-screen enter offset */
51
+ [data-slot="toaster"][data-stack][data-y-position="bottom"] [data-slot="toast"] {
52
+ bottom: 0;
53
+ --lift: -1;
54
+ --enter-y: 100%;
55
+ }
56
+ [data-slot="toaster"][data-stack][data-y-position="top"] [data-slot="toast"] {
57
+ top: 0;
58
+ --lift: 1;
59
+ --enter-y: -100%;
60
+ }
61
+
62
+ /* closing: slide back off + fade out; the node is dropped on its transitionend */
63
+ [data-slot="toaster"][data-stack] [data-slot="toast"][data-state="closed"] {
64
+ --y: translateY(var(--enter-y));
65
+ opacity: 0;
66
+ }
67
+
68
+ /* collapsed: back toasts lift + shrink by depth, clamp to the front height */
69
+ [data-slot="toaster"][data-stack]
70
+ [data-slot="toast"][data-state="open"][data-front="false"] {
71
+ --y: translateY(calc(var(--lift) * var(--gap) * var(--index)))
72
+ scale(calc(1 - var(--index) * 0.05));
73
+ height: var(--front-height);
74
+ }
75
+ [data-slot="toaster"][data-stack] [data-slot="toast"][data-front="false"] > * {
76
+ opacity: 0;
77
+ transition: opacity 300ms;
78
+ }
79
+ /* only the front three show while collapsed */
80
+ [data-slot="toaster"][data-stack] [data-slot="toast"][data-visible="false"] {
81
+ opacity: 0;
82
+ pointer-events: none;
83
+ }
84
+
85
+ /* expanded (hover / keyboard focus): spread to the offset, real height, content
86
+ + overflow back */
87
+ [data-slot="toaster"][data-stack]:hover [data-slot="toast"][data-state="open"],
88
+ [data-slot="toaster"][data-stack]:focus-within
89
+ [data-slot="toast"][data-state="open"] {
90
+ --y: translateY(calc(var(--lift) * var(--offset)));
91
+ height: var(--initial-height);
92
+ }
93
+ [data-slot="toaster"][data-stack]:hover [data-slot="toast"] > *,
94
+ [data-slot="toaster"][data-stack]:focus-within [data-slot="toast"] > *,
95
+ [data-slot="toaster"][data-stack]:hover [data-slot="toast"][data-visible="false"],
96
+ [data-slot="toaster"][data-stack]:focus-within
97
+ [data-slot="toast"][data-visible="false"] {
98
+ opacity: 1;
99
+ pointer-events: auto;
100
+ }
101
+
102
+ /* a transparent strip over the gap so the hover area stays contiguous between
103
+ spread toasts (otherwise the pointer falls through the gap and it collapses) */
104
+ [data-slot="toaster"][data-stack] [data-slot="toast"]::after {
105
+ content: "";
106
+ position: absolute;
107
+ left: 0;
108
+ width: 100%;
109
+ height: calc(var(--gap) + 1px);
110
+ }
111
+ [data-slot="toaster"][data-stack][data-y-position="bottom"]
112
+ [data-slot="toast"]::after {
113
+ bottom: 100%;
114
+ }
115
+ [data-slot="toaster"][data-stack][data-y-position="top"]
116
+ [data-slot="toast"]::after {
117
+ top: 100%;
118
+ }
119
+
120
+ /* 1:1 finger tracking during a swipe — kill the transition */
121
+ [data-slot="toaster"][data-stack] [data-slot="toast"][data-swiping="true"] {
122
+ transition: none;
123
+ }
124
+
125
+ /* auto-dismiss without a host timer: a no-op "life" animation runs for --life,
126
+ then its `animationend` fires (the toast closes itself). Hovering/focusing the
127
+ stack pauses just that animation (animation-play-state's 2nd slot), so the
128
+ countdown holds — and resumes with the elapsed time preserved — while the
129
+ enter (1st slot) keeps running. Persistent toasts set data-autodismiss=false,
130
+ so they get no life animation. (The React port uses setTimeout instead.) */
131
+ @keyframes ev-toast-life {
132
+ from {
133
+ --ev-life: 0;
134
+ }
135
+ to {
136
+ --ev-life: 1;
137
+ }
138
+ }
139
+ [data-slot="toaster"][data-stack]
140
+ [data-slot="toast"][data-autodismiss="true"][data-state="open"] {
141
+ animation:
142
+ ev-toast-enter 350ms cubic-bezier(0.21, 0.8, 0.4, 1) backwards,
143
+ ev-toast-life var(--life) linear;
144
+ }
145
+ [data-slot="toaster"][data-stack]:hover
146
+ [data-slot="toast"][data-autodismiss="true"],
147
+ [data-slot="toaster"][data-stack]:focus-within
148
+ [data-slot="toast"][data-autodismiss="true"] {
149
+ animation-play-state: running, paused;
150
+ }
151
+
152
+ /* reduced motion: opacity-only enter + near-instant transform (still fires
153
+ transitionend, so exit removal works), no height animation */
154
+ @keyframes ev-toast-fade-in {
155
+ from {
156
+ opacity: 0;
157
+ }
158
+ }
159
+ @media (prefers-reduced-motion: reduce) {
160
+ [data-slot="toaster"][data-stack] [data-slot="toast"] {
161
+ transition:
162
+ transform 1ms linear,
163
+ opacity 200ms;
164
+ animation: ev-toast-fade-in 200ms;
165
+ }
166
+ /* keep the auto-dismiss life animation under reduced motion */
167
+ [data-slot="toaster"][data-stack]
168
+ [data-slot="toast"][data-autodismiss="true"][data-state="open"] {
169
+ animation:
170
+ ev-toast-fade-in 200ms,
171
+ ev-toast-life var(--life) linear;
172
+ }
173
+ }
174
+
175
+ /* ── Drawer (Vaul-style) motion, shared by both ports via data-slot ──────────
176
+ Both ports render the panel as [data-slot="drawer-content"] with
177
+ data-vaul-drawer-direction + data-state, and the scrim as
178
+ [data-slot="drawer-overlay"] with data-state. The enter is a keyframe (plays
179
+ on DOM insertion — no JS mount flip, same reason as the toast above) on the
180
+ independent `translate` property, so it composes with the `transform` the
181
+ React port writes while a finger drags the panel. Closing flips
182
+ data-state=closed: `transform` transitions to the off-screen edge and the
183
+ node is dropped on that transitionend. A drag sets data-dragging on the panel
184
+ and the scrim to kill their transitions for 1:1 finger tracking, then clears
185
+ it on release so the same transition either snaps the panel home or (with
186
+ data-state=closed in the same frame) carries it off from where the finger
187
+ left it. Timing is Vaul's: 500ms cubic-bezier(0.32, 0.72, 0, 1) both ways.
188
+
189
+ touch-action:none on the panel keeps the browser from turning a touch into a
190
+ page pan (which would pointercancel the drag). It does NOT reach into a
191
+ scroll container nested in the panel — that one keeps native touch scrolling,
192
+ and the React port only starts a drag from it when it sits at its top. So
193
+ the scrollable region of a drawer belongs INSIDE the panel, not on it. */
194
+
195
+ [data-slot="drawer-content"] {
196
+ --ev-drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
197
+ touch-action: none;
198
+ will-change: transform;
199
+ transition: transform 500ms var(--ev-drawer-ease);
200
+ animation: ev-drawer-enter 500ms var(--ev-drawer-ease) backwards;
201
+ }
202
+ [data-slot="drawer-content"][data-vaul-drawer-direction="bottom"] {
203
+ --ev-drawer-from: 0 100%;
204
+ --ev-drawer-out: translate3d(0, 100%, 0);
205
+ }
206
+ [data-slot="drawer-content"][data-vaul-drawer-direction="top"] {
207
+ --ev-drawer-from: 0 -100%;
208
+ --ev-drawer-out: translate3d(0, -100%, 0);
209
+ }
210
+ [data-slot="drawer-content"][data-vaul-drawer-direction="left"] {
211
+ --ev-drawer-from: -100% 0;
212
+ --ev-drawer-out: translate3d(-100%, 0, 0);
213
+ }
214
+ [data-slot="drawer-content"][data-vaul-drawer-direction="right"] {
215
+ --ev-drawer-from: 100% 0;
216
+ --ev-drawer-out: translate3d(100%, 0, 0);
217
+ }
218
+ @keyframes ev-drawer-enter {
219
+ from {
220
+ translate: var(--ev-drawer-from, 0 100%);
221
+ }
222
+ to {
223
+ translate: 0 0;
224
+ }
225
+ }
226
+ [data-slot="drawer-content"][data-state="closed"] {
227
+ transform: var(--ev-drawer-out, translate3d(0, 100%, 0));
228
+ }
229
+
230
+ /* the over-drag away from the edge is dampened but still moves the panel, so
231
+ the panel's own background extends past its docked edge to cover the gap */
232
+ [data-slot="drawer-content"]::after {
233
+ content: "";
234
+ position: absolute;
235
+ background: inherit;
236
+ pointer-events: none;
237
+ }
238
+ [data-slot="drawer-content"][data-vaul-drawer-direction="bottom"]::after {
239
+ top: 100%;
240
+ left: 0;
241
+ right: 0;
242
+ height: 200%;
243
+ }
244
+ [data-slot="drawer-content"][data-vaul-drawer-direction="top"]::after {
245
+ bottom: 100%;
246
+ left: 0;
247
+ right: 0;
248
+ height: 200%;
249
+ }
250
+ [data-slot="drawer-content"][data-vaul-drawer-direction="left"]::after {
251
+ right: 100%;
252
+ top: 0;
253
+ bottom: 0;
254
+ width: 200%;
255
+ }
256
+ [data-slot="drawer-content"][data-vaul-drawer-direction="right"]::after {
257
+ left: 100%;
258
+ top: 0;
259
+ bottom: 0;
260
+ width: 200%;
261
+ }
262
+
263
+ [data-slot="drawer-overlay"] {
264
+ touch-action: none;
265
+ will-change: opacity;
266
+ transition: opacity 500ms cubic-bezier(0.32, 0.72, 0, 1);
267
+ animation: ev-drawer-fade-in 500ms cubic-bezier(0.32, 0.72, 0, 1) backwards;
268
+ }
269
+ @keyframes ev-drawer-fade-in {
270
+ from {
271
+ opacity: 0;
272
+ }
273
+ }
274
+ [data-slot="drawer-overlay"][data-state="closed"] {
275
+ opacity: 0;
276
+ }
277
+
278
+ /* 1:1 finger tracking during a drag — kill the transitions */
279
+ [data-slot="drawer-content"][data-dragging="true"],
280
+ [data-slot="drawer-overlay"][data-dragging="true"] {
281
+ transition: none;
282
+ }
283
+
284
+ /* a mouse drag would otherwise start a text selection alongside the panel */
285
+ @media (hover: hover) and (pointer: fine) {
286
+ [data-slot="drawer-content"] {
287
+ user-select: none;
288
+ }
289
+ }
290
+
291
+ /* reduced motion: fade only. The transform transition stays (1ms) so its
292
+ transitionend still fires and the closing node is still dropped. */
293
+ @media (prefers-reduced-motion: reduce) {
294
+ [data-slot="drawer-content"] {
295
+ transition:
296
+ transform 1ms linear,
297
+ opacity 200ms;
298
+ animation: ev-drawer-fade-in 200ms;
299
+ }
300
+ [data-slot="drawer-content"][data-state="closed"] {
301
+ opacity: 0;
302
+ }
303
+ [data-slot="drawer-overlay"] {
304
+ transition: opacity 200ms;
305
+ animation: ev-drawer-fade-in 200ms;
306
+ }
307
+ }
308
+
309
+ /* ═══ CONTRACT ═══════════════════════════════════════════════════ */
310
+
311
+ @theme inline {
312
+ --radius-sm: calc(var(--radius) - 4px);
313
+ --radius-md: calc(var(--radius) - 2px);
314
+ --radius-lg: var(--radius);
315
+ --radius-xl: calc(var(--radius) + 4px);
316
+ --radius-2xl: calc(var(--radius) + 8px);
317
+
318
+ /* Depth has to be a ladder, not a single drop shadow: on a dark theme the
319
+ * value steps alone are too close to separate a resting card from a menu
320
+ * from a modal, so each tier gets its own spread + opacity. */
321
+ --shadow-card: 0 1px 2px 0 rgb(0 0 0 / 0.30);
322
+ --shadow-elevated:
323
+ 0 4px 12px -2px rgb(0 0 0 / 0.40),
324
+ 0 2px 4px -2px rgb(0 0 0 / 0.30);
325
+ --shadow-overlay: 0 16px 48px -12px rgb(0 0 0 / 0.60);
326
+
327
+ /* The families are parameters: a brand scope sets `--brand-font-sans`,
328
+ * `--brand-font-display` and `--brand-font-mono` (evinvest-palette writes
329
+ * them from a brand file's [fonts]); `font-serif` is the display role under
330
+ * its older name. Unset, each falls through to EV: next/font (landing) sets
331
+ * `--font-inter` / `--font-playfair`, a Rust consumer `@font-face`s the same
332
+ * families itself. Every leading var() carries its own fallback — an
333
+ * undefined one would invalidate the whole `font-family` and drop the
334
+ * browser to its default, so it must never resolve to guaranteed-invalid.
335
+ * `inline` puts the var() chain in the utility itself, so it resolves on
336
+ * the element and a brand scope re-fonts its subtree. */
337
+ --font-sans: var(--brand-font-sans, var(--font-inter, "Inter")), ui-sans-serif, system-ui, sans-serif;
338
+ --font-serif: var(--brand-font-display, var(--font-playfair, "Playfair Display")), ui-serif, Georgia, serif;
339
+ --font-mono: var(--brand-font-mono, var(--font-inter, "Inter")), ui-monospace, monospace;
340
+ --font-display: var(--brand-font-display, var(--font-playfair, "Playfair Display")), ui-serif, Georgia, serif;
341
+
342
+ /* The colour contract: every `--color-X: var(--X)` below is a name a palette
343
+ * owes, except the derived ones in the next rule. `evinvest-palette` reads
344
+ * this block as the list, so there is no second copy of it to drift. */
345
+
346
+ /* surfaces */
347
+ --color-background: var(--background);
348
+ --color-card: var(--card);
349
+ --color-popover: var(--popover);
350
+ --color-muted: var(--muted);
351
+ --color-hover: var(--hover);
352
+
353
+ /* ink — hierarchy, loudest first */
354
+ --color-ink: var(--ink);
355
+ --color-ink-mid: var(--ink-mid);
356
+ --color-ink-soft: var(--ink-soft);
357
+
358
+ /* lines */
359
+ --color-border: var(--border);
360
+ --color-input: var(--input);
361
+ --color-ring: var(--ring);
362
+
363
+ /* roles */
364
+ --color-brand: var(--brand);
365
+ --color-primary: var(--primary);
366
+ --color-on-primary: var(--on-primary);
367
+ --color-primary-ink: var(--primary-ink);
368
+ --color-secondary: var(--secondary);
369
+ --color-on-secondary: var(--on-secondary);
370
+ --color-positive: var(--positive);
371
+ --color-on-positive: var(--on-positive);
372
+ --color-accent-trace: var(--accent-trace);
373
+ --color-on-accent-trace: var(--on-accent-trace);
374
+ --color-accent-debug: var(--accent-debug);
375
+ --color-on-accent-debug: var(--on-accent-debug);
376
+ --color-accent-info: var(--accent-info);
377
+ --color-on-accent-info: var(--on-accent-info);
378
+ --color-accent-warn: var(--accent-warn);
379
+ --color-on-accent-warn: var(--on-accent-warn);
380
+ --color-accent-error: var(--accent-error);
381
+ --color-on-accent-error: var(--on-accent-error);
382
+
383
+ /* charts — distinguishable hues carrying no significance */
384
+ --color-chart-1: var(--chart-1);
385
+ --color-chart-2: var(--chart-2);
386
+ --color-chart-3: var(--chart-3);
387
+ --color-chart-4: var(--chart-4);
388
+ --color-chart-5: var(--chart-5);
389
+ }
390
+
391
+ /* Derived from the palette, and redeclared on every scope a palette can be
392
+ * declared on. A var() inside a custom property is resolved where it is
393
+ * declared and the result is what inherits — declared on `:root` alone, a brand
394
+ * scope would change the text and keep EV's borders, hover and focus ring.
395
+ * Zero specificity, so a palette that sets one of these outright wins. */
396
+ :where(:root, .dark, .light, [data-brand]) {
397
+ /* the menu-item tint: a surface state, so it is named for the state */
398
+ --hover: color-mix(in srgb, var(--ink) 8%, transparent);
399
+ --ink-mid: color-mix(in srgb, var(--ink) 82%, transparent);
400
+ /* the most-used secondary text token, so it has to clear AA on its own */
401
+ --ink-soft: color-mix(in srgb, var(--ink) 65%, transparent);
402
+ --border: color-mix(in srgb, var(--ink) 14%, transparent);
403
+ --input: color-mix(in srgb, var(--ink) 20%, transparent);
404
+ --ring: var(--primary-ink);
405
+ }
406
+
407
+ /* Geometry. A `[data-brand]` scope is a theme root of its own, so it restarts
408
+ * from these defaults (and from the scale-ups below) rather than inheriting
409
+ * whatever an ancestor computed — `--control-radius` in particular is derived
410
+ * from `--radius` and has to be recomputed wherever `--radius` may change. */
411
+ :where(:root, [data-brand]) {
412
+ --radius: 0.375rem; /* 6px on mobile; bumped to 8px ≥ sm (see @media below) */
413
+ /* control geometry, so a consumer reshapes every button/input at once */
414
+ --control-radius: calc(var(--radius) - 2px);
415
+ --control-py: 0.5rem;
416
+ /* the Xl (call-to-action) button reads all three */
417
+ --control-px: 2rem;
418
+ --control-text: 1rem;
419
+ /* multiplies the display type scale (uikit <Display>) */
420
+ --display-scale: 1;
421
+ /* the vertical rhythm of a full-bleed <Section>; `tight` is the sub-page one */
422
+ --band-py: 2.25rem;
423
+ --band-py-tight: 2rem;
424
+
425
+ --sidebar-width: 14rem;
426
+
427
+ /* page container — responsive gutter + max width (uikit <Container>) */
428
+ --page-max: 90rem; /* 1440px */
429
+ --page-px: 1rem; /* 16px — mobile gutter, scales up below */
430
+
431
+ /* ── the mark ────────────────────────────────────────────────── *
432
+ * `--brand-mark` (a url() at your own SVG) and `--brand-aspect` (its ratio)
433
+ * are what `<Logo>` masks. Deliberately undefined: the kit ships no artwork,
434
+ * and a default here would put EV's crown on somebody else's 404. Undeclared,
435
+ * `mask-image` is invalid at computed-value time and the mark paints as a
436
+ * solid block — loud, which is the point. Declare both beside the palette:
437
+ *
438
+ * --brand-mark: url("/assets/logo.svg");
439
+ * --brand-aspect: 387 / 335;
440
+ */
441
+ }
442
+
443
+ :root {
444
+ /* shell offset — viewport space reserved by whatever shell hosts the app.
445
+ Standalone apps get 0; a hosting shell overrides on :root. Size
446
+ viewport-bound surfaces with calc(100dvh - var(--ev-shell-offset, 0px)). */
447
+ --ev-shell-offset: 0px;
448
+ }
449
+
450
+ /* Mobile-first scale-ups: roomier gutters + slightly larger radii on bigger
451
+ viewports. Lives here (not per-component) so every consumer scales alike. */
452
+ @media (min-width: 640px) {
453
+ :where(:root, [data-brand]) {
454
+ --radius: 0.5rem;
455
+ --page-px: 1.5rem;
456
+ }
457
+ }
458
+
459
+ @media (min-width: 768px) {
460
+ :where(:root, [data-brand]) {
461
+ --band-py: 4rem;
462
+ --band-py-tight: 3.5rem;
463
+ }
464
+ }
465
+
466
+ @media (min-width: 1024px) {
467
+ :where(:root, [data-brand]) {
468
+ --page-px: 3rem;
469
+ }
470
+ }