@apliteni/apliteni-ui 0.62.0 → 0.64.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.
package/README.md CHANGED
@@ -170,6 +170,9 @@ See Storybook's **Button / Busy Transition** for the live vanilla example.
170
170
 
171
171
  ## Theming
172
172
 
173
+ React forms can use [Checkbox, radio and Switch](react/README.md#checkbox-and-switch)
174
+ with native labels, keyboard behavior and controlled or uncontrolled state.
175
+
173
176
  React pages can use [ThemeToggle and its pre-paint script](react/README.md#themetoggle)
174
177
  to save dark, light or auto choices. Auto resolves to dark or light on the root.
175
178
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.62.0",
3
+ "version": "0.64.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
package/react/README.md CHANGED
@@ -29,7 +29,7 @@ import '@apliteni/apliteni-ui/react/css'; // React components' shell styles (mo
29
29
  import { DataTable, Modal, Button } from '@apliteni/apliteni-ui/react';
30
30
  ```
31
31
 
32
- Components: `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
32
+ Components: `Checkbox`, `Switch`, `EmptyState`, `DataTable`, `Pagination`, `StatBand`, `Modal`, `Confirm`, `Drawer`, `CommandPalette`, `Dropdown`, `BackLink`, `Tooltip`, `Snippet`, `Tabs`, `Button`, `Badge`, `Pill`, `StatusDot`, `Card`, `Callout`, `Icon`.
33
33
 
34
34
  `Pill` renders metadata with the existing pill spacing. Omit `variant` for neutral
35
35
  metadata or use `live` or `soon`. Its children accept React content.
@@ -69,6 +69,20 @@ for a small inline control: `<Button size="xs" variant="ghost" icon="copy" iconO
69
69
  Its glyph is 13px and its icon-only target is 24×24px; labelled xs buttons use
70
70
  `--text-xs`. The other sizes keep 16px glyphs.
71
71
 
72
+ `Button href="/reports"` renders a native link and forwards an anchor ref. Without
73
+ `href`, it remains a native button. `leading={<VendorMark />}` puts decorative
74
+ caller artwork before the label and takes precedence over `icon`; artwork must
75
+ contain no focusable elements. Use visible children or an explicit accessible name.
76
+ Disabled links leave the tab order; busy links keep focus. Both remove their href
77
+ and block activation until enabled. Anchor attributes such as `target`, `rel`,
78
+ and `download` pass through. The kit does not supply vendor artwork.
79
+
80
+ A `className` you pass is added to the kit's classes rather than swapped for them,
81
+ and the component's own `aria-busy`, `aria-disabled` and `data-btn-*` attributes win
82
+ over anything you spread in, so a busy control cannot be made to read as idle. An
83
+ icon-only control with no children, no `icon` and no label ships nameless on purpose:
84
+ the gap then shows up in an accessibility check instead of reading as satisfied.
85
+
72
86
  `CommandPalette` renders the kit's `commandPalette()` markup, class for class, and imports the
73
87
  kit's ranking rather than repeating it — so a palette a server rendered and the same palette
74
88
  after a keystroke put the same row first. Two differences from the vanilla one follow from
@@ -538,3 +552,27 @@ Tooltip accepts text and inline children, without nested controls. Hovering or f
538
552
  ```tsx
539
553
  <Tooltip text="Updated daily">Balance</Tooltip>
540
554
  ```
555
+
556
+ ## Checkbox and Switch
557
+
558
+ `Checkbox` wraps a native checkbox and its visible `label`. Set `type="radio"`
559
+ and give related options the same `name` for a native radio group. Use a fieldset
560
+ and legend to name the group. `Switch` has the same native checkbox behavior,
561
+ with the kit's switch track; its required `label` supplies the accessible name.
562
+ Place visible setting text beside a switch.
563
+
564
+ Both accept native input props: `checked` with `onChange` for controlled state,
565
+ `defaultChecked` for uncontrolled state, and `disabled`, `name`, `value`,
566
+ `required`, and ARIA attributes. Refs and every other prop reach the input.
567
+ `className` reaches the input on `Checkbox` and the `.ui-switch` label on
568
+ `Switch`, whose input is a hidden zero-size box that paints nothing. An empty
569
+ `Switch` label falls back to "Toggle", as the vanilla factory does. Disabled
570
+ controls do not activate, submit, or enter the Tab order, and the shared
571
+ stylesheet paints them with the kit's disabled ink, surface and edge and drops
572
+ the accent from a checked box.
573
+
574
+ ```tsx
575
+ <Checkbox label="Read only" type="radio" name="scope" value="read" defaultChecked />
576
+ <Checkbox label="Full access" type="radio" name="scope" value="full" />
577
+ <Switch label="Email notifications" checked={email} onChange={event => setEmail(event.currentTarget.checked)} />
578
+ ```
@@ -26,6 +26,260 @@
26
26
  }
27
27
  }
28
28
 
29
+ /* ../src/styles/input.css */
30
+ .ui-field {
31
+ display: block;
32
+ }
33
+ .ui-field__label {
34
+ display: block;
35
+ color: var(--strong);
36
+ font-size: var(--text-base);
37
+ font-weight: var(--weight-medium);
38
+ margin-bottom: 8px;
39
+ }
40
+ .ui-field__req {
41
+ color: var(--pink);
42
+ margin-left: 4px;
43
+ }
44
+ .ui-field__hint {
45
+ color: var(--text);
46
+ font-size: var(--text-sm);
47
+ margin-top: 7px;
48
+ line-height: 1.5;
49
+ }
50
+ .ui-field__error {
51
+ color: var(--pink);
52
+ font-size: var(--text-sm);
53
+ margin-top: 7px;
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 6px;
57
+ }
58
+ .ui-field__error svg {
59
+ width: 16px;
60
+ height: 16px;
61
+ flex: none;
62
+ stroke-width: 2.4;
63
+ }
64
+ .ui-input,
65
+ .ui-textarea,
66
+ .ui-select {
67
+ font-family: var(--font-sans);
68
+ font-size: var(--text-base);
69
+ color: var(--strong);
70
+ background: var(--field-bg);
71
+ border: 1px solid var(--field-edge);
72
+ border-radius: var(--radius-md);
73
+ padding: 12px 15px;
74
+ width: 100%;
75
+ transition: border-color var(--dur-med) var(--ease), box-shadow var(--dur-med) var(--ease);
76
+ }
77
+ .ui-textarea {
78
+ resize: vertical;
79
+ min-height: 96px;
80
+ line-height: 1.55;
81
+ }
82
+ .ui-input:hover,
83
+ .ui-textarea:hover,
84
+ .ui-select:hover {
85
+ border-color: var(--field-edge-hover);
86
+ }
87
+ .ui-input:focus-visible,
88
+ .ui-textarea:focus-visible,
89
+ .ui-select:focus-visible {
90
+ outline: 2px solid transparent;
91
+ border-color: var(--accent);
92
+ box-shadow: var(--ring);
93
+ }
94
+ .ui-input::placeholder,
95
+ .ui-textarea::placeholder {
96
+ color: var(--muted);
97
+ opacity: 0.9;
98
+ }
99
+ .ui-input:disabled,
100
+ .ui-select:disabled,
101
+ .ui-textarea:disabled {
102
+ background: var(--disabled-surface);
103
+ border-color: var(--disabled-border);
104
+ color: var(--disabled-ink);
105
+ cursor: not-allowed;
106
+ }
107
+ .ui-input.is-invalid,
108
+ .ui-select.is-invalid,
109
+ .ui-textarea.is-invalid {
110
+ border-color: var(--pink);
111
+ }
112
+ .ui-input[type=search]::-webkit-search-cancel-button {
113
+ appearance: none;
114
+ display: none;
115
+ }
116
+ .ui-input-group {
117
+ position: relative;
118
+ display: flex;
119
+ align-items: center;
120
+ }
121
+ .ui-input-group .ui-input {
122
+ padding-left: 42px;
123
+ }
124
+ .ui-input-group__icon {
125
+ position: absolute;
126
+ left: 14px;
127
+ width: 17px;
128
+ height: 17px;
129
+ color: var(--muted);
130
+ pointer-events: none;
131
+ }
132
+ .ui-input-group__icon svg {
133
+ width: 100%;
134
+ height: 100%;
135
+ stroke: currentColor;
136
+ fill: none;
137
+ stroke-width: 2.2;
138
+ }
139
+ .ui-select {
140
+ appearance: none;
141
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a29db6' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
142
+ background-repeat: no-repeat;
143
+ background-position: right 15px center;
144
+ padding-right: 40px;
145
+ cursor: pointer;
146
+ }
147
+ :root[data-theme=light] .ui-select {
148
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235c6270' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
149
+ }
150
+ .ui-check {
151
+ display: inline-flex;
152
+ align-items: flex-start;
153
+ gap: 11px;
154
+ cursor: pointer;
155
+ font-size: var(--text-base);
156
+ color: var(--text);
157
+ line-height: 1.5;
158
+ }
159
+ .ui-check input {
160
+ appearance: none;
161
+ width: 19px;
162
+ height: 19px;
163
+ flex: none;
164
+ margin-top: 1px;
165
+ border: 1.5px solid var(--border-strong);
166
+ border-radius: var(--radius-xs);
167
+ background: var(--surface-2);
168
+ cursor: pointer;
169
+ transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
170
+ position: relative;
171
+ }
172
+ .ui-check input::before {
173
+ content: "";
174
+ position: absolute;
175
+ left: 50%;
176
+ top: 50%;
177
+ width: 24px;
178
+ height: 24px;
179
+ transform: translate(-50%, -50%);
180
+ }
181
+ .ui-check input[type=radio] {
182
+ border-radius: 50%;
183
+ }
184
+ .ui-check input:enabled:hover {
185
+ border-color: var(--accent);
186
+ }
187
+ .ui-check input:checked {
188
+ background: var(--accent);
189
+ border-color: var(--accent);
190
+ }
191
+ .ui-check input[type=checkbox]:checked::after {
192
+ content: "";
193
+ position: absolute;
194
+ left: 5px;
195
+ top: 1.5px;
196
+ width: 5px;
197
+ height: 10px;
198
+ border: solid var(--accent-contrast);
199
+ border-width: 0 2px 2px 0;
200
+ transform: rotate(45deg);
201
+ }
202
+ .ui-check input[type=radio]:checked::after {
203
+ content: "";
204
+ position: absolute;
205
+ inset: 4px;
206
+ border-radius: 50%;
207
+ background: var(--accent-contrast);
208
+ }
209
+ .ui-check input:focus-visible {
210
+ outline: 2px solid transparent;
211
+ box-shadow: var(--ring);
212
+ }
213
+ .ui-check input:disabled {
214
+ background: var(--disabled-surface);
215
+ border-color: var(--disabled-border);
216
+ cursor: not-allowed;
217
+ }
218
+ .ui-check input:disabled:checked {
219
+ background: var(--disabled-ink);
220
+ border-color: var(--disabled-ink);
221
+ }
222
+ .ui-check input[type=checkbox]:disabled:checked::after {
223
+ border-color: var(--disabled-surface);
224
+ }
225
+ .ui-check input[type=radio]:disabled:checked::after {
226
+ background: var(--disabled-surface);
227
+ }
228
+ .ui-check:has(input:disabled) {
229
+ color: var(--disabled-ink);
230
+ cursor: not-allowed;
231
+ }
232
+ .ui-switch {
233
+ --sw-w: 44px;
234
+ --sw-h: 26px;
235
+ position: relative;
236
+ display: inline-block;
237
+ width: var(--sw-w);
238
+ height: var(--sw-h);
239
+ flex: none;
240
+ }
241
+ .ui-switch input {
242
+ position: absolute;
243
+ opacity: 0;
244
+ width: 0;
245
+ height: 0;
246
+ }
247
+ .ui-switch__track {
248
+ position: absolute;
249
+ inset: 0;
250
+ background: var(--surface-3);
251
+ border-radius: var(--radius-pill);
252
+ cursor: pointer;
253
+ transition: background var(--dur-med) var(--ease);
254
+ }
255
+ .ui-switch__track::before {
256
+ content: "";
257
+ position: absolute;
258
+ top: 3px;
259
+ left: 3px;
260
+ width: calc(var(--sw-h) - 6px);
261
+ height: calc(var(--sw-h) - 6px);
262
+ background: var(--control-knob);
263
+ border: 1px solid var(--border-strong);
264
+ box-sizing: border-box;
265
+ border-radius: 50%;
266
+ transition: transform var(--dur-med) var(--ease);
267
+ }
268
+ .ui-switch input:checked + .ui-switch__track {
269
+ background: var(--accent);
270
+ }
271
+ .ui-switch input:checked + .ui-switch__track::before {
272
+ transform: translateX(calc(var(--sw-w) - var(--sw-h)));
273
+ }
274
+ .ui-switch input:focus-visible + .ui-switch__track {
275
+ outline: 2px solid transparent;
276
+ box-shadow: var(--ring);
277
+ }
278
+ .ui-switch input:disabled + .ui-switch__track {
279
+ opacity: 0.5;
280
+ cursor: not-allowed;
281
+ }
282
+
29
283
  /* ../src/styles/tooltip.css */
30
284
  .ui-tip-host {
31
285
  position: relative;
@@ -333,241 +587,6 @@
333
587
  animation: m-scale-in var(--dur-med) var(--ease-out) var(--delay-1) both;
334
588
  }
335
589
 
336
- /* ../src/styles/input.css */
337
- .ui-field {
338
- display: block;
339
- }
340
- .ui-field__label {
341
- display: block;
342
- color: var(--strong);
343
- font-size: var(--text-base);
344
- font-weight: var(--weight-medium);
345
- margin-bottom: 8px;
346
- }
347
- .ui-field__req {
348
- color: var(--pink);
349
- margin-left: 4px;
350
- }
351
- .ui-field__hint {
352
- color: var(--text);
353
- font-size: var(--text-sm);
354
- margin-top: 7px;
355
- line-height: 1.5;
356
- }
357
- .ui-field__error {
358
- color: var(--pink);
359
- font-size: var(--text-sm);
360
- margin-top: 7px;
361
- display: flex;
362
- align-items: center;
363
- gap: 6px;
364
- }
365
- .ui-field__error svg {
366
- width: 16px;
367
- height: 16px;
368
- flex: none;
369
- stroke-width: 2.4;
370
- }
371
- .ui-input,
372
- .ui-textarea,
373
- .ui-select {
374
- font-family: var(--font-sans);
375
- font-size: var(--text-base);
376
- color: var(--strong);
377
- background: var(--field-bg);
378
- border: 1px solid var(--field-edge);
379
- border-radius: var(--radius-md);
380
- padding: 12px 15px;
381
- width: 100%;
382
- transition: border-color var(--dur-med) var(--ease), box-shadow var(--dur-med) var(--ease);
383
- }
384
- .ui-textarea {
385
- resize: vertical;
386
- min-height: 96px;
387
- line-height: 1.55;
388
- }
389
- .ui-input:hover,
390
- .ui-textarea:hover,
391
- .ui-select:hover {
392
- border-color: var(--field-edge-hover);
393
- }
394
- .ui-input:focus-visible,
395
- .ui-textarea:focus-visible,
396
- .ui-select:focus-visible {
397
- outline: 2px solid transparent;
398
- border-color: var(--accent);
399
- box-shadow: var(--ring);
400
- }
401
- .ui-input::placeholder,
402
- .ui-textarea::placeholder {
403
- color: var(--muted);
404
- opacity: 0.9;
405
- }
406
- .ui-input:disabled,
407
- .ui-select:disabled,
408
- .ui-textarea:disabled {
409
- background: var(--disabled-surface);
410
- border-color: var(--disabled-border);
411
- color: var(--disabled-ink);
412
- cursor: not-allowed;
413
- }
414
- .ui-input.is-invalid,
415
- .ui-select.is-invalid,
416
- .ui-textarea.is-invalid {
417
- border-color: var(--pink);
418
- }
419
- .ui-input[type=search]::-webkit-search-cancel-button {
420
- appearance: none;
421
- display: none;
422
- }
423
- .ui-input-group {
424
- position: relative;
425
- display: flex;
426
- align-items: center;
427
- }
428
- .ui-input-group .ui-input {
429
- padding-left: 42px;
430
- }
431
- .ui-input-group__icon {
432
- position: absolute;
433
- left: 14px;
434
- width: 17px;
435
- height: 17px;
436
- color: var(--muted);
437
- pointer-events: none;
438
- }
439
- .ui-input-group__icon svg {
440
- width: 100%;
441
- height: 100%;
442
- stroke: currentColor;
443
- fill: none;
444
- stroke-width: 2.2;
445
- }
446
- .ui-select {
447
- appearance: none;
448
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a29db6' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
449
- background-repeat: no-repeat;
450
- background-position: right 15px center;
451
- padding-right: 40px;
452
- cursor: pointer;
453
- }
454
- :root[data-theme=light] .ui-select {
455
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235c6270' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
456
- }
457
- .ui-check {
458
- display: inline-flex;
459
- align-items: flex-start;
460
- gap: 11px;
461
- cursor: pointer;
462
- font-size: var(--text-base);
463
- color: var(--text);
464
- line-height: 1.5;
465
- }
466
- .ui-check input {
467
- appearance: none;
468
- width: 19px;
469
- height: 19px;
470
- flex: none;
471
- margin-top: 1px;
472
- border: 1.5px solid var(--border-strong);
473
- border-radius: var(--radius-xs);
474
- background: var(--surface-2);
475
- cursor: pointer;
476
- transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
477
- position: relative;
478
- }
479
- .ui-check input::before {
480
- content: "";
481
- position: absolute;
482
- left: 50%;
483
- top: 50%;
484
- width: 24px;
485
- height: 24px;
486
- transform: translate(-50%, -50%);
487
- }
488
- .ui-check input[type=radio] {
489
- border-radius: 50%;
490
- }
491
- .ui-check input:hover {
492
- border-color: var(--accent);
493
- }
494
- .ui-check input:checked {
495
- background: var(--accent);
496
- border-color: var(--accent);
497
- }
498
- .ui-check input[type=checkbox]:checked::after {
499
- content: "";
500
- position: absolute;
501
- left: 5px;
502
- top: 1.5px;
503
- width: 5px;
504
- height: 10px;
505
- border: solid var(--accent-contrast);
506
- border-width: 0 2px 2px 0;
507
- transform: rotate(45deg);
508
- }
509
- .ui-check input[type=radio]:checked::after {
510
- content: "";
511
- position: absolute;
512
- inset: 4px;
513
- border-radius: 50%;
514
- background: var(--accent-contrast);
515
- }
516
- .ui-check input:focus-visible {
517
- outline: 2px solid transparent;
518
- box-shadow: var(--ring);
519
- }
520
- .ui-switch {
521
- --sw-w: 44px;
522
- --sw-h: 26px;
523
- position: relative;
524
- display: inline-block;
525
- width: var(--sw-w);
526
- height: var(--sw-h);
527
- flex: none;
528
- }
529
- .ui-switch input {
530
- position: absolute;
531
- opacity: 0;
532
- width: 0;
533
- height: 0;
534
- }
535
- .ui-switch__track {
536
- position: absolute;
537
- inset: 0;
538
- background: var(--surface-3);
539
- border-radius: var(--radius-pill);
540
- cursor: pointer;
541
- transition: background var(--dur-med) var(--ease);
542
- }
543
- .ui-switch__track::before {
544
- content: "";
545
- position: absolute;
546
- top: 3px;
547
- left: 3px;
548
- width: calc(var(--sw-h) - 6px);
549
- height: calc(var(--sw-h) - 6px);
550
- background: var(--control-knob);
551
- border: 1px solid var(--border-strong);
552
- box-sizing: border-box;
553
- border-radius: 50%;
554
- transition: transform var(--dur-med) var(--ease);
555
- }
556
- .ui-switch input:checked + .ui-switch__track {
557
- background: var(--accent);
558
- }
559
- .ui-switch input:checked + .ui-switch__track::before {
560
- transform: translateX(calc(var(--sw-w) - var(--sw-h)));
561
- }
562
- .ui-switch input:focus-visible + .ui-switch__track {
563
- outline: 2px solid transparent;
564
- box-shadow: var(--ring);
565
- }
566
- .ui-switch input:disabled + .ui-switch__track {
567
- opacity: 0.5;
568
- cursor: not-allowed;
569
- }
570
-
571
590
  /* src/Field.css */
572
591
  .ui-field-number {
573
592
  display: flex;
@@ -1,14 +1,32 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ButtonHTMLAttributes, HTMLAttributes, RefObject, MouseEvent, ElementType, ComponentPropsWithoutRef, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
2
+ import { InputHTMLAttributes, ReactNode, ButtonHTMLAttributes, RefAttributes, ReactElement, AnchorHTMLAttributes, HTMLAttributes, RefObject, MouseEvent, ElementType, ComponentPropsWithoutRef, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
3
3
 
4
4
  declare function Icon({ name }: {
5
5
  name: string;
6
6
  }): react.JSX.Element;
7
7
 
8
- type ButtonProps = {
8
+ type CheckboxProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'children'> & {
9
+ label: ReactNode;
10
+ type?: 'checkbox' | 'radio';
11
+ };
12
+ declare const Checkbox: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "children"> & {
13
+ label: ReactNode;
14
+ type?: "checkbox" | "radio";
15
+ } & react.RefAttributes<HTMLInputElement>>;
16
+
17
+ type SwitchProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'children'> & {
18
+ label: string;
19
+ };
20
+ declare const Switch: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "children"> & {
21
+ label: string;
22
+ } & react.RefAttributes<HTMLInputElement>>;
23
+
24
+ type ButtonOptions = {
9
25
  variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
10
26
  size?: 'xs' | 'sm' | 'md' | 'lg';
11
27
  icon?: string;
28
+ /** Decorative caller artwork, before the label; takes precedence over icon. */
29
+ leading?: ReactNode;
12
30
  iconRight?: string;
13
31
  iconOnly?: boolean;
14
32
  block?: boolean;
@@ -17,20 +35,22 @@ type ButtonProps = {
17
35
  /** Message after busy ends; an empty string leaves outcome announcements to the caller. */
18
36
  completionMessage?: string;
19
37
  children?: ReactNode;
20
- } & ButtonHTMLAttributes<HTMLButtonElement>;
21
- declare const Button: react.ForwardRefExoticComponent<{
22
- variant?: "primary" | "secondary" | "ghost" | "danger";
23
- size?: "xs" | "sm" | "md" | "lg";
24
- icon?: string;
25
- iconRight?: string;
26
- iconOnly?: boolean;
27
- block?: boolean;
28
- /** In flight: keeps focus, blocks activation, announces progress, and shows dots. */
29
- busy?: boolean;
30
- /** Message after busy ends; an empty string leaves outcome announcements to the caller. */
31
- completionMessage?: string;
32
- children?: ReactNode;
33
- } & ButtonHTMLAttributes<HTMLButtonElement> & react.RefAttributes<HTMLButtonElement>>;
38
+ };
39
+ type NativeButtonProps = ButtonOptions & ButtonHTMLAttributes<HTMLButtonElement> & {
40
+ href?: never;
41
+ };
42
+ type ButtonLinkProps = ButtonOptions & AnchorHTMLAttributes<HTMLAnchorElement> & {
43
+ href: string;
44
+ disabled?: boolean;
45
+ };
46
+ type ButtonProps = NativeButtonProps | ButtonLinkProps;
47
+ type ButtonElement = HTMLButtonElement | HTMLAnchorElement;
48
+ declare const Button: {
49
+ (props: NativeButtonProps & RefAttributes<HTMLButtonElement>): ReactElement;
50
+ (props: ButtonLinkProps & RefAttributes<HTMLAnchorElement>): ReactElement;
51
+ (props: ButtonProps & RefAttributes<ButtonElement>): ReactElement;
52
+ displayName?: string;
53
+ };
34
54
 
35
55
  declare function Badge({ variant, children }: {
36
56
  variant?: string;
@@ -712,4 +732,4 @@ declare const SidebarNav: react.ForwardRefExoticComponent<Omit<Omit<react.Detail
712
732
  declare const PAGE_SIZES: readonly number[];
713
733
  declare const DEFAULT_PAGE_SIZE: number;
714
734
 
715
- export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };
735
+ export { AppShell, type AppShellProps, type AppShellSection, BackLink, type BackLinkOwnProps, type BackLinkProps, Badge, BusyRegion, type BusyRegionProps, Button, type ButtonLinkProps, type ButtonProps, Callout, type CalloutProps, type CalloutVariant, Card, Checkbox, type CheckboxProps, type Column, type CommandGroup, type CommandItem, CommandPalette, type CommandPaletteProps, Confirm, type ConfirmProps, DEFAULT_PAGE_SIZE, DataTable, type DataTableProps, DeltaValue, type DeltaValueProps, Denied, type DeniedProps, Drawer, type DrawerProps, DrawerSection, type DrawerSectionProps, Dropdown, type DropdownBadge, type DropdownEntry, type DropdownItem, type DropdownProps, type DropdownRowProps, type DropdownSearch, type DropdownSection, type DropdownSeparator, EmptyState, type EmptyStateProps, type EmptyStateVariant, Field, type FieldControlProps, type FieldProps, FileField, type FileFieldProps, type Filter, FilterBar, type FilterBarProps, Icon, KeyValueList, type KeyValueListProps, type KeyValueRow, Modal, type ModalProps, type NativeButtonProps, NumericValue, type NumericValueProps, PAGE_SIZES, Pagination, type PaginationProps, Pill, type PillProps, RowIdentity, type RowIdentityProps, type SegmentOption, Segmented, type SegmentedProps, SelectField, type SelectFieldProps, SidebarNav, type SidebarNavBadge, type SidebarNavItem, type SidebarNavLeaf, type SidebarNavProps, type SidebarNavSection, Skeleton, type SkeletonProps, SkeletonTable, type SkeletonTableProps, Snippet, type SnippetProps, StatBand, type StatBandProps, type StatDelta, type StatFigure, type StatTone, type StatVariant, StatusDot, type StatusDotProps, Switch, type SwitchProps, THEME_INIT_SCRIPT, type TabItem, type TableSort, Tabs, type TabsProps, TextArea, type TextAreaProps, TextField, type TextFieldProps, ThemeToggle, type ThemeToggleProps, Timeline, type TimelineEvent, type TimelineEventKind, type TimelineProps, Toast, type ToastNotice, type ToastProps, Tooltip, type TooltipProps, sortTableRows, useToast };