@attrus-ds/core 0.2.0 → 1.0.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 (80) hide show
  1. package/LICENSE +27 -27
  2. package/dist/assets/flags/Brazil-rounded.svg +15 -15
  3. package/dist/assets/flags/Brazil-square.svg +16 -16
  4. package/dist/assets/flags/Chile-rounded.svg +10 -10
  5. package/dist/assets/flags/Chile-square.svg +10 -10
  6. package/dist/assets/flags/Colombia-rounded.svg +9 -9
  7. package/dist/assets/flags/Colombia-square.svg +9 -9
  8. package/dist/assets/flags/EUA-rounded.svg +10 -10
  9. package/dist/assets/flags/EUA-square.svg +10 -10
  10. package/dist/assets/flags/EUR-rounded.svg +8 -8
  11. package/dist/assets/flags/EUR-square.svg +8 -8
  12. package/dist/assets/flags/Mexico-rounded.svg +18 -18
  13. package/dist/assets/flags/Mexico-square.svg +18 -18
  14. package/dist/assets/icon-attrus-dark.svg +2 -2
  15. package/dist/assets/icon-attrus-light.svg +2 -2
  16. package/dist/assets/logo-attrus-dark.svg +7 -7
  17. package/dist/assets/logo-attrus-light.svg +7 -7
  18. package/dist/css/components/buttons.css +301 -301
  19. package/dist/css/components/callout.css +195 -195
  20. package/dist/css/components/cards.css +272 -272
  21. package/dist/css/components/combobox.css +319 -319
  22. package/dist/css/components/empty-states.css +61 -61
  23. package/dist/css/components/inputs.css +975 -975
  24. package/dist/css/components/number-input.css +138 -138
  25. package/dist/css/components/overlays.css +374 -374
  26. package/dist/css/components/primitives.css +314 -314
  27. package/dist/css/components/sidebar.css +179 -179
  28. package/dist/css/components/smart-search.css +91 -91
  29. package/dist/css/components/status-pills.css +207 -207
  30. package/dist/css/components/stepper.css +309 -309
  31. package/dist/css/components/table.css +602 -602
  32. package/dist/css/components/tabs.css +319 -319
  33. package/dist/css/components/toast.css +243 -243
  34. package/dist/css/components/tooltip.css +231 -231
  35. package/dist/css/components/topbar.css +148 -148
  36. package/dist/css/tokens.css +713 -666
  37. package/dist/react/AppSidebar/AppSidebar.d.ts +50 -50
  38. package/dist/react/AppSidebar/AppSidebar.tsx +197 -197
  39. package/dist/react/AppTopbar/AppTopbar.d.ts +58 -58
  40. package/dist/react/AppTopbar/AppTopbar.tsx +203 -203
  41. package/dist/react/Atoms/Atoms.d.ts +149 -149
  42. package/dist/react/Atoms/Atoms.tsx +407 -407
  43. package/dist/react/Button/Button.d.ts +25 -25
  44. package/dist/react/Button/Button.tsx +83 -83
  45. package/dist/react/Callout/Callout.d.ts +29 -29
  46. package/dist/react/Callout/Callout.tsx +98 -98
  47. package/dist/react/Card/Card.d.ts +38 -38
  48. package/dist/react/Card/Card.tsx +93 -93
  49. package/dist/react/Combobox/Combobox.d.ts +52 -52
  50. package/dist/react/Combobox/Combobox.tsx +254 -254
  51. package/dist/react/DataTable/DataTable.d.ts +81 -81
  52. package/dist/react/DataTable/DataTable.tsx +308 -308
  53. package/dist/react/Drawer/Drawer.d.ts +35 -35
  54. package/dist/react/Drawer/Drawer.tsx +226 -226
  55. package/dist/react/Input/Input.d.ts +155 -155
  56. package/dist/react/Input/Input.tsx +478 -478
  57. package/dist/react/Modal/Modal.d.ts +27 -27
  58. package/dist/react/Modal/Modal.tsx +173 -173
  59. package/dist/react/NumberInput/NumberInput.d.ts +25 -25
  60. package/dist/react/NumberInput/NumberInput.tsx +117 -117
  61. package/dist/react/Pill/Pill.d.ts +31 -31
  62. package/dist/react/Pill/Pill.tsx +104 -104
  63. package/dist/react/SearchPill/SearchPill.d.ts +14 -14
  64. package/dist/react/SearchPill/SearchPill.tsx +40 -40
  65. package/dist/react/SmartSearch/SmartSearch.d.ts +65 -65
  66. package/dist/react/SmartSearch/SmartSearch.tsx +271 -271
  67. package/dist/react/Stepper/Stepper.d.ts +25 -25
  68. package/dist/react/Stepper/Stepper.tsx +94 -94
  69. package/dist/react/Tabs/Tabs.d.ts +32 -32
  70. package/dist/react/Tabs/Tabs.tsx +147 -147
  71. package/dist/react/Toast/Toast.d.ts +30 -30
  72. package/dist/react/Toast/Toast.tsx +94 -94
  73. package/dist/react/Tooltip/Tooltip.d.ts +23 -23
  74. package/dist/react/Tooltip/Tooltip.tsx +80 -80
  75. package/dist/react/ZeroState/ZeroState.d.ts +17 -17
  76. package/dist/react/ZeroState/ZeroState.tsx +45 -45
  77. package/dist/react/index.ts +35 -35
  78. package/dist/tokens/attrus-tokens.flat.json +1234 -1234
  79. package/dist/tokens/attrus-tokens.json +1089 -1089
  80. package/package.json +66 -64
@@ -1,374 +1,374 @@
1
- /* ============================================================
2
- OVERLAY SYSTEM — Modal + Drawer
3
- Both share scrim, entry/exit motion, focus management.
4
- All values from tokens.
5
- ============================================================ */
6
-
7
- /* =================== SCRIM =================== */
8
- .scrim {
9
- position: fixed;
10
- inset: 0;
11
- background: var(--color-scrim);
12
- backdrop-filter: blur(var(--scrim-blur));
13
- -webkit-backdrop-filter: blur(var(--scrim-blur));
14
- z-index: 9000;
15
- opacity: 0;
16
- pointer-events: none;
17
- transition: opacity var(--duration-base) var(--ease-standard);
18
- }
19
- .scrim.is-open {
20
- opacity: 1;
21
- pointer-events: auto;
22
- }
23
-
24
- /* Fallback for browsers without backdrop-filter — strengthen the tint to compensate. */
25
- @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
26
- .scrim { background: rgba(10, 15, 31, 0.55); }
27
- }
28
-
29
- /* =================== MODAL =================== */
30
- .modal {
31
- position: fixed;
32
- z-index: 9100;
33
- top: 50%; left: 50%;
34
- transform: translate(-50%, calc(-50% + 32px)) scale(0.96);
35
- width: min(560px, calc(100vw - var(--space-8)));
36
- max-height: calc(100vh - var(--space-12));
37
- display: flex;
38
- flex-direction: column;
39
- background: var(--color-background-surface);
40
- border-radius: var(--radius-lg);
41
- box-shadow: var(--shadow-2xl);
42
- opacity: 0;
43
- pointer-events: none;
44
- will-change: transform, opacity;
45
- /* Exit (default state) — quick fade + small slide */
46
- transition:
47
- opacity var(--duration-base) var(--ease-standard),
48
- transform var(--duration-base) var(--ease-standard);
49
- }
50
- .modal.is-open {
51
- opacity: 1;
52
- pointer-events: auto;
53
- transform: translate(-50%, -50%) scale(1);
54
- /* Entry — page-level pace, emphasized for the "land" feel */
55
- transition:
56
- opacity var(--duration-slower) var(--ease-standard),
57
- transform var(--duration-deliberate) var(--ease-emphasis);
58
- }
59
-
60
- /* Sizes */
61
- .modal-sm { width: min(420px, calc(100vw - var(--space-8))); }
62
- .modal-md { width: min(560px, calc(100vw - var(--space-8))); }
63
- .modal-lg { width: min(720px, calc(100vw - var(--space-8))); }
64
- .modal-xl { width: min(960px, calc(100vw - var(--space-8))); }
65
-
66
- /* Header */
67
- .modal-header {
68
- display: flex;
69
- align-items: flex-start;
70
- justify-content: space-between;
71
- gap: var(--space-4);
72
- padding: var(--space-6) var(--space-6) var(--space-4);
73
- }
74
- .modal-header-text { flex: 1; min-width: 0; }
75
- .modal-title {
76
- font-family: var(--font-sans);
77
- font-size: var(--fs-xl);
78
- font-weight: var(--w-bold);
79
- letter-spacing: var(--ls-title);
80
- line-height: var(--lh-tight);
81
- color: var(--color-foreground-default);
82
- margin: 0 0 var(--space-1);
83
- }
84
- .modal-subtitle {
85
- font-size: var(--fs-sm);
86
- color: var(--color-foreground-secondary);
87
- line-height: var(--lh-normal);
88
- margin: 0;
89
- }
90
- .modal-close {
91
- display: inline-flex;
92
- align-items: center;
93
- justify-content: center;
94
- width: 32px; height: 32px;
95
- background: transparent;
96
- border: none;
97
- border-radius: var(--radius-sm);
98
- color: var(--color-foreground-muted);
99
- cursor: pointer;
100
- flex-shrink: 0;
101
- transition: var(--transition-fast);
102
- }
103
- .modal-close:hover {
104
- background: var(--color-background-hover);
105
- color: var(--color-foreground-default);
106
- }
107
- .modal-close:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
108
- .modal-close i, .modal-close svg { width: 18px; height: 18px; stroke-width: var(--icon-stroke); }
109
-
110
- /* Body */
111
- .modal-body {
112
- padding: var(--space-2) var(--space-6) var(--space-6);
113
- overflow-y: auto;
114
- font-size: var(--fs-sm);
115
- color: var(--color-foreground-default);
116
- line-height: var(--lh-relaxed);
117
- flex: 1;
118
- }
119
- .modal-body p { margin: 0 0 var(--space-3); }
120
- .modal-body p:last-child { margin-bottom: 0; }
121
-
122
- /* Footer */
123
- .modal-footer {
124
- display: flex;
125
- align-items: center;
126
- justify-content: flex-end;
127
- gap: var(--space-3);
128
- padding: var(--space-4) var(--space-6) var(--space-6);
129
- border-top: 1px solid var(--color-border-subtle);
130
- margin-top: auto;
131
- flex-shrink: 0;
132
- }
133
- .modal-footer-spread { justify-content: space-between; }
134
- .modal-footer-aux {
135
- display: flex;
136
- align-items: center;
137
- gap: var(--space-2);
138
- font-size: var(--fs-xs);
139
- color: var(--color-foreground-muted);
140
- }
141
-
142
- /* Variant: destructive — top accent strip */
143
- .modal-destructive .modal-header {
144
- border-top: 3px solid var(--color-feedback-danger-foreground);
145
- padding-top: calc(var(--space-6) - 3px);
146
- border-top-left-radius: var(--radius-lg);
147
- border-top-right-radius: var(--radius-lg);
148
- }
149
- .modal-destructive .modal-icon {
150
- display: inline-flex;
151
- align-items: center;
152
- justify-content: center;
153
- width: 40px; height: 40px;
154
- background: var(--color-feedback-danger-background);
155
- color: var(--color-feedback-danger-foreground);
156
- border-radius: var(--radius-pill);
157
- margin-bottom: var(--space-3);
158
- }
159
- .modal-destructive .modal-icon i, .modal-destructive .modal-icon svg { width: 20px; height: 20px; stroke-width: var(--icon-stroke); }
160
-
161
-
162
- /* =================== DRAWER =================== */
163
- .drawer {
164
- position: fixed;
165
- z-index: 9100;
166
- background: var(--color-background-surface);
167
- display: flex;
168
- flex-direction: column;
169
- overflow-y: auto;
170
- overflow-x: hidden;
171
- box-shadow: var(--shadow-2xl);
172
- opacity: 0;
173
- pointer-events: none;
174
- will-change: transform, opacity;
175
- /* Exit — slide back out + fade */
176
- transition:
177
- transform var(--duration-slow) var(--ease-standard),
178
- opacity var(--duration-base) var(--ease-standard);
179
- }
180
- .drawer.is-open {
181
- opacity: 1;
182
- pointer-events: auto;
183
- /* Entry — page-level pace, emphasized */
184
- transition:
185
- transform var(--duration-deliberate) var(--ease-emphasis),
186
- opacity var(--duration-slower) var(--ease-standard);
187
- }
188
-
189
- /* Right (default). --drawer-w drives the width per size variant. */
190
- .drawer-right {
191
- --drawer-w: min(440px, 90vw); /* @kind spacing */
192
- top: 0; right: 0; bottom: 0;
193
- width: var(--drawer-w);
194
- border-top-left-radius: var(--radius-xs);
195
- border-bottom-left-radius: var(--radius-xs);
196
- transform: translateX(100%);
197
- }
198
- .drawer-right.is-open { transform: translateX(0); }
199
-
200
- /* Left */
201
- .drawer-left {
202
- --drawer-w: min(320px, 85vw); /* @kind spacing */
203
- top: 0; left: 0; bottom: 0;
204
- width: var(--drawer-w);
205
- border-top-right-radius: var(--radius-xs);
206
- border-bottom-right-radius: var(--radius-xs);
207
- transform: translateX(-100%);
208
- }
209
- .drawer-left.is-open { transform: translateX(0); }
210
-
211
- /* Bottom */
212
- .drawer-bottom {
213
- left: 0; right: 0; bottom: 0;
214
- max-height: 80vh;
215
- border-top-left-radius: var(--radius-sm);
216
- border-top-right-radius: var(--radius-sm);
217
- transform: translateY(100%);
218
- }
219
- .drawer-bottom.is-open { transform: translateY(0); }
220
-
221
- /* Drawer sizes (right/left) — feed --drawer-w so widths scale per variant */
222
- .drawer-sm { --drawer-w: min(360px, 90vw); /* @kind spacing */ width: var(--drawer-w); }
223
- .drawer-md { --drawer-w: min(440px, 90vw); /* @kind spacing */ width: var(--drawer-w); }
224
- .drawer-lg { --drawer-w: min(560px, 92vw); /* @kind spacing */ width: var(--drawer-w); }
225
- .drawer-xl { --drawer-w: min(720px, 94vw); /* @kind spacing */ width: var(--drawer-w); }
226
-
227
- /* Drawer parts (mirror modal vocabulary).
228
- Header and footer are STICKY with a liquid-glass background: body
229
- content scrolls underneath them with a frosted blur. The drawer
230
- itself is the scroll container (see .drawer overflow-y above). */
231
- .drawer-header {
232
- position: sticky;
233
- top: 0;
234
- z-index: 2;
235
- display: flex;
236
- align-items: flex-start;
237
- justify-content: space-between;
238
- gap: var(--space-4);
239
- padding: var(--space-6);
240
- background: var(--glass-white);
241
- -webkit-backdrop-filter: var(--glass-blur);
242
- backdrop-filter: var(--glass-blur);
243
- border-bottom: 1px solid var(--color-border-subtle);
244
- flex-shrink: 0;
245
- }
246
- @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
247
- .drawer-header, .drawer-footer { background: var(--color-background-surface); }
248
- }
249
- .drawer-header-text { flex: 1; min-width: 0; }
250
- .drawer-title {
251
- font-family: var(--font-sans);
252
- font-size: var(--fs-lg);
253
- font-weight: var(--w-bold);
254
- letter-spacing: var(--ls-title);
255
- line-height: var(--lh-tight);
256
- color: var(--color-foreground-default);
257
- margin: 0 0 var(--space-1);
258
- }
259
- .drawer-subtitle {
260
- font-size: var(--fs-sm);
261
- color: var(--color-foreground-secondary);
262
- line-height: var(--lh-normal);
263
- margin: 0;
264
- }
265
- .drawer-close {
266
- display: inline-flex;
267
- align-items: center;
268
- justify-content: center;
269
- align-self: flex-start;
270
- width: 32px; height: 32px;
271
- background: transparent;
272
- border: none;
273
- border-radius: var(--radius-sm);
274
- color: var(--color-foreground-muted);
275
- cursor: pointer;
276
- flex-shrink: 0;
277
- transition: var(--transition-fast);
278
- }
279
- .drawer-close:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
280
-
281
- /* Footer aux — left-side total/note when the footer is space-between (footerAux prop) */
282
- .drawer-footer-aux { font-size: var(--fs-xs); color: var(--color-foreground-muted); min-width: 0; }
283
-
284
- /* Mobile: footer actions stack full-width (doc §Header & Footer, variant 7) */
285
- @media (max-width: 560px) {
286
- .drawer-footer { flex-direction: column; align-items: stretch; gap: var(--space-2); padding-bottom: max(var(--space-4), env(safe-area-inset-bottom)); }
287
- .drawer-footer .btn { width: 100%; justify-content: center; }
288
- .drawer-footer-aux { text-align: center; margin-bottom: var(--space-1); }
289
- }
290
-
291
- /* Dense header — tight chrome for utility drawers (filters) */
292
- .drawer-header.is-dense { padding: var(--space-3) var(--space-4); }
293
- .drawer-header.is-dense .drawer-title { font-size: var(--fs-md); }
294
-
295
- /* Header with a second control row (search pill, tabs) */
296
- .drawer-header.has-extra { flex-wrap: wrap; }
297
- .drawer-header > .drawer-header-extra { flex-basis: 100%; min-width: 0; }
298
- .drawer-close:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
299
- .drawer-close i, .drawer-close svg { width: 18px; height: 18px; stroke-width: var(--icon-stroke); }
300
-
301
- /* Header lead tile — standardized icon container on the LEFT of the header.
302
- 40×40, top-aligned, consistent radius; caller sets bg/color per context. */
303
- .drawer-lead {
304
- width: 40px; height: 40px;
305
- flex-shrink: 0;
306
- align-self: flex-start;
307
- display: inline-flex;
308
- align-items: center;
309
- justify-content: center;
310
- border-radius: var(--radius-md);
311
- }
312
- .drawer-lead i, .drawer-lead svg { width: 20px; height: 20px; stroke-width: var(--icon-stroke); }
313
-
314
- .drawer-body {
315
- padding: var(--space-6);
316
- flex: 1 0 auto;
317
- font-size: var(--fs-sm);
318
- color: var(--color-foreground-default);
319
- line-height: var(--lh-relaxed);
320
- }
321
-
322
- .drawer-footer {
323
- position: sticky;
324
- bottom: 0;
325
- z-index: 2;
326
- display: flex;
327
- align-items: center;
328
- justify-content: flex-end;
329
- gap: var(--space-3);
330
- padding: var(--space-5) var(--space-6);
331
- background: var(--glass-white);
332
- -webkit-backdrop-filter: var(--glass-blur);
333
- backdrop-filter: var(--glass-blur);
334
- border-top: 1px solid var(--color-border-subtle);
335
- flex-shrink: 0;
336
- }
337
-
338
- /* Drawer-bottom drag handle (mobile-style) */
339
- .drawer-handle {
340
- position: sticky;
341
- top: 0;
342
- z-index: 3;
343
- display: flex;
344
- justify-content: center;
345
- padding: var(--space-3) 0 var(--space-1);
346
- background: var(--glass-white);
347
- -webkit-backdrop-filter: var(--glass-blur);
348
- backdrop-filter: var(--glass-blur);
349
- flex-shrink: 0;
350
- }
351
- /* When a handle precedes the header (bottom drawer), the header no longer
352
- needs its own glass top — they read as one frosted bar. */
353
- .drawer-handle + .drawer-header { border-top: 0; }
354
- .drawer-handle::before {
355
- content: "";
356
- width: 36px;
357
- height: 4px;
358
- background: var(--color-border-strong);
359
- border-radius: var(--radius-pill);
360
- }
361
-
362
- /* Reduced motion */
363
- @media (prefers-reduced-motion: reduce) {
364
- .scrim, .modal, .drawer, .modal.is-open, .drawer.is-open {
365
- transition: opacity var(--duration-instant) linear !important;
366
- }
367
- .modal.is-open { transform: translate(-50%, -50%); }
368
- .drawer-right.is-open,
369
- .drawer-left.is-open,
370
- .drawer-bottom.is-open { transform: none; }
371
- .drawer-right { transform: none; }
372
- .drawer-left { transform: none; }
373
- .drawer-bottom { transform: none; }
374
- }
1
+ /* ============================================================
2
+ OVERLAY SYSTEM — Modal + Drawer
3
+ Both share scrim, entry/exit motion, focus management.
4
+ All values from tokens.
5
+ ============================================================ */
6
+
7
+ /* =================== SCRIM =================== */
8
+ .scrim {
9
+ position: fixed;
10
+ inset: 0;
11
+ background: var(--color-scrim);
12
+ backdrop-filter: blur(var(--scrim-blur));
13
+ -webkit-backdrop-filter: blur(var(--scrim-blur));
14
+ z-index: 9000;
15
+ opacity: 0;
16
+ pointer-events: none;
17
+ transition: opacity var(--duration-base) var(--ease-standard);
18
+ }
19
+ .scrim.is-open {
20
+ opacity: 1;
21
+ pointer-events: auto;
22
+ }
23
+
24
+ /* Fallback for browsers without backdrop-filter — strengthen the tint to compensate. */
25
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
26
+ .scrim { background: rgba(10, 15, 31, 0.55); }
27
+ }
28
+
29
+ /* =================== MODAL =================== */
30
+ .modal {
31
+ position: fixed;
32
+ z-index: 9100;
33
+ top: 50%; left: 50%;
34
+ transform: translate(-50%, calc(-50% + 32px)) scale(0.96);
35
+ width: min(560px, calc(100vw - var(--space-8)));
36
+ max-height: calc(100vh - var(--space-12));
37
+ display: flex;
38
+ flex-direction: column;
39
+ background: var(--color-background-surface);
40
+ border-radius: var(--radius-lg);
41
+ box-shadow: var(--shadow-2xl);
42
+ opacity: 0;
43
+ pointer-events: none;
44
+ will-change: transform, opacity;
45
+ /* Exit (default state) — quick fade + small slide */
46
+ transition:
47
+ opacity var(--duration-base) var(--ease-standard),
48
+ transform var(--duration-base) var(--ease-standard);
49
+ }
50
+ .modal.is-open {
51
+ opacity: 1;
52
+ pointer-events: auto;
53
+ transform: translate(-50%, -50%) scale(1);
54
+ /* Entry — page-level pace, emphasized for the "land" feel */
55
+ transition:
56
+ opacity var(--duration-slower) var(--ease-standard),
57
+ transform var(--duration-deliberate) var(--ease-emphasis);
58
+ }
59
+
60
+ /* Sizes */
61
+ .modal-sm { width: min(420px, calc(100vw - var(--space-8))); }
62
+ .modal-md { width: min(560px, calc(100vw - var(--space-8))); }
63
+ .modal-lg { width: min(720px, calc(100vw - var(--space-8))); }
64
+ .modal-xl { width: min(960px, calc(100vw - var(--space-8))); }
65
+
66
+ /* Header */
67
+ .modal-header {
68
+ display: flex;
69
+ align-items: flex-start;
70
+ justify-content: space-between;
71
+ gap: var(--space-4);
72
+ padding: var(--space-6) var(--space-6) var(--space-4);
73
+ }
74
+ .modal-header-text { flex: 1; min-width: 0; }
75
+ .modal-title {
76
+ font-family: var(--font-sans);
77
+ font-size: var(--fs-xl);
78
+ font-weight: var(--w-bold);
79
+ letter-spacing: var(--ls-title);
80
+ line-height: var(--lh-tight);
81
+ color: var(--color-foreground-default);
82
+ margin: 0 0 var(--space-1);
83
+ }
84
+ .modal-subtitle {
85
+ font-size: var(--fs-sm);
86
+ color: var(--color-foreground-secondary);
87
+ line-height: var(--lh-normal);
88
+ margin: 0;
89
+ }
90
+ .modal-close {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ justify-content: center;
94
+ width: 32px; height: 32px;
95
+ background: transparent;
96
+ border: none;
97
+ border-radius: var(--radius-sm);
98
+ color: var(--color-foreground-muted);
99
+ cursor: pointer;
100
+ flex-shrink: 0;
101
+ transition: var(--transition-fast);
102
+ }
103
+ .modal-close:hover {
104
+ background: var(--color-background-hover);
105
+ color: var(--color-foreground-default);
106
+ }
107
+ .modal-close:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
108
+ .modal-close i, .modal-close svg { width: 18px; height: 18px; stroke-width: var(--icon-stroke); }
109
+
110
+ /* Body */
111
+ .modal-body {
112
+ padding: var(--space-2) var(--space-6) var(--space-6);
113
+ overflow-y: auto;
114
+ font-size: var(--fs-sm);
115
+ color: var(--color-foreground-default);
116
+ line-height: var(--lh-relaxed);
117
+ flex: 1;
118
+ }
119
+ .modal-body p { margin: 0 0 var(--space-3); }
120
+ .modal-body p:last-child { margin-bottom: 0; }
121
+
122
+ /* Footer */
123
+ .modal-footer {
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: flex-end;
127
+ gap: var(--space-3);
128
+ padding: var(--space-4) var(--space-6) var(--space-6);
129
+ border-top: 1px solid var(--color-border-subtle);
130
+ margin-top: auto;
131
+ flex-shrink: 0;
132
+ }
133
+ .modal-footer-spread { justify-content: space-between; }
134
+ .modal-footer-aux {
135
+ display: flex;
136
+ align-items: center;
137
+ gap: var(--space-2);
138
+ font-size: var(--fs-xs);
139
+ color: var(--color-foreground-muted);
140
+ }
141
+
142
+ /* Variant: destructive — top accent strip */
143
+ .modal-destructive .modal-header {
144
+ border-top: 3px solid var(--color-feedback-danger-foreground);
145
+ padding-top: calc(var(--space-6) - 3px);
146
+ border-top-left-radius: var(--radius-lg);
147
+ border-top-right-radius: var(--radius-lg);
148
+ }
149
+ .modal-destructive .modal-icon {
150
+ display: inline-flex;
151
+ align-items: center;
152
+ justify-content: center;
153
+ width: 40px; height: 40px;
154
+ background: var(--color-feedback-danger-background);
155
+ color: var(--color-feedback-danger-foreground);
156
+ border-radius: var(--radius-pill);
157
+ margin-bottom: var(--space-3);
158
+ }
159
+ .modal-destructive .modal-icon i, .modal-destructive .modal-icon svg { width: 20px; height: 20px; stroke-width: var(--icon-stroke); }
160
+
161
+
162
+ /* =================== DRAWER =================== */
163
+ .drawer {
164
+ position: fixed;
165
+ z-index: 9100;
166
+ background: var(--color-background-surface);
167
+ display: flex;
168
+ flex-direction: column;
169
+ overflow-y: auto;
170
+ overflow-x: hidden;
171
+ box-shadow: var(--shadow-2xl);
172
+ opacity: 0;
173
+ pointer-events: none;
174
+ will-change: transform, opacity;
175
+ /* Exit — slide back out + fade */
176
+ transition:
177
+ transform var(--duration-slow) var(--ease-standard),
178
+ opacity var(--duration-base) var(--ease-standard);
179
+ }
180
+ .drawer.is-open {
181
+ opacity: 1;
182
+ pointer-events: auto;
183
+ /* Entry — page-level pace, emphasized */
184
+ transition:
185
+ transform var(--duration-deliberate) var(--ease-emphasis),
186
+ opacity var(--duration-slower) var(--ease-standard);
187
+ }
188
+
189
+ /* Right (default). --drawer-w drives the width per size variant. */
190
+ .drawer-right {
191
+ --drawer-w: min(440px, 90vw); /* @kind spacing */
192
+ top: 0; right: 0; bottom: 0;
193
+ width: var(--drawer-w);
194
+ border-top-left-radius: var(--radius-xs);
195
+ border-bottom-left-radius: var(--radius-xs);
196
+ transform: translateX(100%);
197
+ }
198
+ .drawer-right.is-open { transform: translateX(0); }
199
+
200
+ /* Left */
201
+ .drawer-left {
202
+ --drawer-w: min(320px, 85vw); /* @kind spacing */
203
+ top: 0; left: 0; bottom: 0;
204
+ width: var(--drawer-w);
205
+ border-top-right-radius: var(--radius-xs);
206
+ border-bottom-right-radius: var(--radius-xs);
207
+ transform: translateX(-100%);
208
+ }
209
+ .drawer-left.is-open { transform: translateX(0); }
210
+
211
+ /* Bottom */
212
+ .drawer-bottom {
213
+ left: 0; right: 0; bottom: 0;
214
+ max-height: 80vh;
215
+ border-top-left-radius: var(--radius-sm);
216
+ border-top-right-radius: var(--radius-sm);
217
+ transform: translateY(100%);
218
+ }
219
+ .drawer-bottom.is-open { transform: translateY(0); }
220
+
221
+ /* Drawer sizes (right/left) — feed --drawer-w so widths scale per variant */
222
+ .drawer-sm { --drawer-w: min(360px, 90vw); /* @kind spacing */ width: var(--drawer-w); }
223
+ .drawer-md { --drawer-w: min(440px, 90vw); /* @kind spacing */ width: var(--drawer-w); }
224
+ .drawer-lg { --drawer-w: min(560px, 92vw); /* @kind spacing */ width: var(--drawer-w); }
225
+ .drawer-xl { --drawer-w: min(720px, 94vw); /* @kind spacing */ width: var(--drawer-w); }
226
+
227
+ /* Drawer parts (mirror modal vocabulary).
228
+ Header and footer are STICKY with a liquid-glass background: body
229
+ content scrolls underneath them with a frosted blur. The drawer
230
+ itself is the scroll container (see .drawer overflow-y above). */
231
+ .drawer-header {
232
+ position: sticky;
233
+ top: 0;
234
+ z-index: 2;
235
+ display: flex;
236
+ align-items: flex-start;
237
+ justify-content: space-between;
238
+ gap: var(--space-4);
239
+ padding: var(--space-6);
240
+ background: var(--glass-white);
241
+ -webkit-backdrop-filter: var(--glass-blur);
242
+ backdrop-filter: var(--glass-blur);
243
+ border-bottom: 1px solid var(--color-border-subtle);
244
+ flex-shrink: 0;
245
+ }
246
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
247
+ .drawer-header, .drawer-footer { background: var(--color-background-surface); }
248
+ }
249
+ .drawer-header-text { flex: 1; min-width: 0; }
250
+ .drawer-title {
251
+ font-family: var(--font-sans);
252
+ font-size: var(--fs-lg);
253
+ font-weight: var(--w-bold);
254
+ letter-spacing: var(--ls-title);
255
+ line-height: var(--lh-tight);
256
+ color: var(--color-foreground-default);
257
+ margin: 0 0 var(--space-1);
258
+ }
259
+ .drawer-subtitle {
260
+ font-size: var(--fs-sm);
261
+ color: var(--color-foreground-secondary);
262
+ line-height: var(--lh-normal);
263
+ margin: 0;
264
+ }
265
+ .drawer-close {
266
+ display: inline-flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ align-self: flex-start;
270
+ width: 32px; height: 32px;
271
+ background: transparent;
272
+ border: none;
273
+ border-radius: var(--radius-sm);
274
+ color: var(--color-foreground-muted);
275
+ cursor: pointer;
276
+ flex-shrink: 0;
277
+ transition: var(--transition-fast);
278
+ }
279
+ .drawer-close:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
280
+
281
+ /* Footer aux — left-side total/note when the footer is space-between (footerAux prop) */
282
+ .drawer-footer-aux { font-size: var(--fs-xs); color: var(--color-foreground-muted); min-width: 0; }
283
+
284
+ /* Mobile: footer actions stack full-width (doc §Header & Footer, variant 7) */
285
+ @media (max-width: 560px) {
286
+ .drawer-footer { flex-direction: column; align-items: stretch; gap: var(--space-2); padding-bottom: max(var(--space-4), env(safe-area-inset-bottom)); }
287
+ .drawer-footer .btn { width: 100%; justify-content: center; }
288
+ .drawer-footer-aux { text-align: center; margin-bottom: var(--space-1); }
289
+ }
290
+
291
+ /* Dense header — tight chrome for utility drawers (filters) */
292
+ .drawer-header.is-dense { padding: var(--space-3) var(--space-4); }
293
+ .drawer-header.is-dense .drawer-title { font-size: var(--fs-md); }
294
+
295
+ /* Header with a second control row (search pill, tabs) */
296
+ .drawer-header.has-extra { flex-wrap: wrap; }
297
+ .drawer-header > .drawer-header-extra { flex-basis: 100%; min-width: 0; }
298
+ .drawer-close:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
299
+ .drawer-close i, .drawer-close svg { width: 18px; height: 18px; stroke-width: var(--icon-stroke); }
300
+
301
+ /* Header lead tile — standardized icon container on the LEFT of the header.
302
+ 40×40, top-aligned, consistent radius; caller sets bg/color per context. */
303
+ .drawer-lead {
304
+ width: 40px; height: 40px;
305
+ flex-shrink: 0;
306
+ align-self: flex-start;
307
+ display: inline-flex;
308
+ align-items: center;
309
+ justify-content: center;
310
+ border-radius: var(--radius-md);
311
+ }
312
+ .drawer-lead i, .drawer-lead svg { width: 20px; height: 20px; stroke-width: var(--icon-stroke); }
313
+
314
+ .drawer-body {
315
+ padding: var(--space-6);
316
+ flex: 1 0 auto;
317
+ font-size: var(--fs-sm);
318
+ color: var(--color-foreground-default);
319
+ line-height: var(--lh-relaxed);
320
+ }
321
+
322
+ .drawer-footer {
323
+ position: sticky;
324
+ bottom: 0;
325
+ z-index: 2;
326
+ display: flex;
327
+ align-items: center;
328
+ justify-content: flex-end;
329
+ gap: var(--space-3);
330
+ padding: var(--space-5) var(--space-6);
331
+ background: var(--glass-white);
332
+ -webkit-backdrop-filter: var(--glass-blur);
333
+ backdrop-filter: var(--glass-blur);
334
+ border-top: 1px solid var(--color-border-subtle);
335
+ flex-shrink: 0;
336
+ }
337
+
338
+ /* Drawer-bottom drag handle (mobile-style) */
339
+ .drawer-handle {
340
+ position: sticky;
341
+ top: 0;
342
+ z-index: 3;
343
+ display: flex;
344
+ justify-content: center;
345
+ padding: var(--space-3) 0 var(--space-1);
346
+ background: var(--glass-white);
347
+ -webkit-backdrop-filter: var(--glass-blur);
348
+ backdrop-filter: var(--glass-blur);
349
+ flex-shrink: 0;
350
+ }
351
+ /* When a handle precedes the header (bottom drawer), the header no longer
352
+ needs its own glass top — they read as one frosted bar. */
353
+ .drawer-handle + .drawer-header { border-top: 0; }
354
+ .drawer-handle::before {
355
+ content: "";
356
+ width: 36px;
357
+ height: 4px;
358
+ background: var(--color-border-strong);
359
+ border-radius: var(--radius-pill);
360
+ }
361
+
362
+ /* Reduced motion */
363
+ @media (prefers-reduced-motion: reduce) {
364
+ .scrim, .modal, .drawer, .modal.is-open, .drawer.is-open {
365
+ transition: opacity var(--duration-instant) linear !important;
366
+ }
367
+ .modal.is-open { transform: translate(-50%, -50%); }
368
+ .drawer-right.is-open,
369
+ .drawer-left.is-open,
370
+ .drawer-bottom.is-open { transform: none; }
371
+ .drawer-right { transform: none; }
372
+ .drawer-left { transform: none; }
373
+ .drawer-bottom { transform: none; }
374
+ }