ionbase-ui 0.51.0 → 0.69.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 (174) hide show
  1. package/dist/components/Accordion.d.ts +54 -0
  2. package/dist/components/Accordion.d.ts.map +1 -0
  3. package/dist/components/Accordion.js +68 -0
  4. package/dist/components/Accordion.js.map +1 -0
  5. package/dist/components/AgentActivity.d.ts +2 -1
  6. package/dist/components/AgentActivity.d.ts.map +1 -1
  7. package/dist/components/AgentActivity.js +4 -17
  8. package/dist/components/AgentActivity.js.map +1 -1
  9. package/dist/components/Breadcrumb.d.ts +45 -0
  10. package/dist/components/Breadcrumb.d.ts.map +1 -0
  11. package/dist/components/Breadcrumb.js +31 -0
  12. package/dist/components/Breadcrumb.js.map +1 -0
  13. package/dist/components/Calendar.d.ts +20 -0
  14. package/dist/components/Calendar.d.ts.map +1 -0
  15. package/dist/components/Calendar.js +151 -0
  16. package/dist/components/Calendar.js.map +1 -0
  17. package/dist/components/CalendarPopover.d.ts +26 -0
  18. package/dist/components/CalendarPopover.d.ts.map +1 -0
  19. package/dist/components/CalendarPopover.js +45 -0
  20. package/dist/components/CalendarPopover.js.map +1 -0
  21. package/dist/components/Checkbox.d.ts +2 -1
  22. package/dist/components/Checkbox.d.ts.map +1 -1
  23. package/dist/components/Checkbox.js +4 -2
  24. package/dist/components/Checkbox.js.map +1 -1
  25. package/dist/components/Combobox.d.ts +86 -0
  26. package/dist/components/Combobox.d.ts.map +1 -0
  27. package/dist/components/Combobox.js +261 -0
  28. package/dist/components/Combobox.js.map +1 -0
  29. package/dist/components/DateField.d.ts +15 -0
  30. package/dist/components/DateField.d.ts.map +1 -0
  31. package/dist/components/DateField.js +74 -0
  32. package/dist/components/DateField.js.map +1 -0
  33. package/dist/components/DatePicker.d.ts +81 -0
  34. package/dist/components/DatePicker.d.ts.map +1 -0
  35. package/dist/components/DatePicker.js +102 -0
  36. package/dist/components/DatePicker.js.map +1 -0
  37. package/dist/components/DateRangePicker.d.ts +117 -0
  38. package/dist/components/DateRangePicker.d.ts.map +1 -0
  39. package/dist/components/DateRangePicker.js +152 -0
  40. package/dist/components/DateRangePicker.js.map +1 -0
  41. package/dist/components/Drawer.d.ts +52 -0
  42. package/dist/components/Drawer.d.ts.map +1 -0
  43. package/dist/components/Drawer.js +66 -0
  44. package/dist/components/Drawer.js.map +1 -0
  45. package/dist/components/FileUpload.d.ts +79 -0
  46. package/dist/components/FileUpload.d.ts.map +1 -0
  47. package/dist/components/FileUpload.js +218 -0
  48. package/dist/components/FileUpload.js.map +1 -0
  49. package/dist/components/Header.d.ts +10 -0
  50. package/dist/components/Header.d.ts.map +1 -1
  51. package/dist/components/Header.js +18 -3
  52. package/dist/components/Header.js.map +1 -1
  53. package/dist/components/NumberInput.d.ts +63 -0
  54. package/dist/components/NumberInput.d.ts.map +1 -0
  55. package/dist/components/NumberInput.js +102 -0
  56. package/dist/components/NumberInput.js.map +1 -0
  57. package/dist/components/ProgressBar.d.ts +46 -0
  58. package/dist/components/ProgressBar.d.ts.map +1 -0
  59. package/dist/components/ProgressBar.js +52 -0
  60. package/dist/components/ProgressBar.js.map +1 -0
  61. package/dist/components/PromptInput.d.ts +89 -0
  62. package/dist/components/PromptInput.d.ts.map +1 -0
  63. package/dist/components/PromptInput.js +130 -0
  64. package/dist/components/PromptInput.js.map +1 -0
  65. package/dist/components/SegmentedControl.d.ts +63 -0
  66. package/dist/components/SegmentedControl.d.ts.map +1 -0
  67. package/dist/components/SegmentedControl.js +79 -0
  68. package/dist/components/SegmentedControl.js.map +1 -0
  69. package/dist/components/Select.d.ts +9 -2
  70. package/dist/components/Select.d.ts.map +1 -1
  71. package/dist/components/Select.js.map +1 -1
  72. package/dist/components/Sidebar.d.ts +109 -0
  73. package/dist/components/Sidebar.d.ts.map +1 -0
  74. package/dist/components/Sidebar.js +106 -0
  75. package/dist/components/Sidebar.js.map +1 -0
  76. package/dist/components/Skeleton.d.ts +39 -0
  77. package/dist/components/Skeleton.d.ts.map +1 -0
  78. package/dist/components/Skeleton.js +50 -0
  79. package/dist/components/Skeleton.js.map +1 -0
  80. package/dist/components/Spinner.d.ts +45 -0
  81. package/dist/components/Spinner.d.ts.map +1 -0
  82. package/dist/components/Spinner.js +44 -0
  83. package/dist/components/Spinner.js.map +1 -0
  84. package/dist/components/Stepper.d.ts +73 -0
  85. package/dist/components/Stepper.d.ts.map +1 -0
  86. package/dist/components/Stepper.js +82 -0
  87. package/dist/components/Stepper.js.map +1 -0
  88. package/dist/components/Toggle.d.ts +2 -1
  89. package/dist/components/Toggle.d.ts.map +1 -1
  90. package/dist/components/Toggle.js +4 -2
  91. package/dist/components/Toggle.js.map +1 -1
  92. package/dist/components/ToolCall.d.ts +66 -0
  93. package/dist/components/ToolCall.d.ts.map +1 -0
  94. package/dist/components/ToolCall.js +78 -0
  95. package/dist/components/ToolCall.js.map +1 -0
  96. package/dist/components/agent-status.d.ts +14 -0
  97. package/dist/components/agent-status.d.ts.map +1 -0
  98. package/dist/components/agent-status.js +16 -0
  99. package/dist/components/agent-status.js.map +1 -0
  100. package/dist/components/index.d.ts +33 -0
  101. package/dist/components/index.d.ts.map +1 -1
  102. package/dist/components/index.js +16 -0
  103. package/dist/components/index.js.map +1 -1
  104. package/dist/components/iso-date.d.ts +53 -0
  105. package/dist/components/iso-date.d.ts.map +1 -0
  106. package/dist/components/iso-date.js +102 -0
  107. package/dist/components/iso-date.js.map +1 -0
  108. package/dist/components/resolve-selection.d.ts +36 -0
  109. package/dist/components/resolve-selection.d.ts.map +1 -0
  110. package/dist/components/resolve-selection.js +14 -0
  111. package/dist/components/resolve-selection.js.map +1 -0
  112. package/dist/figma-descriptions.json +255 -145
  113. package/dist/figma-map.json +3205 -1860
  114. package/dist/meta/Accordion.json +123 -0
  115. package/dist/meta/AccordionItem.json +90 -0
  116. package/dist/meta/AgentActivityStep.json +2 -2
  117. package/dist/meta/Breadcrumb.json +87 -0
  118. package/dist/meta/BreadcrumbItem.json +78 -0
  119. package/dist/meta/Checkbox.json +18 -1
  120. package/dist/meta/Combobox.json +265 -0
  121. package/dist/meta/DatePicker.json +270 -0
  122. package/dist/meta/DateRangePicker.json +284 -0
  123. package/dist/meta/Drawer.json +234 -0
  124. package/dist/meta/EmptyState.json +1 -1
  125. package/dist/meta/FileUpload.json +246 -0
  126. package/dist/meta/Header.json +32 -2
  127. package/dist/meta/NumberInput.json +407 -0
  128. package/dist/meta/PhoneInput.json +39 -39
  129. package/dist/meta/Popover.json +3 -3
  130. package/dist/meta/ProgressBar.json +166 -0
  131. package/dist/meta/PromptInput.json +212 -0
  132. package/dist/meta/SegmentedControl.json +217 -0
  133. package/dist/meta/SegmentedControlItem.json +122 -0
  134. package/dist/meta/Select.json +4 -3
  135. package/dist/meta/Sidebar.json +150 -0
  136. package/dist/meta/SidebarItem.json +184 -0
  137. package/dist/meta/SidebarSection.json +131 -0
  138. package/dist/meta/Skeleton.json +120 -0
  139. package/dist/meta/Spinner.json +123 -0
  140. package/dist/meta/Stepper.json +135 -0
  141. package/dist/meta/StepperStep.json +148 -0
  142. package/dist/meta/Tabs.json +5 -0
  143. package/dist/meta/Toggle.json +18 -1
  144. package/dist/meta/ToolCall.json +192 -0
  145. package/dist/meta/Tooltip.json +3 -3
  146. package/dist/meta/components.json +6759 -2705
  147. package/dist/meta/contrast.json +11075 -3098
  148. package/dist/meta/index.json +266 -8
  149. package/dist/meta/patterns/AgentRun.json +27 -0
  150. package/dist/meta/patterns/Form.json +9 -0
  151. package/dist/meta/patterns/PageShell.json +43 -3
  152. package/dist/meta/patterns/Wizard.json +20 -1
  153. package/dist/meta/patterns/index.json +10 -1
  154. package/dist/styles/accordion.css +102 -0
  155. package/dist/styles/breadcrumb.css +72 -0
  156. package/dist/styles/combobox.css +154 -0
  157. package/dist/styles/date-picker.css +420 -0
  158. package/dist/styles/date-range-picker.css +280 -0
  159. package/dist/styles/drawer.css +181 -0
  160. package/dist/styles/empty-state.css +16 -4
  161. package/dist/styles/file-upload.css +236 -0
  162. package/dist/styles/header.css +26 -0
  163. package/dist/styles/index.css +23 -0
  164. package/dist/styles/number-input.css +83 -0
  165. package/dist/styles/progress-bar.css +107 -0
  166. package/dist/styles/prompt-input.css +127 -0
  167. package/dist/styles/segmented-control.css +182 -0
  168. package/dist/styles/sidebar.css +307 -0
  169. package/dist/styles/skeleton.css +76 -0
  170. package/dist/styles/spinner.css +70 -0
  171. package/dist/styles/stepper.css +250 -0
  172. package/dist/styles/tool-call.css +202 -0
  173. package/llms.txt +2 -2
  174. package/package.json +2 -1
@@ -0,0 +1,181 @@
1
+ /*
2
+ * Drawer
3
+ *
4
+ * Drawn in Figma as `Drawer` (1365:2404), from this file. Twelve variants:
5
+ * four placements by three sizes, the same cross product the props expose.
6
+ *
7
+ * The panel IS the block — `.ion-drawer`, not `.ion-drawer__panel` — which
8
+ * matches popover.css and is load-bearing rather than cosmetic. The contrast
9
+ * gate resolves a translucent hover colour's backdrop from the block's own
10
+ * background, so with the surface on an element class the close button's hover
11
+ * pairing came back "skipped": unmeasured, and silently so.
12
+ *
13
+ * The scrim lives here for the same reason it lives in modal.css: nothing can
14
+ * render an overlay without also rendering what sits behind it, so the split
15
+ * Figma makes between panel and overlay has no meaning in code.
16
+ *
17
+ * Placement uses logical properties throughout — `inset-inline-start` rather
18
+ * than `left` — so `start` and `end` follow writing direction and a right-hand
19
+ * drawer in English is a left-hand one in Arabic without a second variant.
20
+ */
21
+ .ion-drawer__scrim {
22
+ position: fixed;
23
+ inset: 0;
24
+ z-index: 1000;
25
+ display: flex;
26
+ background-color: var(--surface-scrim);
27
+ }
28
+
29
+ .ion-drawer {
30
+ --ion-drawer-size: 30rem;
31
+
32
+ position: fixed;
33
+ display: flex;
34
+ flex-direction: column;
35
+ max-width: 100%;
36
+ max-height: 100%;
37
+ background-color: var(--surface-raised);
38
+ color: var(--text-default);
39
+ font-family: var(--font-family-sans);
40
+ }
41
+
42
+ .ion-drawer--sm {
43
+ --ion-drawer-size: 22rem;
44
+ }
45
+
46
+ .ion-drawer--lg {
47
+ --ion-drawer-size: 40rem;
48
+ }
49
+
50
+ /* Edge panels are square against their own edge and rounded on the inner one:
51
+ * a drawer rounded on all four corners reads as a floating card that happens
52
+ * to touch the side. */
53
+ .ion-drawer--start,
54
+ .ion-drawer--end {
55
+ inset-block: 0;
56
+ width: var(--ion-drawer-size);
57
+ }
58
+
59
+ .ion-drawer--start {
60
+ inset-inline-start: 0;
61
+ border-start-end-radius: var(--radius-lg);
62
+ border-end-end-radius: var(--radius-lg);
63
+ }
64
+
65
+ .ion-drawer--end {
66
+ inset-inline-end: 0;
67
+ border-start-start-radius: var(--radius-lg);
68
+ border-end-start-radius: var(--radius-lg);
69
+ }
70
+
71
+ .ion-drawer--top,
72
+ .ion-drawer--bottom {
73
+ inset-inline: 0;
74
+ height: var(--ion-drawer-size);
75
+ width: 100%;
76
+ }
77
+
78
+ .ion-drawer--top {
79
+ inset-block-start: 0;
80
+ border-end-start-radius: var(--radius-lg);
81
+ border-end-end-radius: var(--radius-lg);
82
+ }
83
+
84
+ .ion-drawer--bottom {
85
+ inset-block-end: 0;
86
+ border-start-start-radius: var(--radius-lg);
87
+ border-start-end-radius: var(--radius-lg);
88
+ }
89
+
90
+ .ion-drawer__header {
91
+ display: flex;
92
+ align-items: flex-start;
93
+ justify-content: space-between;
94
+ gap: var(--spacing-12);
95
+ flex: none;
96
+ padding: var(--spacing-24);
97
+ border-bottom: var(--border-width-default) solid var(--border-subtle);
98
+ }
99
+
100
+ .ion-drawer__heading {
101
+ display: flex;
102
+ flex-direction: column;
103
+ gap: var(--spacing-4);
104
+ min-width: 0;
105
+ }
106
+
107
+ .ion-drawer__title {
108
+ margin: 0;
109
+ font-size: var(--type-h5);
110
+ line-height: var(--type-h5-line-height);
111
+ font-weight: var(--font-weight-semibold);
112
+ }
113
+
114
+ .ion-drawer__description {
115
+ margin: 0;
116
+ color: var(--text-secondary);
117
+ font-size: var(--type-body-sm);
118
+ line-height: var(--type-body-sm-line-height);
119
+ }
120
+
121
+ .ion-drawer__close {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ flex: none;
126
+ width: var(--spacing-32);
127
+ height: var(--spacing-32);
128
+ padding: var(--spacing-4);
129
+ border: 0;
130
+ border-radius: var(--radius-sm);
131
+ background: none;
132
+ color: var(--icon-secondary);
133
+ cursor: pointer;
134
+ }
135
+
136
+ /*
137
+ * Background only, no colour change — the same rule Modal's close button uses,
138
+ * and for a measurable reason as well as a consistency one. `surface/hover` is
139
+ * translucent, so a rule that changes BOTH creates a foreground/background pair
140
+ * whose backdrop the contrast gate cannot resolve, and it reports the pairing
141
+ * as skipped. A skipped pairing is an unmeasured one.
142
+ */
143
+ @media (hover: hover) {
144
+ .ion-drawer__close:hover {
145
+ background-color: var(--surface-hover);
146
+ }
147
+ }
148
+
149
+ .ion-drawer__close:focus-visible {
150
+ outline: var(--border-width-thick) solid var(--border-focus);
151
+ outline-offset: 2px;
152
+ }
153
+
154
+ .ion-drawer__close svg {
155
+ width: var(--icon-size-md);
156
+ height: var(--icon-size-md);
157
+ }
158
+
159
+ /*
160
+ * The body is the only scrolling region. A drawer whose whole panel scrolls
161
+ * takes its own title off screen, which is the one piece of context a user
162
+ * needs while working down a long list.
163
+ */
164
+ .ion-drawer__body {
165
+ flex: 1;
166
+ min-height: 0;
167
+ overflow-y: auto;
168
+ padding: var(--spacing-24);
169
+ font-size: var(--type-body);
170
+ line-height: var(--type-body-line-height);
171
+ }
172
+
173
+ .ion-drawer__footer {
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: flex-end;
177
+ gap: var(--spacing-8);
178
+ flex: none;
179
+ padding: var(--spacing-24);
180
+ border-top: var(--border-width-default) solid var(--border-subtle);
181
+ }
@@ -1,9 +1,11 @@
1
1
  /*
2
2
  * EmptyState
3
3
  *
4
- * Repo-owned, not measured from Figma — there is no Empty State component in
5
- * the file yet. Every value below is a token already on a ladder, so when one
6
- * is drawn the two can be reconciled without moving anything underneath.
4
+ * Drawn in Figma on 15 Sep 2026, as `Empty State` — 12 variants, Size x Reason.
5
+ * It was the last component in this package with no Figma counterpart, and it
6
+ * went the unusual direction: built here first from the pattern tier's
7
+ * requirement, then drawn to match. Every value was already a token on a
8
+ * ladder, which is what made the reconciliation cost one line.
7
9
  *
8
10
  * REASON CHANGES THE ICON COLOUR AND NOTHING ELSE
9
11
  *
@@ -117,7 +119,17 @@
117
119
  color: var(--text-default);
118
120
  font-size: var(--ion-empty-state-title);
119
121
  line-height: var(--ion-empty-state-title-line-height);
120
- font-weight: var(--font-weight-medium);
122
+
123
+ /*
124
+ * Semibold, not medium, settled when the component was drawn. Every heading
125
+ * style in the Figma file — `Heading/H3`, `Heading/H4` — is semibold, so a
126
+ * medium title here would have been the only heading-sized text in the system
127
+ * off the ramp, and would have needed a text style that exists nowhere else.
128
+ * The `inline` size sits at body-md rather than a heading rung, and its style
129
+ * (`Body/Medium Semibold`) was added to the file alongside the component, in
130
+ * the same shape as the `Body/Default Semibold` already there.
131
+ */
132
+ font-weight: var(--font-weight-semibold);
121
133
 
122
134
  /* A filter value or record id in the title must wrap, not widen the region. */
123
135
  overflow-wrap: anywhere;
@@ -0,0 +1,236 @@
1
+ /*
2
+ * FileUpload
3
+ *
4
+ * Drawn in Figma as `File Upload` (1367:2333), from this file. Nothing here was
5
+ * invented: the border, radius, background and disabled treatment are
6
+ * `Input`'s, and the centred icon-over-text stack with its padding ladder is
7
+ * `EmptyState`'s. The bet when this was written was that drawing it would move
8
+ * values and not structure; drawing it moved neither.
9
+ *
10
+ * Figma draws the DROP ZONE only, not the label and helper around it — the same
11
+ * split `Input` makes against `Form Field`. The file list has no Figma
12
+ * counterpart at all, because it is runtime content rather than a state.
13
+ *
14
+ * THE VISUALLY HIDDEN INPUT IS NOT `display: none`
15
+ *
16
+ * A `display: none` input leaves the tab order and the accessibility tree, and
17
+ * the component then has no control in it at all — which is how the common
18
+ * build of this component ends up keyboard-unreachable while looking identical.
19
+ * `.ion-visually-hidden` would work, but the input is also positioned to sit
20
+ * inside the zone so a browser can anchor its own validation bubble somewhere
21
+ * sensible rather than at the top-left of the page.
22
+ */
23
+ .ion-file-upload {
24
+ --ion-file-upload-padding: var(--spacing-32);
25
+ --ion-file-upload-gap: var(--spacing-8);
26
+ --ion-file-upload-icon-size: var(--icon-size-lg);
27
+
28
+ position: relative;
29
+ display: flex;
30
+ background-color: var(--surface-default);
31
+ border: var(--border-width-default) dashed var(--border-default);
32
+ border-radius: var(--radius-md);
33
+ transition:
34
+ border-color var(--ion-duration-base) var(--ion-ease-out),
35
+ background-color var(--ion-duration-base) var(--ion-ease-out);
36
+ }
37
+
38
+ .ion-file-upload--sm {
39
+ --ion-file-upload-padding: var(--spacing-20);
40
+ --ion-file-upload-gap: var(--spacing-6);
41
+ --ion-file-upload-icon-size: var(--icon-size-md);
42
+ }
43
+
44
+ /*
45
+ * 1px, not 0. The input has to have a box for the focus ring to be drawn
46
+ * against and for a validation bubble to point at; collapsing it to nothing
47
+ * puts both in the document corner. It is clipped, so the 1px is never seen.
48
+ */
49
+ .ion-file-upload__input {
50
+ position: absolute;
51
+ bottom: var(--spacing-8);
52
+ left: 50%;
53
+ width: 1px;
54
+ height: 1px;
55
+ padding: 0;
56
+ overflow: hidden;
57
+ clip-path: inset(50%);
58
+ border: 0;
59
+ }
60
+
61
+ .ion-file-upload__target {
62
+ display: flex;
63
+ flex: 1 1 auto;
64
+ flex-direction: column;
65
+ align-items: center;
66
+ justify-content: center;
67
+ gap: var(--ion-file-upload-gap);
68
+ padding: var(--ion-file-upload-padding) var(--spacing-24);
69
+ text-align: center;
70
+ cursor: pointer;
71
+ }
72
+
73
+ .ion-file-upload__icon {
74
+ display: inline-flex;
75
+ color: var(--icon-secondary);
76
+ }
77
+
78
+ .ion-file-upload__icon svg {
79
+ width: var(--ion-file-upload-icon-size);
80
+ height: var(--ion-file-upload-icon-size);
81
+ }
82
+
83
+ .ion-file-upload__prompt {
84
+ color: var(--text-default);
85
+ font-family: var(--font-family-sans);
86
+ font-size: var(--type-body);
87
+ line-height: var(--type-body-line-height);
88
+ font-weight: var(--font-weight-medium);
89
+ }
90
+
91
+ .ion-file-upload__hint {
92
+ color: var(--text-tertiary);
93
+ font-family: var(--font-family-sans);
94
+ font-size: var(--type-body-sm);
95
+ line-height: var(--type-body-sm-line-height);
96
+ }
97
+
98
+ @media (hover: hover) {
99
+ .ion-file-upload:hover {
100
+ border-color: var(--border-strong);
101
+ background-color: var(--surface-hover);
102
+ }
103
+ }
104
+
105
+ /*
106
+ * The focus ring is on the ZONE, driven by the hidden input's focus. The ring
107
+ * belongs where the user's eye is, and the input is one clipped pixel.
108
+ */
109
+ .ion-file-upload:focus-within {
110
+ outline: var(--border-width-thick) solid var(--ring-focus);
111
+ outline-offset: 2px;
112
+ }
113
+
114
+ /*
115
+ * Dragging reads as a solid border, not a colour change alone. A drop target
116
+ * that only shifts hue is invisible to anyone who cannot distinguish the two —
117
+ * WCAG 1.4.1, and it is also just easier to see.
118
+ */
119
+ .ion-file-upload--dragging,
120
+ .ion-file-upload[data-dragging='true'] {
121
+ border-style: solid;
122
+ border-color: var(--border-focus);
123
+ background-color: var(--surface-primary-subtle);
124
+ }
125
+
126
+ .ion-file-upload--invalid,
127
+ .ion-file-upload[data-invalid='true'] {
128
+ border-color: var(--border-error);
129
+ }
130
+
131
+ .ion-file-upload--disabled,
132
+ .ion-file-upload[data-disabled='true'] {
133
+ background-color: var(--surface-disabled);
134
+ border-color: var(--border-disabled);
135
+ }
136
+
137
+ .ion-file-upload--disabled .ion-file-upload__target,
138
+ .ion-file-upload[data-disabled='true'] .ion-file-upload__target {
139
+ cursor: not-allowed;
140
+ }
141
+
142
+ .ion-file-upload--disabled .ion-file-upload__prompt,
143
+ .ion-file-upload[data-disabled='true'] .ion-file-upload__prompt {
144
+ color: var(--text-disabled);
145
+ }
146
+
147
+ .ion-file-upload--disabled .ion-file-upload__icon,
148
+ .ion-file-upload[data-disabled='true'] .ion-file-upload__icon {
149
+ color: var(--icon-disabled);
150
+ }
151
+
152
+ /*
153
+ * The list of held files is a sibling of the zone, not a child of it.
154
+ *
155
+ * Inside the zone it would sit inside the `<label>`, which makes every row —
156
+ * including each remove button — a click target for the file picker. Removing
157
+ * a file would open the picker on the way out.
158
+ */
159
+ .ion-file-upload__list {
160
+ display: flex;
161
+ flex-direction: column;
162
+ gap: var(--spacing-6);
163
+ margin: 0;
164
+ padding: 0;
165
+ list-style: none;
166
+ }
167
+
168
+ .ion-file-upload__file {
169
+ display: flex;
170
+ align-items: center;
171
+ gap: var(--spacing-8);
172
+ min-height: var(--spacing-32);
173
+ padding-inline: var(--spacing-12);
174
+ background-color: var(--surface-subtle);
175
+ border-radius: var(--radius-sm);
176
+ font-family: var(--font-family-sans);
177
+ font-size: var(--type-body-sm);
178
+ line-height: var(--type-body-sm-line-height);
179
+ }
180
+
181
+ .ion-file-upload__name {
182
+ flex: 1 1 auto;
183
+ min-width: 0;
184
+ overflow: hidden;
185
+ color: var(--text-default);
186
+ text-overflow: ellipsis;
187
+ white-space: nowrap;
188
+ }
189
+
190
+ .ion-file-upload__size {
191
+ flex-shrink: 0;
192
+ color: var(--text-tertiary);
193
+ font-variant-numeric: tabular-nums;
194
+ }
195
+
196
+ /*
197
+ * 24px square — WCAG 2.2 SC 2.5.8's minimum pointer target, around a 16px
198
+ * glyph. Do not shrink it to the icon.
199
+ */
200
+ .ion-file-upload__remove {
201
+ display: inline-flex;
202
+ align-items: center;
203
+ justify-content: center;
204
+ flex-shrink: 0;
205
+ width: var(--spacing-24);
206
+ height: var(--spacing-24);
207
+ padding: 0;
208
+ background: none;
209
+ border: none;
210
+ border-radius: var(--radius-sm);
211
+ color: var(--icon-secondary);
212
+ cursor: pointer;
213
+ transition: background-color var(--ion-duration-base) var(--ion-ease-out);
214
+ }
215
+
216
+ .ion-file-upload__remove svg {
217
+ width: var(--icon-size-sm);
218
+ height: var(--icon-size-sm);
219
+ }
220
+
221
+ @media (hover: hover) {
222
+ .ion-file-upload__remove:hover {
223
+ background-color: var(--surface-hover);
224
+ color: var(--icon-default);
225
+ }
226
+ }
227
+
228
+ .ion-file-upload__remove:focus-visible {
229
+ outline: var(--border-width-thick) solid var(--ring-focus);
230
+ outline-offset: 2px;
231
+ }
232
+
233
+ .ion-file-upload__remove:disabled {
234
+ color: var(--icon-disabled);
235
+ cursor: not-allowed;
236
+ }
@@ -187,6 +187,32 @@
187
187
  }
188
188
  }
189
189
 
190
+ /*
191
+ * menuType="dialog": the toggle opens the caller's Drawer, so there is no
192
+ * panel. The slots keep their bar layout below the breakpoint, and the end
193
+ * slot is pushed to the far edge because the toggle now leads.
194
+ */
195
+ @media (width < 896px) {
196
+ .ion-header.ion-header--dialog-menu .ion-header__menu {
197
+ display: contents;
198
+ }
199
+
200
+ .ion-header--dialog-menu .ion-header__center {
201
+ flex-direction: row;
202
+ align-items: center;
203
+ width: auto;
204
+ }
205
+
206
+ .ion-header--dialog-menu .ion-header__end {
207
+ width: auto;
208
+ margin-inline-start: auto;
209
+ }
210
+
211
+ .ion-header--dialog-menu .ion-header__end > * {
212
+ flex: none;
213
+ }
214
+ }
215
+
190
216
  /*
191
217
  * Forced-colors: box-shadow is dropped by the OS, so the toggle would lose its
192
218
  * only edge. A border in ButtonText keeps it visible.
@@ -67,6 +67,29 @@
67
67
  @import url('./citation.css');
68
68
  @import url('./confidence-indicator.css');
69
69
  @import url('./empty-state.css');
70
+ @import url('./spinner.css');
71
+ @import url('./progress-bar.css');
72
+ @import url('./skeleton.css');
73
+ @import url('./breadcrumb.css');
74
+ @import url('./accordion.css');
75
+ @import url('./drawer.css');
76
+ @import url('./stepper.css');
77
+ @import url('./prompt-input.css');
78
+ @import url('./tool-call.css');
79
+ @import url('./number-input.css');
80
+ @import url('./segmented-control.css');
81
+ @import url('./sidebar.css');
82
+ @import url('./file-upload.css');
83
+ @import url('./combobox.css');
84
+
85
+ /*
86
+ * date-range-picker AFTER date-picker, and the order is load-bearing rather
87
+ * than alphabetical. The range band overrides `.ion-calendar__day`'s
88
+ * border-radius at equal specificity — square sides so consecutive days join
89
+ * into one bar — and at equal specificity the later rule is the one that wins.
90
+ */
91
+ @import url('./date-picker.css');
92
+ @import url('./date-range-picker.css');
70
93
 
71
94
  /*
72
95
  * Visually hidden, but not hidden from assistive tech.
@@ -0,0 +1,83 @@
1
+ /*
2
+ * NumberInput
3
+ *
4
+ * Drawn in Figma as `Number Input` (1389:473), from this file — Input's 21
5
+ * variants cloned, so the box keeps Input's bindings. The box is `.ion-input` itself — the component renders
6
+ * Input's classes, so every size and state comes from input.css and cannot
7
+ * drift from it. This file owns only what Input does not have: the − and +
8
+ * buttons, and tabular figures.
9
+ *
10
+ * − and + sit side by side at (field height − 8px): 24 / 32 / 40. Stacked
11
+ * chevrons would halve that, and a Small field's 16px targets would fall under
12
+ * WCAG 2.5.8's 24px.
13
+ */
14
+
15
+ /*
16
+ * Restates Input's default surface, at zero specificity, for the contrast gate.
17
+ *
18
+ * The gate resolves a translucent `surface/hover` against the block's own
19
+ * background — looked up in THIS stylesheet, and the box's background is
20
+ * declared in input.css. Without this, the step buttons' hover and pressed
21
+ * pairings came back skipped in both modes: unmeasured, not passing.
22
+ *
23
+ * `:where()` is what makes it safe. It matches what the box already paints and
24
+ * loses to every Input state, so disabled and read-only still repaint the box.
25
+ */
26
+ :where(.ion-number-input) {
27
+ background-color: var(--surface-default);
28
+ }
29
+
30
+ .ion-number-input--stepper {
31
+ /* The buttons carry their own inset; full padding would double it. */
32
+ padding-inline-end: var(--spacing-4);
33
+ }
34
+
35
+ /* Figures line up down a column of fields, which is where numbers usually are. */
36
+ .ion-number-input__field {
37
+ font-variant-numeric: tabular-nums;
38
+ }
39
+
40
+ .ion-number-input__steps {
41
+ display: inline-flex;
42
+ flex: none;
43
+ gap: var(--spacing-2);
44
+ }
45
+
46
+ .ion-number-input__step {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ width: calc(var(--ion-input-height) - var(--spacing-8));
51
+ height: calc(var(--ion-input-height) - var(--spacing-8));
52
+ margin: 0;
53
+ padding: 0;
54
+ border: 0;
55
+ border-radius: var(--radius-sm);
56
+ background-color: transparent;
57
+ color: var(--icon-secondary);
58
+ cursor: pointer;
59
+ }
60
+
61
+ .ion-number-input__step svg {
62
+ width: var(--ion-input-icon-size);
63
+ height: var(--ion-input-icon-size);
64
+ }
65
+
66
+ @media (hover: hover) {
67
+ .ion-number-input__step:hover {
68
+ background-color: var(--surface-hover);
69
+ color: var(--icon-default);
70
+ }
71
+ }
72
+
73
+ .ion-number-input__step:active {
74
+ background-color: var(--surface-pressed);
75
+ color: var(--icon-default);
76
+ }
77
+
78
+ /* At the bound, or with the whole field disabled or read-only. */
79
+ .ion-number-input__step:disabled {
80
+ background-color: transparent;
81
+ color: var(--icon-disabled);
82
+ cursor: not-allowed;
83
+ }
@@ -0,0 +1,107 @@
1
+ /*
2
+ * ProgressBar
3
+ *
4
+ * The fill width arrives as a custom property rather than an inline `width`,
5
+ * so the only inline value a caller ever sees is a percentage on a token-named
6
+ * property — and `no-raw-style-values` stays true rather than exempted.
7
+ */
8
+ .ion-progress-bar {
9
+ --ion-progress-bar-height: var(--spacing-8);
10
+ --ion-progress-bar-fill: var(--surface-primary);
11
+ --ion-progress-bar-pct: 0%;
12
+
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--spacing-4);
16
+ font-family: var(--font-family-sans);
17
+ }
18
+
19
+ .ion-progress-bar--sm {
20
+ --ion-progress-bar-height: var(--spacing-4);
21
+ }
22
+
23
+ .ion-progress-bar--success {
24
+ --ion-progress-bar-fill: var(--surface-success);
25
+ }
26
+
27
+ .ion-progress-bar--warning {
28
+ --ion-progress-bar-fill: var(--surface-warning);
29
+ }
30
+
31
+ .ion-progress-bar--error {
32
+ --ion-progress-bar-fill: var(--surface-error);
33
+ }
34
+
35
+ .ion-progress-bar__header {
36
+ display: flex;
37
+ align-items: baseline;
38
+ justify-content: space-between;
39
+ gap: var(--spacing-8);
40
+ font-size: var(--type-body-sm);
41
+ line-height: var(--type-body-sm-line-height);
42
+ }
43
+
44
+ .ion-progress-bar__label {
45
+ color: var(--text-default);
46
+ }
47
+
48
+ .ion-progress-bar__value {
49
+ color: var(--text-secondary);
50
+ font-variant-numeric: tabular-nums;
51
+ }
52
+
53
+ .ion-progress-bar__track {
54
+ overflow: hidden;
55
+ height: var(--ion-progress-bar-height);
56
+ background-color: var(--surface-muted);
57
+ border-radius: var(--radius-full);
58
+ }
59
+
60
+ .ion-progress-bar__fill {
61
+ width: var(--ion-progress-bar-pct);
62
+ height: 100%;
63
+ background-color: var(--ion-progress-bar-fill);
64
+ border-radius: inherit;
65
+ transition: width var(--ion-duration-base) var(--ion-ease-out);
66
+ }
67
+
68
+ /*
69
+ * Indeterminate is a travelling segment rather than a filling bar: a bar that
70
+ * fills and resets reads as repeated failure and restart.
71
+ */
72
+ .ion-progress-bar--indeterminate .ion-progress-bar__fill {
73
+ width: 40%;
74
+ animation: ion-progress-bar-slide var(--ion-duration-slow)
75
+ var(--ion-ease-in-out) infinite;
76
+ }
77
+
78
+ @keyframes ion-progress-bar-slide {
79
+ from {
80
+ transform: translateX(-100%);
81
+ }
82
+
83
+ to {
84
+ transform: translateX(250%);
85
+ }
86
+ }
87
+
88
+ @media (prefers-reduced-motion: reduce) {
89
+ .ion-progress-bar__fill {
90
+ transition-duration: 1ms;
91
+ }
92
+
93
+ /* Still moving, still slow — see Spinner for the same reasoning. */
94
+ .ion-progress-bar--indeterminate .ion-progress-bar__fill {
95
+ animation-duration: 3000ms;
96
+ }
97
+ }
98
+
99
+ @media (forced-colors: active) {
100
+ .ion-progress-bar__track {
101
+ border: var(--border-width-default) solid CanvasText;
102
+ }
103
+
104
+ .ion-progress-bar__fill {
105
+ background-color: Highlight;
106
+ }
107
+ }