gbs-add-block 2.1.0 → 2.3.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 (129) hide show
  1. package/.gbs/skills/gbs-components/SKILL.md +190 -134
  2. package/.gbs/skills/gbs-components/references/install.md +25 -3
  3. package/.gbs/skills/gbs-components/references/styling.md +246 -207
  4. package/CHANGELOG.md +74 -0
  5. package/README.md +154 -23
  6. package/index.cjs +212 -3
  7. package/package.json +41 -10
  8. package/schema/passport-v1.schema.json +204 -0
  9. package/source/beta-components/accordion/passport.json +259 -0
  10. package/source/beta-components/accordion/styles.css +207 -208
  11. package/source/beta-components/alert/passport.json +250 -0
  12. package/source/beta-components/alert/styles.css +154 -155
  13. package/source/beta-components/avatar/passport.json +294 -0
  14. package/source/beta-components/avatar/styles.css +225 -226
  15. package/source/beta-components/badge/passport.json +332 -0
  16. package/source/beta-components/badge/styles.css +203 -204
  17. package/source/beta-components/breadcrumb/passport.json +243 -0
  18. package/source/beta-components/breadcrumb/styles.css +138 -140
  19. package/source/beta-components/button/passport.json +402 -0
  20. package/source/beta-components/button/passport.manual.json +31 -0
  21. package/source/beta-components/button/styles.css +232 -234
  22. package/source/beta-components/card/passport.json +337 -0
  23. package/source/beta-components/card/styles.css +230 -232
  24. package/source/beta-components/checkbox/passport.json +456 -0
  25. package/source/beta-components/checkbox/styles.css +211 -213
  26. package/source/beta-components/combobox/passport.json +456 -0
  27. package/source/beta-components/combobox/styles.css +419 -417
  28. package/source/beta-components/data-grid/agent/coerce.ts +368 -0
  29. package/source/beta-components/data-grid/agent/contract.ts +410 -0
  30. package/source/beta-components/data-grid/agent/dataset.ts +92 -0
  31. package/source/beta-components/data-grid/agent/engine.ts +470 -0
  32. package/source/beta-components/data-grid/agent/executors.ts +155 -0
  33. package/source/beta-components/data-grid/agent/index.ts +79 -0
  34. package/source/beta-components/data-grid/agent/intent.ts +324 -0
  35. package/source/beta-components/data-grid/agent/operations.ts +335 -0
  36. package/source/beta-components/data-grid/agent/validate.ts +630 -0
  37. package/source/beta-components/data-grid/agent/webmcp.ts +107 -0
  38. package/source/beta-components/data-grid/index.ts +14 -7
  39. package/source/beta-components/data-grid/passport.json +1051 -0
  40. package/source/beta-components/data-grid/passport.manual.json +255 -0
  41. package/source/beta-components/data-grid/react/AskGrid.tsx +164 -0
  42. package/source/beta-components/data-grid/react/DataGrid.tsx +39 -0
  43. package/source/beta-components/data-grid/styles.css +874 -716
  44. package/source/beta-components/date-picker/passport.json +407 -0
  45. package/source/beta-components/date-picker/styles.css +445 -446
  46. package/source/beta-components/dialog/passport.json +344 -0
  47. package/source/beta-components/dialog/styles.css +280 -279
  48. package/source/beta-components/file-uploader/passport.json +518 -0
  49. package/source/beta-components/file-uploader/styles.css +394 -396
  50. package/source/beta-components/input/passport.json +536 -0
  51. package/source/beta-components/input/styles.css +295 -297
  52. package/source/beta-components/menu/passport.json +322 -0
  53. package/source/beta-components/menu/styles.css +224 -223
  54. package/source/beta-components/modal/passport.json +289 -0
  55. package/source/beta-components/modal/styles.css +241 -240
  56. package/source/beta-components/number-input/passport.json +541 -0
  57. package/source/beta-components/number-input/styles.css +230 -231
  58. package/source/beta-components/popover/passport.json +238 -0
  59. package/source/beta-components/popover/styles.css +148 -147
  60. package/source/beta-components/progress/passport.json +270 -0
  61. package/source/beta-components/progress/styles.css +200 -201
  62. package/source/beta-components/radio-group/passport.json +477 -0
  63. package/source/beta-components/radio-group/styles.css +269 -270
  64. package/source/beta-components/shared/core/agent/adapter.ts +65 -0
  65. package/source/beta-components/shared/core/agent/history.ts +120 -0
  66. package/source/beta-components/shared/core/agent/index.ts +46 -0
  67. package/source/beta-components/shared/core/agent/numbers.ts +217 -0
  68. package/source/beta-components/shared/core/agent/schema.ts +180 -0
  69. package/source/beta-components/shared/core/agent/types.ts +169 -0
  70. package/source/beta-components/shared/core/agent/webmcp.ts +328 -0
  71. package/source/beta-components/shared/index.ts +9 -0
  72. package/source/beta-components/shared/react/GramproAIProvider.tsx +50 -0
  73. package/source/beta-components/shared/react/useAskAgent.ts +217 -0
  74. package/source/beta-components/shared/styles.css +79 -0
  75. package/source/beta-components/shared/version.json +4 -4
  76. package/source/beta-components/shared/version.ts +6 -6
  77. package/source/beta-components/skeleton/passport.json +251 -0
  78. package/source/beta-components/skeleton/styles.css +185 -187
  79. package/source/beta-components/spinner/passport.json +245 -0
  80. package/source/beta-components/spinner/styles.css +173 -174
  81. package/source/beta-components/switch/passport.json +421 -0
  82. package/source/beta-components/switch/styles.css +227 -229
  83. package/source/beta-components/tabs/passport.json +315 -0
  84. package/source/beta-components/tabs/styles.css +263 -264
  85. package/source/beta-components/textarea/passport.json +382 -0
  86. package/source/beta-components/textarea/styles.css +158 -160
  87. package/source/beta-components/toaster/passport.json +221 -0
  88. package/source/beta-components/toaster/styles.css +282 -282
  89. package/source/beta-components/tooltip/passport.json +170 -0
  90. package/source/beta-components/tooltip/styles.css +71 -73
  91. package/tools/env.cjs +61 -0
  92. package/tools/passport/cli.cjs +79 -0
  93. package/tools/passport/extract.cjs +493 -0
  94. package/tools/passport/index.cjs +185 -0
  95. package/tools/passport/merge.cjs +131 -0
  96. package/tools/passport/policy.cjs +65 -0
  97. package/tools/passport/validate.cjs +277 -0
  98. package/tools/ts-require.cjs +79 -0
  99. package/source/beta-components/accordion/__tests__/core.test.ts +0 -58
  100. package/source/beta-components/alert/__tests__/core.test.ts +0 -17
  101. package/source/beta-components/avatar/__tests__/core.test.ts +0 -88
  102. package/source/beta-components/badge/__tests__/core.test.ts +0 -46
  103. package/source/beta-components/breadcrumb/__tests__/core.test.ts +0 -58
  104. package/source/beta-components/button/__tests__/core.test.ts +0 -31
  105. package/source/beta-components/card/__tests__/core.test.ts +0 -57
  106. package/source/beta-components/checkbox/__tests__/core.test.ts +0 -40
  107. package/source/beta-components/combobox/__tests__/core.test.ts +0 -134
  108. package/source/beta-components/data-grid/__tests__/core.test.ts +0 -356
  109. package/source/beta-components/data-grid/__tests__/export.test.ts +0 -70
  110. package/source/beta-components/data-grid/__tests__/pdf.test.ts +0 -209
  111. package/source/beta-components/date-picker/__tests__/core.test.ts +0 -273
  112. package/source/beta-components/dialog/__tests__/core.test.ts +0 -86
  113. package/source/beta-components/file-uploader/__tests__/core.test.ts +0 -395
  114. package/source/beta-components/input/__tests__/core.test.ts +0 -75
  115. package/source/beta-components/menu/__tests__/core.test.ts +0 -120
  116. package/source/beta-components/modal/__tests__/core.test.ts +0 -55
  117. package/source/beta-components/number-input/__tests__/core.test.ts +0 -151
  118. package/source/beta-components/progress/__tests__/core.test.ts +0 -56
  119. package/source/beta-components/radio-group/__tests__/core.test.ts +0 -64
  120. package/source/beta-components/shared/__tests__/boundaries.test.ts +0 -95
  121. package/source/beta-components/shared/__tests__/core.test.ts +0 -55
  122. package/source/beta-components/shared/__tests__/position.test.ts +0 -143
  123. package/source/beta-components/skeleton/__tests__/core.test.ts +0 -41
  124. package/source/beta-components/spinner/__tests__/core.test.ts +0 -48
  125. package/source/beta-components/switch/__tests__/core.test.ts +0 -64
  126. package/source/beta-components/tabs/__tests__/core.test.ts +0 -51
  127. package/source/beta-components/textarea/__tests__/core.test.ts +0 -38
  128. package/source/beta-components/toaster/__tests__/core.test.ts +0 -256
  129. package/source/beta-components/tooltip/__tests__/core.test.ts +0 -42
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "../../../schema/passport-v1.schema.json",
3
+ "component": "Button",
4
+ "purpose": "Trigger an action. Not for navigation unless `render` supplies a link element.",
5
+ "intentDomains": ["ActionIntent"],
6
+ "accessibility": {
7
+ "supplied": [
8
+ "native <button> semantics, including Enter and Space activation",
9
+ "disabled state via the native attribute",
10
+ "focus-visible ring",
11
+ "the spinner is aria-hidden; loadingText is announced"
12
+ ],
13
+ "required": [
14
+ "aria-label when the button is icon-only (`icon` with no children)",
15
+ "type=\"submit\" on the button that submits a form; the default is \"button\""
16
+ ]
17
+ },
18
+ "composition": {
19
+ "mustNotContain": ["Button", "a"],
20
+ "notes": "Nesting interactive elements is invalid HTML and traps keyboard focus. Use `render` to change the element instead of wrapping it."
21
+ },
22
+ "safeMutations": {
23
+ "allowed": ["variant", "size", "fullWidth", "leading", "trailing", "icon", "loadingText"],
24
+ "forbidden": ["type", "onClick", "render", "loading", "disabled"],
25
+ "rationale": "Appearance is safe for an agent to change. `type` and `onClick` change behaviour; `loading` and `disabled` are owned by the component's own async handling."
26
+ },
27
+ "propNotes": {
28
+ "render": "Escape hatch for router links. Changes the rendered element, so composition rules apply to whatever it returns.",
29
+ "loading": "Managed automatically when `onClick` returns a promise. Setting it manually opts out of that."
30
+ }
31
+ }
@@ -1,234 +1,232 @@
1
- /*
2
- * Button styles.
3
- *
4
- * Tokens read the shared --gbs-* variables when they are set on an ancestor
5
- * (set them on :root to theme every component at once) and fall back to the
6
- * same default palette otherwise. Rules live in the `gbs` layer. Declare
7
- * where that sits in your global CSS — e.g. `@layer reset, gbs, app,
8
- * utilities;` — and anything in a later layer, Tailwind utilities included,
9
- * overrides them.
10
- */
11
-
12
- @layer gbs {
13
- .bt-root {
14
- --bt-bg: var(--gbs-bg, var(--dg-bg, light-dark(#ffffff, #0b0b0e)));
15
- --bt-fg: var(--gbs-fg, var(--dg-fg, light-dark(#18181b, #f4f4f5)));
16
- --bt-border: var(--gbs-border, var(--dg-border, light-dark(#e4e4e7, #27272a)));
17
- --bt-hover: var(--gbs-hover, var(--dg-hover, light-dark(#f4f4f5, #1f1f23)));
18
- --bt-subtle: var(--gbs-subtle, light-dark(#f4f4f5, #1c1c20));
19
- --bt-accent: var(--gbs-accent, var(--dg-accent, light-dark(#2563eb, #60a5fa)));
20
- --bt-accent-fg: var(--gbs-accent-fg, var(--dg-accent-fg, light-dark(#ffffff, #0b1220)));
21
- --bt-danger: var(--gbs-danger, var(--dg-danger, light-dark(#dc2626, #f87171)));
22
- --bt-danger-fg: var(--gbs-danger-fg, light-dark(#ffffff, #1c0606));
23
- --bt-focus: var(--gbs-focus, var(--dg-focus, light-dark(#2563eb, #60a5fa)));
24
- --bt-radius: var(--gbs-radius, var(--dg-radius, 8px));
25
- --bt-font-size: var(--gbs-font-size, var(--dg-font-size, 13px));
26
- --bt-height: 36px;
27
- --bt-px: 14px;
28
- --bt-gap: 8px;
29
-
30
- /* Per variant. */
31
- --bt-fill: var(--bt-accent);
32
- --bt-fill-hover: color-mix(in oklab, var(--bt-accent), var(--bt-fg) 12%);
33
- --bt-ink: var(--bt-accent-fg);
34
- --bt-line: var(--bt-accent);
35
-
36
- color-scheme: inherit;
37
- box-sizing: border-box;
38
- position: relative;
39
- display: inline-grid;
40
- place-items: center;
41
- height: var(--bt-height);
42
- padding: 0 var(--bt-px);
43
- border: 1px solid var(--bt-line);
44
- border-radius: var(--bt-radius);
45
- background: var(--bt-fill);
46
- color: var(--bt-ink);
47
- font: inherit;
48
- font-size: var(--bt-font-size);
49
- font-weight: 500;
50
- line-height: 1;
51
- white-space: nowrap;
52
- text-decoration: none;
53
- vertical-align: middle;
54
- cursor: pointer;
55
- user-select: none;
56
- -webkit-tap-highlight-color: transparent;
57
- transition:
58
- background-color 120ms ease,
59
- border-color 120ms ease,
60
- color 120ms ease,
61
- scale 120ms ease;
62
- }
63
-
64
- :where(.dark, [data-theme="dark"]) .bt-root {
65
- color-scheme: dark;
66
- }
67
- :where(.light, [data-theme="light"]) .bt-root {
68
- color-scheme: light;
69
- }
70
-
71
- :where(.bt-root) *,
72
- :where(.bt-root) *::before,
73
- :where(.bt-root) *::after {
74
- box-sizing: border-box;
75
- }
76
-
77
- /* ------------------------------------------------------------ variants */
78
-
79
- .bt-root[data-variant="secondary"] {
80
- --bt-fill: var(--bt-subtle);
81
- --bt-fill-hover: color-mix(in oklab, var(--bt-subtle), var(--bt-fg) 8%);
82
- --bt-ink: var(--bt-fg);
83
- --bt-line: transparent;
84
- }
85
- .bt-root[data-variant="outline"] {
86
- --bt-fill: var(--bt-bg);
87
- --bt-fill-hover: var(--bt-hover);
88
- --bt-ink: var(--bt-fg);
89
- --bt-line: var(--bt-border);
90
- }
91
- .bt-root[data-variant="ghost"] {
92
- --bt-fill: transparent;
93
- --bt-fill-hover: var(--bt-hover);
94
- --bt-ink: var(--bt-fg);
95
- --bt-line: transparent;
96
- }
97
- .bt-root[data-variant="danger"] {
98
- --bt-fill: var(--bt-danger);
99
- --bt-fill-hover: color-mix(in oklab, var(--bt-danger), var(--bt-fg) 12%);
100
- --bt-ink: var(--bt-danger-fg);
101
- --bt-line: var(--bt-danger);
102
- }
103
- .bt-root[data-variant="link"] {
104
- --bt-fill: transparent;
105
- --bt-fill-hover: transparent;
106
- --bt-ink: var(--bt-accent);
107
- --bt-line: transparent;
108
- --bt-px: 0px;
109
- height: auto;
110
- text-underline-offset: 3px;
111
- }
112
- .bt-root[data-variant="link"]:hover:not([aria-disabled="true"], :disabled) {
113
- text-decoration: underline;
114
- }
115
-
116
- /* --------------------------------------------------------------- sizes */
117
-
118
- .bt-root[data-size="sm"] {
119
- --bt-height: 30px;
120
- --bt-px: 10px;
121
- --bt-gap: 6px;
122
- --bt-font-size: 12px;
123
- }
124
- .bt-root[data-size="lg"] {
125
- --bt-height: 44px;
126
- --bt-px: 18px;
127
- --bt-gap: 10px;
128
- --bt-font-size: 14px;
129
- }
130
-
131
- /* -------------------------------------------------------------- states */
132
-
133
- .bt-root:hover:not([aria-disabled="true"], :disabled) {
134
- background: var(--bt-fill-hover);
135
- }
136
- .bt-root:active:not([aria-disabled="true"], :disabled) {
137
- scale: 0.98;
138
- }
139
- .bt-root:focus-visible {
140
- outline: 2px solid var(--bt-focus);
141
- outline-offset: 2px;
142
- }
143
- .bt-root:disabled,
144
- .bt-root[aria-disabled="true"]:not([data-busy]) {
145
- opacity: 0.5;
146
- cursor: not-allowed;
147
- }
148
- .bt-root[data-busy] {
149
- cursor: progress;
150
- }
151
-
152
- .bt-root[data-icon-only] {
153
- width: var(--bt-height);
154
- padding: 0;
155
- }
156
- .bt-root[data-full-width] {
157
- width: 100%;
158
- }
159
-
160
- /* ------------------------------------------------------------- content */
161
-
162
- .bt-content,
163
- .bt-spinner {
164
- grid-area: 1 / 1;
165
- display: inline-flex;
166
- align-items: center;
167
- justify-content: center;
168
- gap: var(--bt-gap);
169
- min-width: 0;
170
- }
171
- /* The label keeps its space while the spinner sits on top… */
172
- .bt-content[data-hidden] {
173
- visibility: hidden;
174
- }
175
- /* …unless loading text replaces it. */
176
- .bt-root[data-loading-text] .bt-content {
177
- display: none;
178
- }
179
-
180
- .bt-icon {
181
- display: inline-flex;
182
- flex: none;
183
- }
184
- .bt-icon > svg {
185
- width: 1.15em;
186
- height: 1.15em;
187
- }
188
-
189
- .bt-spinner-ring {
190
- width: 1.15em;
191
- height: 1.15em;
192
- fill: none;
193
- stroke: currentColor;
194
- stroke-width: 2.5;
195
- stroke-linecap: round;
196
- stroke-dasharray: 30 100;
197
- animation: bt-rotate 0.8s linear infinite;
198
- }
199
- @keyframes bt-rotate {
200
- to {
201
- transform: rotate(360deg);
202
- }
203
- }
204
-
205
- .bt-sr-only {
206
- position: absolute;
207
- width: 1px;
208
- height: 1px;
209
- margin: -1px;
210
- padding: 0;
211
- overflow: hidden;
212
- clip-path: inset(50%);
213
- white-space: nowrap;
214
- border: 0;
215
- }
216
-
217
- @media (prefers-reduced-motion: reduce) {
218
- .bt-root {
219
- transition: none;
220
- }
221
- .bt-root:active:not([aria-disabled="true"], :disabled) {
222
- scale: none;
223
- }
224
- .bt-spinner-ring {
225
- animation-duration: 2.4s;
226
- }
227
- }
228
-
229
- @media (forced-colors: active) {
230
- .bt-root {
231
- border-color: ButtonText;
232
- }
233
- }
234
- }
1
+ /*
2
+ * Button styles.
3
+ *
4
+ * Tokens read the shared --gbs-* variables when they are set on an ancestor
5
+ * (set them on :root to theme every component at once) and fall back to the
6
+ * same default palette otherwise. Rules are unlayered: they beat a framework
7
+ * reset on specificity, and lose to anything you pass through `className`.
8
+ * With Tailwind v4, import this file after the framework and inside its
9
+ * components layer - see the README.
10
+ */
11
+
12
+ .bt-root {
13
+ --bt-bg: var(--gbs-bg, var(--dg-bg, light-dark(#ffffff, #0b0b0e)));
14
+ --bt-fg: var(--gbs-fg, var(--dg-fg, light-dark(#18181b, #f4f4f5)));
15
+ --bt-border: var(--gbs-border, var(--dg-border, light-dark(#e4e4e7, #27272a)));
16
+ --bt-hover: var(--gbs-hover, var(--dg-hover, light-dark(#f4f4f5, #1f1f23)));
17
+ --bt-subtle: var(--gbs-subtle, light-dark(#f4f4f5, #1c1c20));
18
+ --bt-accent: var(--gbs-accent, var(--dg-accent, light-dark(#2563eb, #60a5fa)));
19
+ --bt-accent-fg: var(--gbs-accent-fg, var(--dg-accent-fg, light-dark(#ffffff, #0b1220)));
20
+ --bt-danger: var(--gbs-danger, var(--dg-danger, light-dark(#dc2626, #f87171)));
21
+ --bt-danger-fg: var(--gbs-danger-fg, light-dark(#ffffff, #1c0606));
22
+ --bt-focus: var(--gbs-focus, var(--dg-focus, light-dark(#2563eb, #60a5fa)));
23
+ --bt-radius: var(--gbs-radius, var(--dg-radius, 8px));
24
+ --bt-font-size: var(--gbs-font-size, var(--dg-font-size, 13px));
25
+ --bt-height: 36px;
26
+ --bt-px: 14px;
27
+ --bt-gap: 8px;
28
+
29
+ /* Per variant. */
30
+ --bt-fill: var(--bt-accent);
31
+ --bt-fill-hover: color-mix(in oklab, var(--bt-accent), var(--bt-fg) 12%);
32
+ --bt-ink: var(--bt-accent-fg);
33
+ --bt-line: var(--bt-accent);
34
+
35
+ color-scheme: inherit;
36
+ box-sizing: border-box;
37
+ position: relative;
38
+ display: inline-grid;
39
+ place-items: center;
40
+ height: var(--bt-height);
41
+ padding: 0 var(--bt-px);
42
+ border: 1px solid var(--bt-line);
43
+ border-radius: var(--bt-radius);
44
+ background: var(--bt-fill);
45
+ color: var(--bt-ink);
46
+ font: inherit;
47
+ font-size: var(--bt-font-size);
48
+ font-weight: 500;
49
+ line-height: 1;
50
+ white-space: nowrap;
51
+ text-decoration: none;
52
+ vertical-align: middle;
53
+ cursor: pointer;
54
+ user-select: none;
55
+ -webkit-tap-highlight-color: transparent;
56
+ transition:
57
+ background-color 120ms ease,
58
+ border-color 120ms ease,
59
+ color 120ms ease,
60
+ scale 120ms ease;
61
+ }
62
+
63
+ :where(.dark, [data-theme="dark"]) .bt-root {
64
+ color-scheme: dark;
65
+ }
66
+ :where(.light, [data-theme="light"]) .bt-root {
67
+ color-scheme: light;
68
+ }
69
+
70
+ :where(.bt-root) *,
71
+ :where(.bt-root) *::before,
72
+ :where(.bt-root) *::after {
73
+ box-sizing: border-box;
74
+ }
75
+
76
+ /* ------------------------------------------------------------ variants */
77
+
78
+ .bt-root[data-variant="secondary"] {
79
+ --bt-fill: var(--bt-subtle);
80
+ --bt-fill-hover: color-mix(in oklab, var(--bt-subtle), var(--bt-fg) 8%);
81
+ --bt-ink: var(--bt-fg);
82
+ --bt-line: transparent;
83
+ }
84
+ .bt-root[data-variant="outline"] {
85
+ --bt-fill: var(--bt-bg);
86
+ --bt-fill-hover: var(--bt-hover);
87
+ --bt-ink: var(--bt-fg);
88
+ --bt-line: var(--bt-border);
89
+ }
90
+ .bt-root[data-variant="ghost"] {
91
+ --bt-fill: transparent;
92
+ --bt-fill-hover: var(--bt-hover);
93
+ --bt-ink: var(--bt-fg);
94
+ --bt-line: transparent;
95
+ }
96
+ .bt-root[data-variant="danger"] {
97
+ --bt-fill: var(--bt-danger);
98
+ --bt-fill-hover: color-mix(in oklab, var(--bt-danger), var(--bt-fg) 12%);
99
+ --bt-ink: var(--bt-danger-fg);
100
+ --bt-line: var(--bt-danger);
101
+ }
102
+ .bt-root[data-variant="link"] {
103
+ --bt-fill: transparent;
104
+ --bt-fill-hover: transparent;
105
+ --bt-ink: var(--bt-accent);
106
+ --bt-line: transparent;
107
+ --bt-px: 0px;
108
+ height: auto;
109
+ text-underline-offset: 3px;
110
+ }
111
+ .bt-root[data-variant="link"]:hover:not([aria-disabled="true"], :disabled) {
112
+ text-decoration: underline;
113
+ }
114
+
115
+ /* --------------------------------------------------------------- sizes */
116
+
117
+ .bt-root[data-size="sm"] {
118
+ --bt-height: 30px;
119
+ --bt-px: 10px;
120
+ --bt-gap: 6px;
121
+ --bt-font-size: 12px;
122
+ }
123
+ .bt-root[data-size="lg"] {
124
+ --bt-height: 44px;
125
+ --bt-px: 18px;
126
+ --bt-gap: 10px;
127
+ --bt-font-size: 14px;
128
+ }
129
+
130
+ /* -------------------------------------------------------------- states */
131
+
132
+ .bt-root:hover:not([aria-disabled="true"], :disabled) {
133
+ background: var(--bt-fill-hover);
134
+ }
135
+ .bt-root:active:not([aria-disabled="true"], :disabled) {
136
+ scale: 0.98;
137
+ }
138
+ .bt-root:focus-visible {
139
+ outline: 2px solid var(--bt-focus);
140
+ outline-offset: 2px;
141
+ }
142
+ .bt-root:disabled,
143
+ .bt-root[aria-disabled="true"]:not([data-busy]) {
144
+ opacity: 0.5;
145
+ cursor: not-allowed;
146
+ }
147
+ .bt-root[data-busy] {
148
+ cursor: progress;
149
+ }
150
+
151
+ .bt-root[data-icon-only] {
152
+ width: var(--bt-height);
153
+ padding: 0;
154
+ }
155
+ .bt-root[data-full-width] {
156
+ width: 100%;
157
+ }
158
+
159
+ /* ------------------------------------------------------------- content */
160
+
161
+ .bt-content,
162
+ .bt-spinner {
163
+ grid-area: 1 / 1;
164
+ display: inline-flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ gap: var(--bt-gap);
168
+ min-width: 0;
169
+ }
170
+ /* The label keeps its space while the spinner sits on top… */
171
+ .bt-content[data-hidden] {
172
+ visibility: hidden;
173
+ }
174
+ /* …unless loading text replaces it. */
175
+ .bt-root[data-loading-text] .bt-content {
176
+ display: none;
177
+ }
178
+
179
+ .bt-icon {
180
+ display: inline-flex;
181
+ flex: none;
182
+ }
183
+ .bt-icon > svg {
184
+ width: 1.15em;
185
+ height: 1.15em;
186
+ }
187
+
188
+ .bt-spinner-ring {
189
+ width: 1.15em;
190
+ height: 1.15em;
191
+ fill: none;
192
+ stroke: currentColor;
193
+ stroke-width: 2.5;
194
+ stroke-linecap: round;
195
+ stroke-dasharray: 30 100;
196
+ animation: bt-rotate 0.8s linear infinite;
197
+ }
198
+ @keyframes bt-rotate {
199
+ to {
200
+ transform: rotate(360deg);
201
+ }
202
+ }
203
+
204
+ .bt-sr-only {
205
+ position: absolute;
206
+ width: 1px;
207
+ height: 1px;
208
+ margin: -1px;
209
+ padding: 0;
210
+ overflow: hidden;
211
+ clip-path: inset(50%);
212
+ white-space: nowrap;
213
+ border: 0;
214
+ }
215
+
216
+ @media (prefers-reduced-motion: reduce) {
217
+ .bt-root {
218
+ transition: none;
219
+ }
220
+ .bt-root:active:not([aria-disabled="true"], :disabled) {
221
+ scale: none;
222
+ }
223
+ .bt-spinner-ring {
224
+ animation-duration: 2.4s;
225
+ }
226
+ }
227
+
228
+ @media (forced-colors: active) {
229
+ .bt-root {
230
+ border-color: ButtonText;
231
+ }
232
+ }