@emara/ui 1.1.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 (218) hide show
  1. package/components/ui/.gitkeep +0 -0
  2. package/components/ui/accordion.stories.tsx +231 -0
  3. package/components/ui/accordion.tsx +250 -0
  4. package/components/ui/app-shell.stories.tsx +270 -0
  5. package/components/ui/app-shell.tsx +491 -0
  6. package/components/ui/avatar.stories.tsx +174 -0
  7. package/components/ui/avatar.tsx +257 -0
  8. package/components/ui/badge.stories.tsx +127 -0
  9. package/components/ui/badge.tsx +146 -0
  10. package/components/ui/breadcrumb.stories.tsx +92 -0
  11. package/components/ui/breadcrumb.tsx +302 -0
  12. package/components/ui/button.stories.tsx +186 -0
  13. package/components/ui/button.tsx +128 -0
  14. package/components/ui/card.stories.tsx +279 -0
  15. package/components/ui/card.tsx +250 -0
  16. package/components/ui/checkbox.stories.tsx +93 -0
  17. package/components/ui/checkbox.tsx +131 -0
  18. package/components/ui/combobox.stories.tsx +489 -0
  19. package/components/ui/combobox.tsx +874 -0
  20. package/components/ui/context-menu.stories.tsx +202 -0
  21. package/components/ui/context-menu.tsx +309 -0
  22. package/components/ui/data-table.stories.tsx +227 -0
  23. package/components/ui/data-table.tsx +539 -0
  24. package/components/ui/date-picker.stories.tsx +225 -0
  25. package/components/ui/date-picker.tsx +597 -0
  26. package/components/ui/dialog.stories.tsx +193 -0
  27. package/components/ui/dialog.tsx +262 -0
  28. package/components/ui/divider.stories.tsx +84 -0
  29. package/components/ui/divider.tsx +135 -0
  30. package/components/ui/drawer.stories.tsx +218 -0
  31. package/components/ui/drawer.tsx +329 -0
  32. package/components/ui/dropdown-menu.stories.tsx +270 -0
  33. package/components/ui/dropdown-menu.tsx +353 -0
  34. package/components/ui/empty-state.stories.tsx +121 -0
  35. package/components/ui/empty-state.tsx +289 -0
  36. package/components/ui/field-group.stories.tsx +201 -0
  37. package/components/ui/field-group.tsx +276 -0
  38. package/components/ui/form.stories.tsx +219 -0
  39. package/components/ui/form.tsx +542 -0
  40. package/components/ui/input.stories.tsx +154 -0
  41. package/components/ui/input.tsx +208 -0
  42. package/components/ui/label.stories.tsx +84 -0
  43. package/components/ui/label.tsx +98 -0
  44. package/components/ui/page-header.stories.tsx +136 -0
  45. package/components/ui/page-header.tsx +315 -0
  46. package/components/ui/pagination.stories.tsx +136 -0
  47. package/components/ui/pagination.tsx +427 -0
  48. package/components/ui/popover.stories.tsx +212 -0
  49. package/components/ui/popover.tsx +167 -0
  50. package/components/ui/radio-group.stories.tsx +96 -0
  51. package/components/ui/radio-group.tsx +250 -0
  52. package/components/ui/select.stories.tsx +203 -0
  53. package/components/ui/select.tsx +318 -0
  54. package/components/ui/sidebar.stories.tsx +186 -0
  55. package/components/ui/sidebar.tsx +623 -0
  56. package/components/ui/skeleton.stories.tsx +131 -0
  57. package/components/ui/skeleton.tsx +311 -0
  58. package/components/ui/switch.stories.tsx +74 -0
  59. package/components/ui/switch.tsx +186 -0
  60. package/components/ui/table.stories.tsx +107 -0
  61. package/components/ui/table.tsx +285 -0
  62. package/components/ui/tabs.stories.tsx +222 -0
  63. package/components/ui/tabs.tsx +287 -0
  64. package/components/ui/textarea.stories.tsx +96 -0
  65. package/components/ui/textarea.tsx +182 -0
  66. package/components/ui/toast.stories.tsx +169 -0
  67. package/components/ui/toast.tsx +250 -0
  68. package/components/ui/tooltip.stories.tsx +146 -0
  69. package/components/ui/tooltip.tsx +156 -0
  70. package/components/ui/top-bar.stories.tsx +182 -0
  71. package/components/ui/top-bar.tsx +155 -0
  72. package/dist/components/ui/accordion.d.ts +45 -0
  73. package/dist/components/ui/accordion.d.ts.map +1 -0
  74. package/dist/components/ui/accordion.js +99 -0
  75. package/dist/components/ui/accordion.js.map +1 -0
  76. package/dist/components/ui/app-shell.d.ts +70 -0
  77. package/dist/components/ui/app-shell.d.ts.map +1 -0
  78. package/dist/components/ui/app-shell.js +199 -0
  79. package/dist/components/ui/app-shell.js.map +1 -0
  80. package/dist/components/ui/avatar.d.ts +41 -0
  81. package/dist/components/ui/avatar.d.ts.map +1 -0
  82. package/dist/components/ui/avatar.js +104 -0
  83. package/dist/components/ui/avatar.js.map +1 -0
  84. package/dist/components/ui/badge.d.ts +27 -0
  85. package/dist/components/ui/badge.d.ts.map +1 -0
  86. package/dist/components/ui/badge.js +65 -0
  87. package/dist/components/ui/badge.js.map +1 -0
  88. package/dist/components/ui/breadcrumb.d.ts +35 -0
  89. package/dist/components/ui/breadcrumb.d.ts.map +1 -0
  90. package/dist/components/ui/breadcrumb.js +88 -0
  91. package/dist/components/ui/breadcrumb.js.map +1 -0
  92. package/dist/components/ui/button.d.ts +26 -0
  93. package/dist/components/ui/button.d.ts.map +1 -0
  94. package/dist/components/ui/button.js +73 -0
  95. package/dist/components/ui/button.js.map +1 -0
  96. package/dist/components/ui/card.d.ts +52 -0
  97. package/dist/components/ui/card.d.ts.map +1 -0
  98. package/dist/components/ui/card.js +96 -0
  99. package/dist/components/ui/card.js.map +1 -0
  100. package/dist/components/ui/checkbox.d.ts +18 -0
  101. package/dist/components/ui/checkbox.d.ts.map +1 -0
  102. package/dist/components/ui/checkbox.js +59 -0
  103. package/dist/components/ui/checkbox.js.map +1 -0
  104. package/dist/components/ui/combobox.d.ts +194 -0
  105. package/dist/components/ui/combobox.d.ts.map +1 -0
  106. package/dist/components/ui/combobox.js +361 -0
  107. package/dist/components/ui/combobox.js.map +1 -0
  108. package/dist/components/ui/context-menu.d.ts +46 -0
  109. package/dist/components/ui/context-menu.d.ts.map +1 -0
  110. package/dist/components/ui/context-menu.js +95 -0
  111. package/dist/components/ui/context-menu.js.map +1 -0
  112. package/dist/components/ui/data-table.d.ts +53 -0
  113. package/dist/components/ui/data-table.d.ts.map +1 -0
  114. package/dist/components/ui/data-table.js +163 -0
  115. package/dist/components/ui/data-table.js.map +1 -0
  116. package/dist/components/ui/date-picker.d.ts +103 -0
  117. package/dist/components/ui/date-picker.d.ts.map +1 -0
  118. package/dist/components/ui/date-picker.js +306 -0
  119. package/dist/components/ui/date-picker.js.map +1 -0
  120. package/dist/components/ui/dialog.d.ts +40 -0
  121. package/dist/components/ui/dialog.d.ts.map +1 -0
  122. package/dist/components/ui/dialog.js +110 -0
  123. package/dist/components/ui/dialog.js.map +1 -0
  124. package/dist/components/ui/divider.d.ts +30 -0
  125. package/dist/components/ui/divider.d.ts.map +1 -0
  126. package/dist/components/ui/divider.js +62 -0
  127. package/dist/components/ui/divider.js.map +1 -0
  128. package/dist/components/ui/drawer.d.ts +56 -0
  129. package/dist/components/ui/drawer.d.ts.map +1 -0
  130. package/dist/components/ui/drawer.js +147 -0
  131. package/dist/components/ui/drawer.js.map +1 -0
  132. package/dist/components/ui/dropdown-menu.d.ts +63 -0
  133. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  134. package/dist/components/ui/dropdown-menu.js +116 -0
  135. package/dist/components/ui/dropdown-menu.js.map +1 -0
  136. package/dist/components/ui/empty-state.d.ts +43 -0
  137. package/dist/components/ui/empty-state.d.ts.map +1 -0
  138. package/dist/components/ui/empty-state.js +128 -0
  139. package/dist/components/ui/empty-state.js.map +1 -0
  140. package/dist/components/ui/field-group.d.ts +38 -0
  141. package/dist/components/ui/field-group.d.ts.map +1 -0
  142. package/dist/components/ui/field-group.js +107 -0
  143. package/dist/components/ui/field-group.js.map +1 -0
  144. package/dist/components/ui/form.d.ts +67 -0
  145. package/dist/components/ui/form.d.ts.map +1 -0
  146. package/dist/components/ui/form.js +286 -0
  147. package/dist/components/ui/form.js.map +1 -0
  148. package/dist/components/ui/input.d.ts +36 -0
  149. package/dist/components/ui/input.d.ts.map +1 -0
  150. package/dist/components/ui/input.js +99 -0
  151. package/dist/components/ui/input.js.map +1 -0
  152. package/dist/components/ui/label.d.ts +37 -0
  153. package/dist/components/ui/label.d.ts.map +1 -0
  154. package/dist/components/ui/label.js +34 -0
  155. package/dist/components/ui/label.js.map +1 -0
  156. package/dist/components/ui/page-header.d.ts +65 -0
  157. package/dist/components/ui/page-header.d.ts.map +1 -0
  158. package/dist/components/ui/page-header.js +140 -0
  159. package/dist/components/ui/page-header.js.map +1 -0
  160. package/dist/components/ui/pagination.d.ts +67 -0
  161. package/dist/components/ui/pagination.d.ts.map +1 -0
  162. package/dist/components/ui/pagination.js +109 -0
  163. package/dist/components/ui/pagination.js.map +1 -0
  164. package/dist/components/ui/popover.d.ts +28 -0
  165. package/dist/components/ui/popover.d.ts.map +1 -0
  166. package/dist/components/ui/popover.js +85 -0
  167. package/dist/components/ui/popover.js.map +1 -0
  168. package/dist/components/ui/radio-group.d.ts +35 -0
  169. package/dist/components/ui/radio-group.d.ts.map +1 -0
  170. package/dist/components/ui/radio-group.js +103 -0
  171. package/dist/components/ui/radio-group.js.map +1 -0
  172. package/dist/components/ui/select.d.ts +42 -0
  173. package/dist/components/ui/select.d.ts.map +1 -0
  174. package/dist/components/ui/select.js +86 -0
  175. package/dist/components/ui/select.js.map +1 -0
  176. package/dist/components/ui/sidebar.d.ts +59 -0
  177. package/dist/components/ui/sidebar.d.ts.map +1 -0
  178. package/dist/components/ui/sidebar.js +189 -0
  179. package/dist/components/ui/sidebar.js.map +1 -0
  180. package/dist/components/ui/skeleton.d.ts +77 -0
  181. package/dist/components/ui/skeleton.d.ts.map +1 -0
  182. package/dist/components/ui/skeleton.js +115 -0
  183. package/dist/components/ui/skeleton.js.map +1 -0
  184. package/dist/components/ui/switch.d.ts +26 -0
  185. package/dist/components/ui/switch.d.ts.map +1 -0
  186. package/dist/components/ui/switch.js +84 -0
  187. package/dist/components/ui/switch.js.map +1 -0
  188. package/dist/components/ui/table.d.ts +52 -0
  189. package/dist/components/ui/table.d.ts.map +1 -0
  190. package/dist/components/ui/table.js +109 -0
  191. package/dist/components/ui/table.js.map +1 -0
  192. package/dist/components/ui/tabs.d.ts +42 -0
  193. package/dist/components/ui/tabs.d.ts.map +1 -0
  194. package/dist/components/ui/tabs.js +163 -0
  195. package/dist/components/ui/tabs.js.map +1 -0
  196. package/dist/components/ui/textarea.d.ts +26 -0
  197. package/dist/components/ui/textarea.d.ts.map +1 -0
  198. package/dist/components/ui/textarea.js +96 -0
  199. package/dist/components/ui/textarea.js.map +1 -0
  200. package/dist/components/ui/toast.d.ts +77 -0
  201. package/dist/components/ui/toast.d.ts.map +1 -0
  202. package/dist/components/ui/toast.js +141 -0
  203. package/dist/components/ui/toast.js.map +1 -0
  204. package/dist/components/ui/tooltip.d.ts +31 -0
  205. package/dist/components/ui/tooltip.d.ts.map +1 -0
  206. package/dist/components/ui/tooltip.js +71 -0
  207. package/dist/components/ui/tooltip.js.map +1 -0
  208. package/dist/components/ui/top-bar.d.ts +30 -0
  209. package/dist/components/ui/top-bar.d.ts.map +1 -0
  210. package/dist/components/ui/top-bar.js +64 -0
  211. package/dist/components/ui/top-bar.js.map +1 -0
  212. package/dist/lib/utils.d.ts +3 -0
  213. package/dist/lib/utils.d.ts.map +1 -0
  214. package/dist/lib/utils.js +6 -0
  215. package/dist/lib/utils.js.map +1 -0
  216. package/lib/utils.ts +6 -0
  217. package/package.json +112 -0
  218. package/styles/globals.css +685 -0
@@ -0,0 +1,685 @@
1
+ /*
2
+ * Emara UI — globals.css
3
+ *
4
+ * Default theme baked in: Slate base + Indigo primary + warm density.
5
+ * To regenerate with different choices, run:
6
+ * npx @emara/cli theme set base <name>
7
+ * npx @emara/cli theme set primary <name>
8
+ * npx @emara/cli theme set density <warm|dense>
9
+ *
10
+ * Spec: docs/emara-ui-design-tokens.md
11
+ */
12
+
13
+ /* Google Fonts must come before `@import "tailwindcss"` — Tailwind v4
14
+ expands its own @import inline, and any @import statement after that
15
+ expansion violates the "all @imports must precede other rules" rule. */
16
+ @import url("https://fonts.googleapis.com/css2?family=Mona+Sans:wght@200..900&family=Noto+Kufi+Arabic:wght@100..900&display=swap");
17
+ @import "tailwindcss";
18
+
19
+ /*
20
+ * Source paths for Tailwind v4 content scanning. Only used inside this repo
21
+ * — the CLI's generate-css.ts emits its own globals.css for consumer projects
22
+ * without these directives (consumer components live in their own project
23
+ * tree, which Tailwind auto-scans).
24
+ */
25
+ @source "../components/**/*.{ts,tsx}";
26
+
27
+ /* =============================================================================
28
+ * 1. Semantic color tokens — Slate + Cyan
29
+ * ============================================================================= */
30
+
31
+ :root {
32
+ /* Surfaces */
33
+ --background: oklch(1 0 0);
34
+ --foreground: oklch(0.129 0.042 264);
35
+
36
+ --card: oklch(1 0 0);
37
+ --card-foreground: oklch(0.129 0.042 264);
38
+
39
+ --popover: oklch(1 0 0);
40
+ --popover-foreground: oklch(0.129 0.042 264);
41
+
42
+ /* Actions
43
+ * Light-mode primary uses the 600 shade (not 500) so that white text on
44
+ * primary backgrounds clears WCAG AA's 4.5:1 contrast minimum. The
45
+ * common "X-500" picks fail this check for several hues (cyan, lime,
46
+ * yellow, sky, teal). 600 is consistently safe.
47
+ */
48
+ --primary: oklch(0.511 0.262 276.966); /* indigo-600 */
49
+ --primary-foreground: oklch(1 0 0);
50
+
51
+ --secondary: oklch(0.968 0.007 247); /* slate-100 */
52
+ --secondary-foreground: oklch(0.208 0.042 265);
53
+
54
+ /* Subtle */
55
+ --muted: oklch(0.968 0.007 247);
56
+ --muted-foreground: oklch(0.554 0.046 257);
57
+
58
+ /* Hover / item highlight */
59
+ --accent: oklch(0.968 0.007 247);
60
+ --accent-foreground: oklch(0.208 0.042 265);
61
+
62
+ /* States — destructive uses red-600 for the same contrast reason. */
63
+ --destructive: oklch(0.577 0.245 27); /* red-600 */
64
+ --destructive-foreground: oklch(1 0 0);
65
+ --success: oklch(0.6 0.18 145);
66
+ --warning: oklch(0.75 0.18 80);
67
+ --info: oklch(0.6 0.18 240);
68
+
69
+ /* Lines */
70
+ --border: oklch(0.929 0.013 256);
71
+ --input: oklch(0.929 0.013 256);
72
+ --ring: oklch(0.511 0.262 276.966); /* matches primary */
73
+ }
74
+
75
+ .dark {
76
+ /* Surfaces */
77
+ --background: oklch(0.129 0.042 264);
78
+ --foreground: oklch(0.984 0.003 247);
79
+
80
+ --card: oklch(0.208 0.042 265);
81
+ --card-foreground: oklch(0.984 0.003 247);
82
+
83
+ --popover: oklch(0.208 0.042 265);
84
+ --popover-foreground: oklch(0.984 0.003 247);
85
+
86
+ /* Actions */
87
+ --primary: oklch(0.673 0.182 276.935); /* indigo-400 (one shade lighter in dark) */
88
+ --primary-foreground: oklch(0.129 0.042 264);
89
+
90
+ --secondary: oklch(0.279 0.041 260);
91
+ --secondary-foreground: oklch(0.984 0.003 247);
92
+
93
+ /* Subtle */
94
+ --muted: oklch(0.279 0.041 260);
95
+ --muted-foreground: oklch(0.704 0.04 257);
96
+
97
+ /* Hover / item highlight */
98
+ --accent: oklch(0.279 0.041 260);
99
+ --accent-foreground: oklch(0.984 0.003 247);
100
+
101
+ /* States */
102
+ --destructive: oklch(0.704 0.191 22);
103
+ --destructive-foreground: oklch(0.984 0.003 247);
104
+ --success: oklch(0.72 0.18 145);
105
+ --warning: oklch(0.82 0.18 80);
106
+ --info: oklch(0.7 0.18 240);
107
+
108
+ /* Lines */
109
+ --border: oklch(0.279 0.041 260);
110
+ --input: oklch(0.279 0.041 260);
111
+ --ring: oklch(0.673 0.182 276.935);
112
+ }
113
+
114
+ /* =============================================================================
115
+ * 2. Spacing tokens — warm density
116
+ * ============================================================================= */
117
+
118
+ :root {
119
+ --space-0: 0;
120
+ --space-px: 1px;
121
+ --space-0_5: 2px;
122
+ --space-1: 4px;
123
+ --space-1_5: 6px;
124
+ --space-2: 8px;
125
+ --space-2_5: 10px;
126
+ --space-3: 12px;
127
+ --space-3_5: 14px;
128
+ --space-4: 16px;
129
+ --space-5: 20px;
130
+ --space-6: 24px;
131
+ --space-7: 28px;
132
+ --space-8: 32px;
133
+ --space-10: 40px;
134
+ --space-12: 48px;
135
+ --space-16: 64px;
136
+ --space-20: 80px;
137
+ --space-24: 96px;
138
+ }
139
+
140
+ /* =============================================================================
141
+ * 3. Typography tokens
142
+ * ============================================================================= */
143
+
144
+ :root {
145
+ --font-sans: "Mona Sans", system-ui, sans-serif;
146
+ --font-arabic: "Noto Kufi Arabic", "Mona Sans", sans-serif;
147
+ --font-mono: "JetBrains Mono", ui-monospace, monospace;
148
+
149
+ /* Warm sizes — font-size / line-height */
150
+ --text-2xs: 11px;
151
+ --text-2xs--line-height: 14px;
152
+ --text-xs: 12px;
153
+ --text-xs--line-height: 16px;
154
+ --text-sm: 14px;
155
+ --text-sm--line-height: 20px;
156
+ --text-base: 16px;
157
+ --text-base--line-height: 24px;
158
+ --text-lg: 18px;
159
+ --text-lg--line-height: 28px;
160
+ --text-xl: 20px;
161
+ --text-xl--line-height: 28px;
162
+ --text-2xl: 24px;
163
+ --text-2xl--line-height: 32px;
164
+ --text-3xl: 30px;
165
+ --text-3xl--line-height: 36px;
166
+ --text-4xl: 36px;
167
+ --text-4xl--line-height: 40px;
168
+ --text-5xl: 48px;
169
+ --text-5xl--line-height: 1;
170
+
171
+ --font-weight-normal: 400;
172
+ --font-weight-medium: 500;
173
+ --font-weight-semibold: 600;
174
+ --font-weight-bold: 700;
175
+
176
+ --tracking-tighter: -0.05em;
177
+ --tracking-tight: -0.025em;
178
+ --tracking-normal: 0;
179
+ --tracking-wide: 0.025em;
180
+ --tracking-wider: 0.05em;
181
+ }
182
+
183
+ :lang(ar),
184
+ [dir="rtl"] {
185
+ font-family: var(--font-arabic);
186
+ }
187
+
188
+ /* =============================================================================
189
+ * 4. Icon sizes
190
+ * ============================================================================= */
191
+
192
+ :root {
193
+ --icon-xs: 12px;
194
+ --icon-sm: 14px;
195
+ --icon-md: 16px;
196
+ --icon-lg: 20px;
197
+ --icon-xl: 24px;
198
+ --icon-2xl: 32px;
199
+ }
200
+
201
+ /* =============================================================================
202
+ * 5. Radius — warm density
203
+ * ============================================================================= */
204
+
205
+ :root {
206
+ --radius-none: 0;
207
+ --radius-sm: 4px;
208
+ --radius-md: 6px;
209
+ --radius-lg: 8px;
210
+ --radius-xl: 12px;
211
+ --radius-2xl: 16px;
212
+ --radius-3xl: 24px;
213
+ --radius-full: 9999px;
214
+ }
215
+
216
+ /* =============================================================================
217
+ * 6. Shadows — warm density
218
+ * ============================================================================= */
219
+
220
+ :root {
221
+ --shadow-xs: 0 1px 2px rgb(0 0 0 / 0.05);
222
+ --shadow-sm: 0 1px 3px rgb(0 0 0 / 0.1), 0 1px 2px rgb(0 0 0 / 0.06);
223
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.06);
224
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.05);
225
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.05);
226
+ --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
227
+ }
228
+
229
+ /* =============================================================================
230
+ * 7. Motion
231
+ * ============================================================================= */
232
+
233
+ :root {
234
+ --duration-instant: 0ms;
235
+ --duration-fast: 150ms;
236
+ --duration-normal: 200ms;
237
+ --duration-slow: 300ms;
238
+ --duration-slower: 500ms;
239
+
240
+ --ease-linear: linear;
241
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
242
+ --ease-out: cubic-bezier(0, 0, 0.2, 1);
243
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
244
+ --ease-spring: cubic-bezier(0.5, 1.5, 0.5, 1);
245
+ }
246
+
247
+ @media (prefers-reduced-motion: reduce) {
248
+ *,
249
+ *::before,
250
+ *::after {
251
+ animation-duration: 0ms !important;
252
+ transition-duration: 0ms !important;
253
+ }
254
+ }
255
+
256
+ /* =============================================================================
257
+ * 8. Keyframes (logical directions for RTL safety)
258
+ * ============================================================================= */
259
+
260
+ @keyframes fade-in {
261
+ from {
262
+ opacity: 0;
263
+ }
264
+ to {
265
+ opacity: 1;
266
+ }
267
+ }
268
+
269
+ @keyframes fade-out {
270
+ from {
271
+ opacity: 1;
272
+ }
273
+ to {
274
+ opacity: 0;
275
+ }
276
+ }
277
+
278
+ @keyframes scale-in {
279
+ from {
280
+ opacity: 0;
281
+ transform: scale(0.96);
282
+ }
283
+ to {
284
+ opacity: 1;
285
+ transform: scale(1);
286
+ }
287
+ }
288
+
289
+ @keyframes scale-out {
290
+ from {
291
+ opacity: 1;
292
+ transform: scale(1);
293
+ }
294
+ to {
295
+ opacity: 0;
296
+ transform: scale(0.96);
297
+ }
298
+ }
299
+
300
+ @keyframes slide-in-from-top {
301
+ from {
302
+ transform: translateY(-8px);
303
+ opacity: 0;
304
+ }
305
+ to {
306
+ transform: translateY(0);
307
+ opacity: 1;
308
+ }
309
+ }
310
+
311
+ @keyframes slide-in-from-bottom {
312
+ from {
313
+ transform: translateY(8px);
314
+ opacity: 0;
315
+ }
316
+ to {
317
+ transform: translateY(0);
318
+ opacity: 1;
319
+ }
320
+ }
321
+
322
+ /*
323
+ * Slide-in keyframes use a CSS variable that the cascade flips between
324
+ * directions. Components apply the keyframe and the cascade picks the right
325
+ * offset for the current `dir`.
326
+ */
327
+
328
+ :root {
329
+ --slide-offset-start: -8px;
330
+ --slide-offset-end: 8px;
331
+ }
332
+
333
+ [dir="rtl"] {
334
+ --slide-offset-start: 8px;
335
+ --slide-offset-end: -8px;
336
+ }
337
+
338
+ @keyframes slide-in-from-start {
339
+ from {
340
+ transform: translateX(var(--slide-offset-start));
341
+ opacity: 0;
342
+ }
343
+ to {
344
+ transform: translateX(0);
345
+ opacity: 1;
346
+ }
347
+ }
348
+
349
+ @keyframes slide-in-from-end {
350
+ from {
351
+ transform: translateX(var(--slide-offset-end));
352
+ opacity: 0;
353
+ }
354
+ to {
355
+ transform: translateX(0);
356
+ opacity: 1;
357
+ }
358
+ }
359
+
360
+ @keyframes spin {
361
+ from {
362
+ transform: rotate(0deg);
363
+ }
364
+ to {
365
+ transform: rotate(360deg);
366
+ }
367
+ }
368
+
369
+ @keyframes pulse {
370
+ 0%,
371
+ 100% {
372
+ opacity: 1;
373
+ }
374
+ 50% {
375
+ opacity: 0.5;
376
+ }
377
+ }
378
+
379
+ @keyframes shimmer {
380
+ from {
381
+ background-position: -200% 0;
382
+ }
383
+ to {
384
+ background-position: 200% 0;
385
+ }
386
+ }
387
+
388
+ /*
389
+ * Used by Skeleton's `animation: "shimmer"`. Slides a `::before` pseudo
390
+ * across the element. The cascade flips the direction for RTL via the
391
+ * --shimmer-translate variable.
392
+ */
393
+ :root {
394
+ --shimmer-translate-from: -100%;
395
+ --shimmer-translate-to: 100%;
396
+ }
397
+
398
+ [dir="rtl"] {
399
+ --shimmer-translate-from: 100%;
400
+ --shimmer-translate-to: -100%;
401
+ }
402
+
403
+ @keyframes shimmer-slide {
404
+ from {
405
+ transform: translateX(var(--shimmer-translate-from));
406
+ }
407
+ to {
408
+ transform: translateX(var(--shimmer-translate-to));
409
+ }
410
+ }
411
+
412
+ @keyframes collapse-down {
413
+ from {
414
+ height: 0;
415
+ }
416
+ to {
417
+ height: var(--collapsible-content-height);
418
+ }
419
+ }
420
+
421
+ @keyframes collapse-up {
422
+ from {
423
+ height: var(--collapsible-content-height);
424
+ }
425
+ to {
426
+ height: 0;
427
+ }
428
+ }
429
+
430
+ /* =============================================================================
431
+ * 9. Z-index
432
+ * ============================================================================= */
433
+
434
+ :root {
435
+ --z-base: 0;
436
+ --z-dropdown: 1000;
437
+ --z-sticky: 1100;
438
+ --z-fixed: 1200;
439
+ --z-overlay: 1300;
440
+ --z-modal: 1400;
441
+ --z-popover: 1500;
442
+ --z-tooltip: 1600;
443
+ --z-toast: 1700;
444
+
445
+ /* Component scales — see emara-ui-design-tokens.md §11b */
446
+
447
+ /* Drawer (horizontal — width) */
448
+ --drawer-w-xs: 17.5rem;
449
+ --drawer-w-sm: 22.5rem;
450
+ --drawer-w-md: 27.5rem;
451
+ --drawer-w-lg: 35rem;
452
+ --drawer-w-xl: 45rem;
453
+
454
+ /* Drawer (vertical — height) */
455
+ --drawer-h-xs: 10rem;
456
+ --drawer-h-sm: 15rem;
457
+ --drawer-h-md: 20rem;
458
+ --drawer-h-lg: 30rem;
459
+ --drawer-h-xl: 40rem;
460
+
461
+ /* Dialog (max-width) */
462
+ --dialog-max-w-xs: 20rem;
463
+ --dialog-max-w-sm: 26.25rem;
464
+ --dialog-max-w-md: 35rem;
465
+ --dialog-max-w-lg: 45rem;
466
+ --dialog-max-w-xl: 60rem;
467
+
468
+ /* Sidebar (width) */
469
+ --sidebar-w-sm: 12.5rem;
470
+ --sidebar-w-md: 15rem;
471
+ --sidebar-w-lg: 17.5rem;
472
+ --sidebar-w-collapsed: 4rem;
473
+
474
+ /* FieldGroup (horizontal label min-width) */
475
+ --field-label-min-w: 7rem;
476
+ }
477
+
478
+ /* =============================================================================
479
+ * 10. RTL helpers
480
+ * ============================================================================= */
481
+
482
+ [dir="rtl"] .rtl-mirror {
483
+ transform: scaleX(-1);
484
+ }
485
+
486
+ /* =============================================================================
487
+ * 10b. Z-layer utilities — `z-sticky`, `z-fixed`, … bind to --z-* tokens.
488
+ * Components should use these named layers instead of raw `z-{n}` literals
489
+ * so stacking order stays predictable under composition (e.g. tooltip
490
+ * inside dropdown inside dialog). Spec: emara-ui-design-tokens.md §9.
491
+ * ============================================================================= */
492
+
493
+ @utility z-sticky {
494
+ z-index: var(--z-sticky);
495
+ }
496
+ @utility z-fixed {
497
+ z-index: var(--z-fixed);
498
+ }
499
+ @utility z-overlay {
500
+ z-index: var(--z-overlay);
501
+ }
502
+ @utility z-modal {
503
+ z-index: var(--z-modal);
504
+ }
505
+ @utility z-popover {
506
+ z-index: var(--z-popover);
507
+ }
508
+ @utility z-tooltip {
509
+ z-index: var(--z-tooltip);
510
+ }
511
+ @utility z-toast {
512
+ z-index: var(--z-toast);
513
+ }
514
+
515
+ /* =============================================================================
516
+ * 10c. Motion utilities — bind to --duration-* tokens.
517
+ * Use `duration-fast | duration-normal | duration-slow` instead of raw
518
+ * `duration-{ms}` literals so timing scales with the prefers-reduced-motion
519
+ * override + print mode (both already neutralize the tokens in section 12).
520
+ * Spec: emara-ui-design-tokens.md §motion.
521
+ * ============================================================================= */
522
+
523
+ @utility duration-fast {
524
+ transition-duration: var(--duration-fast);
525
+ }
526
+ @utility duration-normal {
527
+ transition-duration: var(--duration-normal);
528
+ }
529
+ @utility duration-slow {
530
+ transition-duration: var(--duration-slow);
531
+ }
532
+
533
+ /* =============================================================================
534
+ * 10d. Component-scale utilities — bind component-specific scales to
535
+ * Tailwind utilities (`w-drawer-xs` etc.) so component source stays readable.
536
+ * Spec: emara-ui-design-tokens.md §11b.
537
+ * ============================================================================= */
538
+
539
+ @utility w-drawer-xs {
540
+ width: var(--drawer-w-xs);
541
+ }
542
+ @utility w-drawer-sm {
543
+ width: var(--drawer-w-sm);
544
+ }
545
+ @utility w-drawer-md {
546
+ width: var(--drawer-w-md);
547
+ }
548
+ @utility w-drawer-lg {
549
+ width: var(--drawer-w-lg);
550
+ }
551
+ @utility w-drawer-xl {
552
+ width: var(--drawer-w-xl);
553
+ }
554
+
555
+ @utility h-drawer-xs {
556
+ height: var(--drawer-h-xs);
557
+ }
558
+ @utility h-drawer-sm {
559
+ height: var(--drawer-h-sm);
560
+ }
561
+ @utility h-drawer-md {
562
+ height: var(--drawer-h-md);
563
+ }
564
+ @utility h-drawer-lg {
565
+ height: var(--drawer-h-lg);
566
+ }
567
+ @utility h-drawer-xl {
568
+ height: var(--drawer-h-xl);
569
+ }
570
+
571
+ @utility max-w-dialog-xs {
572
+ max-width: var(--dialog-max-w-xs);
573
+ }
574
+ @utility max-w-dialog-sm {
575
+ max-width: var(--dialog-max-w-sm);
576
+ }
577
+ @utility max-w-dialog-md {
578
+ max-width: var(--dialog-max-w-md);
579
+ }
580
+ @utility max-w-dialog-lg {
581
+ max-width: var(--dialog-max-w-lg);
582
+ }
583
+ @utility max-w-dialog-xl {
584
+ max-width: var(--dialog-max-w-xl);
585
+ }
586
+
587
+ @utility w-sidebar-sm {
588
+ width: var(--sidebar-w-sm);
589
+ }
590
+ @utility w-sidebar-md {
591
+ width: var(--sidebar-w-md);
592
+ }
593
+ @utility w-sidebar-lg {
594
+ width: var(--sidebar-w-lg);
595
+ }
596
+ @utility w-sidebar-collapsed {
597
+ width: var(--sidebar-w-collapsed);
598
+ }
599
+
600
+ @utility min-w-field-label {
601
+ min-width: var(--field-label-min-w);
602
+ }
603
+
604
+ /* =============================================================================
605
+ * 11. Tailwind v4 theme integration
606
+ * ============================================================================= */
607
+
608
+ @theme {
609
+ --color-background: var(--background);
610
+ --color-foreground: var(--foreground);
611
+ --color-card: var(--card);
612
+ --color-card-foreground: var(--card-foreground);
613
+ --color-popover: var(--popover);
614
+ --color-popover-foreground: var(--popover-foreground);
615
+ --color-primary: var(--primary);
616
+ --color-primary-foreground: var(--primary-foreground);
617
+ --color-secondary: var(--secondary);
618
+ --color-secondary-foreground: var(--secondary-foreground);
619
+ --color-muted: var(--muted);
620
+ --color-muted-foreground: var(--muted-foreground);
621
+ --color-accent: var(--accent);
622
+ --color-accent-foreground: var(--accent-foreground);
623
+ --color-destructive: var(--destructive);
624
+ --color-destructive-foreground: var(--destructive-foreground);
625
+ --color-success: var(--success);
626
+ --color-warning: var(--warning);
627
+ --color-info: var(--info);
628
+ --color-border: var(--border);
629
+ --color-input: var(--input);
630
+ --color-ring: var(--ring);
631
+
632
+ --radius-sm: var(--radius-sm);
633
+ --radius-md: var(--radius-md);
634
+ --radius-lg: var(--radius-lg);
635
+ --radius-xl: var(--radius-xl);
636
+ --radius-2xl: var(--radius-2xl);
637
+ --radius-3xl: var(--radius-3xl);
638
+ --radius-full: var(--radius-full);
639
+
640
+ --font-sans: var(--font-sans);
641
+ --font-mono: var(--font-mono);
642
+
643
+ /* Extended type-scale step below Tailwind's default `text-xs`. Spec §4.2. */
644
+ --text-2xs: 11px;
645
+ --text-2xs--line-height: 14px;
646
+ }
647
+
648
+ /* =============================================================================
649
+ * 12. Print
650
+ * ============================================================================= */
651
+
652
+ @media print {
653
+ :root {
654
+ --background: white;
655
+ --foreground: black;
656
+ --card: white;
657
+ --card-foreground: black;
658
+ --muted: white;
659
+ --muted-foreground: #555;
660
+ --border: #ccc;
661
+ --shadow-xs: none;
662
+ --shadow-sm: none;
663
+ --shadow-md: none;
664
+ --shadow-lg: none;
665
+ --shadow-xl: none;
666
+ --shadow-2xl: none;
667
+ --duration-fast: 0ms;
668
+ --duration-normal: 0ms;
669
+ --duration-slow: 0ms;
670
+ }
671
+
672
+ .no-print {
673
+ display: none !important;
674
+ }
675
+ }
676
+
677
+ @media not print {
678
+ .print-only {
679
+ display: none !important;
680
+ }
681
+ }
682
+
683
+ @page {
684
+ margin: 2cm;
685
+ }