@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,301 +1,301 @@
1
- /* ============================================================
2
- BUTTONS — ATTRUS Design System
3
- Component CSS — pure tokens, no hardcoded values.
4
-
5
- Anatomy:
6
- [leading icon?] [label] [trailing icon?]
7
-
8
- Variants (semantic):
9
- .btn-primary | brand fill — main action
10
- .btn-secondary | neutral fill + hairline border — alternative actions
11
- .btn-tertiary | soft neutral fill (borderless) — quiet affirmative
12
- .btn-ghost | no chrome — toolbar / cancel / low priority
13
- .btn-link | inline text-link styling
14
- .btn-danger | destructive solid — irreversible
15
- .btn-danger-outline | destructive secondary — contextual remove
16
- .btn-success | strong positive — approve / confirm
17
- .btn-inverse | white pill on dark/brand surfaces
18
-
19
- Modifier:
20
- .btn-icon | icon-only (square hit-area)
21
-
22
- Sizes:
23
- .btn-xs (24h) .btn-sm (32h) default (40h) .btn-lg (48h)
24
-
25
- States: default | :hover | :active | :focus-visible | [disabled] | .is-loading
26
- ============================================================ */
27
-
28
-
29
- /* ----------------------------------------------------------------
30
- 1. BASE
31
- ---------------------------------------------------------------- */
32
- .btn {
33
- /* Layout */
34
- display: inline-flex;
35
- align-items: center;
36
- justify-content: center;
37
- gap: var(--space-2);
38
- white-space: nowrap;
39
- flex-shrink: 0;
40
-
41
- /* Box — default size = md (40px tall) */
42
- height: 40px;
43
- padding: 0 var(--space-5);
44
- border: 1px solid transparent;
45
- border-radius: var(--radius-pill);
46
-
47
- /* Type */
48
- font-family: var(--font-sans);
49
- font-size: var(--fs-md);
50
- font-weight: var(--w-semibold);
51
- line-height: 1;
52
- letter-spacing: var(--ls-body);
53
-
54
- /* Behavior */
55
- cursor: pointer;
56
- user-select: none;
57
- -webkit-tap-highlight-color: transparent;
58
- background: transparent;
59
- color: inherit;
60
-
61
- /* Motion */
62
- transition:
63
- background var(--transition-fast),
64
- border-color var(--transition-fast),
65
- color var(--transition-fast),
66
- box-shadow var(--transition-fast),
67
- transform var(--transition-fast);
68
- }
69
-
70
- .btn:active { transform: translateY(0.5px); }
71
-
72
- .btn:focus { outline: none; }
73
- .btn:focus-visible { box-shadow: var(--shadow-focus); }
74
-
75
- .btn[disabled],
76
- .btn.is-disabled {
77
- cursor: not-allowed;
78
- opacity: 0.5;
79
- pointer-events: none;
80
- }
81
-
82
- /* Icon sizing inside buttons (Lucide replaces <i> with <svg>) */
83
- .btn > i, .btn > svg,
84
- .btn > svg {
85
- width: var(--icon-sm);
86
- height: var(--icon-sm);
87
- stroke-width: var(--icon-stroke);
88
- flex-shrink: 0;
89
- }
90
-
91
-
92
- /* ----------------------------------------------------------------
93
- 2. SIZES
94
- ---------------------------------------------------------------- */
95
- .btn-xs {
96
- height: 24px;
97
- padding: 0 var(--space-3);
98
- font-size: var(--fs-sm);
99
- gap: var(--space-1);
100
- }
101
- .btn-xs > i, .btn-xs > svg, .btn-xs > svg { width: var(--icon-xs); height: var(--icon-xs); }
102
-
103
- .btn-sm {
104
- height: 32px;
105
- padding: 0 var(--space-4);
106
- font-size: var(--fs-md);
107
- }
108
- .btn-sm > i, .btn-sm > svg, .btn-sm > svg { width: var(--icon-xs); height: var(--icon-xs); }
109
-
110
- /* default = md (40px) — defined in .btn */
111
-
112
- .btn-lg {
113
- height: 48px;
114
- padding: 0 var(--space-6);
115
- font-size: var(--fs-base);
116
- gap: var(--space-3);
117
- }
118
- .btn-lg > i, .btn-lg > svg, .btn-lg > svg { width: var(--icon-md); height: var(--icon-md); }
119
-
120
-
121
- /* ----------------------------------------------------------------
122
- 3. ICON-ONLY MODIFIER
123
- ---------------------------------------------------------------- */
124
- .btn-icon { padding: 0; aspect-ratio: 1 / 1; }
125
- .btn-icon.btn-xs { width: 24px; }
126
- .btn-icon.btn-sm { width: 32px; }
127
- .btn-icon { width: 40px; }
128
- .btn-icon.btn-lg { width: 48px; }
129
-
130
-
131
- /* ----------------------------------------------------------------
132
- 4. VARIANTS
133
- ---------------------------------------------------------------- */
134
-
135
- /* PRIMARY — brand-blue solid */
136
- .btn-primary {
137
- background: var(--color-brand-secondary-500);
138
- color: var(--color-foreground-on-solid);
139
- }
140
- .btn-primary:hover { background: var(--color-brand-secondary-600); }
141
- .btn-primary:active { background: var(--color-brand-secondary-700); }
142
- .btn-primary:focus-visible { box-shadow: var(--shadow-focus); }
143
-
144
-
145
- /* SECONDARY — soft neutral fill + hairline border */
146
- .btn-secondary {
147
- background: var(--color-background-subtle);
148
- color: var(--color-foreground-default);
149
- border-color: var(--color-border-default);
150
- }
151
- .btn-secondary:hover { background: var(--color-background-hover); border-color: var(--color-border-strong); }
152
- .btn-secondary:active { background: var(--color-background-press); }
153
- .btn-secondary:focus-visible { box-shadow: var(--shadow-focus); }
154
-
155
-
156
- /* TERTIARY — soft neutral fill, borderless (quieter than secondary) */
157
- .btn-tertiary {
158
- background: color-mix(in srgb, var(--color-foreground-default) 7%, transparent);
159
- color: var(--color-foreground-default);
160
- border-color: transparent;
161
- }
162
- .btn-tertiary:hover { background: color-mix(in srgb, var(--color-foreground-default) 12%, transparent); }
163
- .btn-tertiary:active { background: color-mix(in srgb, var(--color-foreground-default) 16%, transparent); }
164
- .btn-tertiary:focus-visible { box-shadow: var(--shadow-focus); }
165
-
166
-
167
- /* GHOST — no chrome until hover */
168
- .btn-ghost {
169
- background: transparent;
170
- color: var(--color-foreground-secondary);
171
- }
172
- .btn-ghost:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
173
- .btn-ghost:active { background: var(--color-background-press); }
174
- .btn-ghost:focus-visible { box-shadow: var(--shadow-focus); }
175
-
176
-
177
- /* LINK — looks like inline text link */
178
- .btn-link {
179
- background: transparent;
180
- color: var(--color-foreground-highlight);
181
- height: auto;
182
- padding: 0;
183
- border: 0;
184
- border-radius: var(--radius-2xs);
185
- text-decoration: none;
186
- }
187
- .btn-link:hover { color: var(--color-brand-secondary-700); text-decoration: underline; text-underline-offset: 2px; }
188
- .btn-link:active { color: var(--color-brand-secondary-800); }
189
- .btn-link:focus-visible { box-shadow: var(--shadow-focus); }
190
-
191
-
192
- /* DANGER — destructive solid */
193
- .btn-danger {
194
- background: var(--color-danger-500);
195
- color: var(--color-foreground-on-solid);
196
- }
197
- .btn-danger:hover { background: var(--color-danger-700); }
198
- .btn-danger:active { background: var(--color-danger-900); }
199
- .btn-danger:focus-visible { box-shadow: var(--shadow-focus-danger); }
200
-
201
-
202
- /* DANGER OUTLINE — destructive contextual */
203
- .btn-danger-outline {
204
- background: var(--color-background-surface);
205
- color: var(--color-danger-500);
206
- border-color: var(--color-feedback-danger-border);
207
- }
208
- .btn-danger-outline:hover { background: var(--color-feedback-danger-background); border-color: var(--color-danger-500); }
209
- .btn-danger-outline:active { background: color-mix(in srgb, var(--color-feedback-danger-background) 85%, var(--color-danger-500)); }
210
- .btn-danger-outline:focus-visible { box-shadow: var(--shadow-focus-danger); }
211
-
212
-
213
- /* SUCCESS — strong positive (rare) */
214
- .btn-success {
215
- background: var(--color-success-500);
216
- color: var(--color-foreground-on-solid);
217
- }
218
- .btn-success:hover { background: var(--color-success-700); }
219
- .btn-success:active { background: var(--color-success-900); }
220
- .btn-success:focus-visible { box-shadow: var(--shadow-focus-success); }
221
-
222
-
223
- /* INVERSE — white pill on dark/brand surfaces */
224
- .btn-inverse {
225
- background: var(--color-pure-white);
226
- color: var(--color-brand-secondary-700);
227
- }
228
- .btn-inverse:hover { background: var(--color-neutral-100); color: var(--color-brand-secondary-800); }
229
- .btn-inverse:active { background: var(--color-neutral-150); }
230
- .btn-inverse:focus-visible { box-shadow: var(--shadow-focus-sky); }
231
-
232
-
233
- /* ----------------------------------------------------------------
234
- 5. ON-INVERSE GHOSTS — for use inside .inverse / dark surfaces
235
- Apply BOTH .btn-ghost AND .on-inverse
236
- ---------------------------------------------------------------- */
237
- .btn-ghost.on-inverse {
238
- color: var(--color-foreground-inverse-secondary);
239
- }
240
- .btn-ghost.on-inverse:hover { background: var(--color-background-inverse-hover); color: var(--color-foreground-inverse-default); }
241
- .btn-ghost.on-inverse:active { background: var(--color-background-inverse-press); }
242
-
243
- /* Outline ghost on inverse — pairs with .btn-secondary semantics */
244
- .btn-secondary.on-inverse {
245
- background: transparent;
246
- color: var(--color-foreground-inverse-default);
247
- border-color: var(--color-border-inverse-strong);
248
- }
249
- .btn-secondary.on-inverse:hover { background: var(--color-background-inverse-hover); border-color: var(--color-foreground-inverse-secondary); }
250
- .btn-secondary.on-inverse:active { background: var(--color-background-inverse-press); }
251
-
252
-
253
- /* ----------------------------------------------------------------
254
- 6. LOADING STATE
255
- Add .is-loading; keeps current width but hides label/icons,
256
- shows a centered spinner.
257
- ---------------------------------------------------------------- */
258
- .btn.is-loading {
259
- position: relative;
260
- color: transparent !important;
261
- pointer-events: none;
262
- }
263
- .btn.is-loading > * { visibility: hidden; }
264
- .btn.is-loading::after {
265
- content: "";
266
- position: absolute;
267
- top: 50%; left: 50%;
268
- width: var(--icon-sm);
269
- height: var(--icon-sm);
270
- margin: calc(var(--icon-sm) / -2) 0 0 calc(var(--icon-sm) / -2);
271
- border: 2px solid currentColor;
272
- border-top-color: transparent;
273
- border-radius: var(--radius-pill);
274
- animation: btn-spin var(--duration-slower) linear infinite;
275
- /* Re-introduce currentColor where the variant set it; ::after inherits the parent's color BEFORE the !important override */
276
- color: var(--color-foreground-default);
277
- }
278
- .btn-primary.is-loading::after,
279
- .btn-danger.is-loading::after,
280
- .btn-success.is-loading::after { color: var(--color-pure-white); }
281
- .btn-tertiary.is-loading::after { color: var(--color-foreground-default); }
282
-
283
- @keyframes btn-spin {
284
- to { transform: rotate(360deg); }
285
- }
286
-
287
-
288
- /* ----------------------------------------------------------------
289
- 7. BUTTON GROUP — segmented / toolbar
290
- ---------------------------------------------------------------- */
291
- .btn-group {
292
- display: inline-flex;
293
- }
294
- .btn-group .btn {
295
- border-radius: 0;
296
- }
297
- .btn-group .btn:first-child { border-top-left-radius: var(--radius-pill); border-bottom-left-radius: var(--radius-pill); }
298
- .btn-group .btn:last-child { border-top-right-radius: var(--radius-pill); border-bottom-right-radius: var(--radius-pill); }
299
- .btn-group .btn + .btn { margin-left: -1px; }
300
- .btn-group .btn:focus-visible,
301
- .btn-group .btn:hover { z-index: 1; }
1
+ /* ============================================================
2
+ BUTTONS — ATTRUS Design System
3
+ Component CSS — pure tokens, no hardcoded values.
4
+
5
+ Anatomy:
6
+ [leading icon?] [label] [trailing icon?]
7
+
8
+ Variants (semantic):
9
+ .btn-primary | brand fill — main action
10
+ .btn-secondary | neutral fill + hairline border — alternative actions
11
+ .btn-tertiary | soft neutral fill (borderless) — quiet affirmative
12
+ .btn-ghost | no chrome — toolbar / cancel / low priority
13
+ .btn-link | inline text-link styling
14
+ .btn-danger | destructive solid — irreversible
15
+ .btn-danger-outline | destructive secondary — contextual remove
16
+ .btn-success | strong positive — approve / confirm
17
+ .btn-inverse | white pill on dark/brand surfaces
18
+
19
+ Modifier:
20
+ .btn-icon | icon-only (square hit-area)
21
+
22
+ Sizes:
23
+ .btn-xs (24h) .btn-sm (32h) default (40h) .btn-lg (48h)
24
+
25
+ States: default | :hover | :active | :focus-visible | [disabled] | .is-loading
26
+ ============================================================ */
27
+
28
+
29
+ /* ----------------------------------------------------------------
30
+ 1. BASE
31
+ ---------------------------------------------------------------- */
32
+ .btn {
33
+ /* Layout */
34
+ display: inline-flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ gap: var(--space-2);
38
+ white-space: nowrap;
39
+ flex-shrink: 0;
40
+
41
+ /* Box — default size = md (40px tall) */
42
+ height: 40px;
43
+ padding: 0 var(--space-5);
44
+ border: 1px solid transparent;
45
+ border-radius: var(--radius-pill);
46
+
47
+ /* Type */
48
+ font-family: var(--font-sans);
49
+ font-size: var(--fs-md);
50
+ font-weight: var(--w-semibold);
51
+ line-height: 1;
52
+ letter-spacing: var(--ls-body);
53
+
54
+ /* Behavior */
55
+ cursor: pointer;
56
+ user-select: none;
57
+ -webkit-tap-highlight-color: transparent;
58
+ background: transparent;
59
+ color: inherit;
60
+
61
+ /* Motion */
62
+ transition:
63
+ background var(--transition-fast),
64
+ border-color var(--transition-fast),
65
+ color var(--transition-fast),
66
+ box-shadow var(--transition-fast),
67
+ transform var(--transition-fast);
68
+ }
69
+
70
+ .btn:active { transform: translateY(0.5px); }
71
+
72
+ .btn:focus { outline: none; }
73
+ .btn:focus-visible { box-shadow: var(--shadow-focus); }
74
+
75
+ .btn[disabled],
76
+ .btn.is-disabled {
77
+ cursor: not-allowed;
78
+ opacity: 0.5;
79
+ pointer-events: none;
80
+ }
81
+
82
+ /* Icon sizing inside buttons (Lucide replaces <i> with <svg>) */
83
+ .btn > i, .btn > svg,
84
+ .btn > svg {
85
+ width: var(--icon-sm);
86
+ height: var(--icon-sm);
87
+ stroke-width: var(--icon-stroke);
88
+ flex-shrink: 0;
89
+ }
90
+
91
+
92
+ /* ----------------------------------------------------------------
93
+ 2. SIZES
94
+ ---------------------------------------------------------------- */
95
+ .btn-xs {
96
+ height: 24px;
97
+ padding: 0 var(--space-3);
98
+ font-size: var(--fs-sm);
99
+ gap: var(--space-1);
100
+ }
101
+ .btn-xs > i, .btn-xs > svg, .btn-xs > svg { width: var(--icon-xs); height: var(--icon-xs); }
102
+
103
+ .btn-sm {
104
+ height: 32px;
105
+ padding: 0 var(--space-4);
106
+ font-size: var(--fs-md);
107
+ }
108
+ .btn-sm > i, .btn-sm > svg, .btn-sm > svg { width: var(--icon-xs); height: var(--icon-xs); }
109
+
110
+ /* default = md (40px) — defined in .btn */
111
+
112
+ .btn-lg {
113
+ height: 48px;
114
+ padding: 0 var(--space-6);
115
+ font-size: var(--fs-base);
116
+ gap: var(--space-3);
117
+ }
118
+ .btn-lg > i, .btn-lg > svg, .btn-lg > svg { width: var(--icon-md); height: var(--icon-md); }
119
+
120
+
121
+ /* ----------------------------------------------------------------
122
+ 3. ICON-ONLY MODIFIER
123
+ ---------------------------------------------------------------- */
124
+ .btn-icon { padding: 0; aspect-ratio: 1 / 1; }
125
+ .btn-icon.btn-xs { width: 24px; }
126
+ .btn-icon.btn-sm { width: 32px; }
127
+ .btn-icon { width: 40px; }
128
+ .btn-icon.btn-lg { width: 48px; }
129
+
130
+
131
+ /* ----------------------------------------------------------------
132
+ 4. VARIANTS
133
+ ---------------------------------------------------------------- */
134
+
135
+ /* PRIMARY — brand solid */
136
+ .btn-primary {
137
+ background: var(--color-brand-solid);
138
+ color: var(--color-brand-on-solid);
139
+ }
140
+ .btn-primary:hover { background: var(--color-brand-solid-hover); }
141
+ .btn-primary:active { background: var(--color-brand-solid-active); }
142
+ .btn-primary:focus-visible { box-shadow: var(--shadow-focus); }
143
+
144
+
145
+ /* SECONDARY — soft neutral fill + hairline border */
146
+ .btn-secondary {
147
+ background: var(--color-background-subtle);
148
+ color: var(--color-foreground-default);
149
+ border-color: var(--color-border-default);
150
+ }
151
+ .btn-secondary:hover { background: var(--color-background-hover); border-color: var(--color-border-strong); }
152
+ .btn-secondary:active { background: var(--color-background-press); }
153
+ .btn-secondary:focus-visible { box-shadow: var(--shadow-focus); }
154
+
155
+
156
+ /* TERTIARY — soft neutral fill, borderless (quieter than secondary) */
157
+ .btn-tertiary {
158
+ background: color-mix(in srgb, var(--color-foreground-default) 7%, transparent);
159
+ color: var(--color-foreground-default);
160
+ border-color: transparent;
161
+ }
162
+ .btn-tertiary:hover { background: color-mix(in srgb, var(--color-foreground-default) 12%, transparent); }
163
+ .btn-tertiary:active { background: color-mix(in srgb, var(--color-foreground-default) 16%, transparent); }
164
+ .btn-tertiary:focus-visible { box-shadow: var(--shadow-focus); }
165
+
166
+
167
+ /* GHOST — no chrome until hover */
168
+ .btn-ghost {
169
+ background: transparent;
170
+ color: var(--color-foreground-secondary);
171
+ }
172
+ .btn-ghost:hover { background: var(--color-background-hover); color: var(--color-foreground-default); }
173
+ .btn-ghost:active { background: var(--color-background-press); }
174
+ .btn-ghost:focus-visible { box-shadow: var(--shadow-focus); }
175
+
176
+
177
+ /* LINK — looks like inline text link */
178
+ .btn-link {
179
+ background: transparent;
180
+ color: var(--color-foreground-highlight);
181
+ height: auto;
182
+ padding: 0;
183
+ border: 0;
184
+ border-radius: var(--radius-2xs);
185
+ text-decoration: none;
186
+ }
187
+ .btn-link:hover { color: var(--color-brand-text); text-decoration: underline; text-underline-offset: 2px; }
188
+ .btn-link:active { color: var(--color-brand-text-strong); }
189
+ .btn-link:focus-visible { box-shadow: var(--shadow-focus); }
190
+
191
+
192
+ /* DANGER — destructive solid */
193
+ .btn-danger {
194
+ background: var(--color-danger-500);
195
+ color: var(--color-foreground-on-solid);
196
+ }
197
+ .btn-danger:hover { background: var(--color-danger-700); }
198
+ .btn-danger:active { background: var(--color-danger-900); }
199
+ .btn-danger:focus-visible { box-shadow: var(--shadow-focus-danger); }
200
+
201
+
202
+ /* DANGER OUTLINE — destructive contextual */
203
+ .btn-danger-outline {
204
+ background: var(--color-background-surface);
205
+ color: var(--color-danger-500);
206
+ border-color: var(--color-feedback-danger-border);
207
+ }
208
+ .btn-danger-outline:hover { background: var(--color-feedback-danger-background); border-color: var(--color-danger-500); }
209
+ .btn-danger-outline:active { background: color-mix(in srgb, var(--color-feedback-danger-background) 85%, var(--color-danger-500)); }
210
+ .btn-danger-outline:focus-visible { box-shadow: var(--shadow-focus-danger); }
211
+
212
+
213
+ /* SUCCESS — strong positive (rare) */
214
+ .btn-success {
215
+ background: var(--color-success-500);
216
+ color: var(--color-foreground-on-solid);
217
+ }
218
+ .btn-success:hover { background: var(--color-success-700); }
219
+ .btn-success:active { background: var(--color-success-900); }
220
+ .btn-success:focus-visible { box-shadow: var(--shadow-focus-success); }
221
+
222
+
223
+ /* INVERSE — white pill on dark/brand surfaces */
224
+ .btn-inverse {
225
+ background: var(--color-pure-white);
226
+ color: var(--color-brand-text);
227
+ }
228
+ .btn-inverse:hover { background: var(--color-neutral-100); color: var(--color-brand-text-strong); }
229
+ .btn-inverse:active { background: var(--color-neutral-150); }
230
+ .btn-inverse:focus-visible { box-shadow: var(--shadow-focus-sky); }
231
+
232
+
233
+ /* ----------------------------------------------------------------
234
+ 5. ON-INVERSE GHOSTS — for use inside .inverse / dark surfaces
235
+ Apply BOTH .btn-ghost AND .on-inverse
236
+ ---------------------------------------------------------------- */
237
+ .btn-ghost.on-inverse {
238
+ color: var(--color-foreground-inverse-secondary);
239
+ }
240
+ .btn-ghost.on-inverse:hover { background: var(--color-background-inverse-hover); color: var(--color-foreground-inverse-default); }
241
+ .btn-ghost.on-inverse:active { background: var(--color-background-inverse-press); }
242
+
243
+ /* Outline ghost on inverse — pairs with .btn-secondary semantics */
244
+ .btn-secondary.on-inverse {
245
+ background: transparent;
246
+ color: var(--color-foreground-inverse-default);
247
+ border-color: var(--color-border-inverse-strong);
248
+ }
249
+ .btn-secondary.on-inverse:hover { background: var(--color-background-inverse-hover); border-color: var(--color-foreground-inverse-secondary); }
250
+ .btn-secondary.on-inverse:active { background: var(--color-background-inverse-press); }
251
+
252
+
253
+ /* ----------------------------------------------------------------
254
+ 6. LOADING STATE
255
+ Add .is-loading; keeps current width but hides label/icons,
256
+ shows a centered spinner.
257
+ ---------------------------------------------------------------- */
258
+ .btn.is-loading {
259
+ position: relative;
260
+ color: transparent !important;
261
+ pointer-events: none;
262
+ }
263
+ .btn.is-loading > * { visibility: hidden; }
264
+ .btn.is-loading::after {
265
+ content: "";
266
+ position: absolute;
267
+ top: 50%; left: 50%;
268
+ width: var(--icon-sm);
269
+ height: var(--icon-sm);
270
+ margin: calc(var(--icon-sm) / -2) 0 0 calc(var(--icon-sm) / -2);
271
+ border: 2px solid currentColor;
272
+ border-top-color: transparent;
273
+ border-radius: var(--radius-pill);
274
+ animation: btn-spin var(--duration-slower) linear infinite;
275
+ /* Re-introduce currentColor where the variant set it; ::after inherits the parent's color BEFORE the !important override */
276
+ color: var(--color-foreground-default);
277
+ }
278
+ .btn-primary.is-loading::after,
279
+ .btn-danger.is-loading::after,
280
+ .btn-success.is-loading::after { color: var(--color-pure-white); }
281
+ .btn-tertiary.is-loading::after { color: var(--color-foreground-default); }
282
+
283
+ @keyframes btn-spin {
284
+ to { transform: rotate(360deg); }
285
+ }
286
+
287
+
288
+ /* ----------------------------------------------------------------
289
+ 7. BUTTON GROUP — segmented / toolbar
290
+ ---------------------------------------------------------------- */
291
+ .btn-group {
292
+ display: inline-flex;
293
+ }
294
+ .btn-group .btn {
295
+ border-radius: 0;
296
+ }
297
+ .btn-group .btn:first-child { border-top-left-radius: var(--radius-pill); border-bottom-left-radius: var(--radius-pill); }
298
+ .btn-group .btn:last-child { border-top-right-radius: var(--radius-pill); border-bottom-right-radius: var(--radius-pill); }
299
+ .btn-group .btn + .btn { margin-left: -1px; }
300
+ .btn-group .btn:focus-visible,
301
+ .btn-group .btn:hover { z-index: 1; }