@robr0/design-system 0.2.0 → 0.4.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 (99) hide show
  1. package/README.md +33 -28
  2. package/components/Accordion/Accordion.js +0 -1
  3. package/components/AgentStatus/AgentStatus.css +436 -0
  4. package/components/AgentStatus/AgentStatus.d.ts +37 -0
  5. package/components/AgentStatus/AgentStatus.js +53 -0
  6. package/components/AgentStatus/AgentStatusPatterns.d.ts +17 -0
  7. package/components/AgentStatus/AgentStatusPatterns.js +20 -0
  8. package/components/AlertDialog/AlertDialog.css +5 -5
  9. package/components/Avatar/Avatar.js +1 -0
  10. package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
  11. package/components/Breadcrumb/Breadcrumb.js +3 -2
  12. package/components/Button/Button.css +18 -0
  13. package/components/Button/Button.d.ts +20 -2
  14. package/components/Button/Button.js +13 -3
  15. package/components/ButtonGroup/ButtonGroup.js +2 -9
  16. package/components/Card/Card.css +6 -0
  17. package/components/Checkbox/Checkbox.d.ts +17 -3
  18. package/components/CircularButton/CircularButton.css +11 -0
  19. package/components/CircularButton/CircularButton.d.ts +15 -3
  20. package/components/CircularButton/CircularButton.js +19 -6
  21. package/components/CodeBlock/CodeBlock.js +10 -1
  22. package/components/ColorPicker/ColorPicker.css +278 -0
  23. package/components/ColorPicker/ColorPicker.d.ts +50 -0
  24. package/components/ColorPicker/ColorPicker.js +338 -0
  25. package/components/Combobox/Combobox.css +0 -36
  26. package/components/Combobox/Combobox.d.ts +10 -2
  27. package/components/Combobox/Combobox.js +90 -82
  28. package/components/CommandPalette/CommandPalette.css +1 -15
  29. package/components/CommandPalette/CommandPalette.js +6 -5
  30. package/components/ContextMenu/ContextMenu.css +258 -0
  31. package/components/ContextMenu/ContextMenu.d.ts +26 -0
  32. package/components/ContextMenu/ContextMenu.js +350 -0
  33. package/components/DateInput/DateInput.css +0 -36
  34. package/components/DateInput/DateInput.d.ts +5 -1
  35. package/components/DateInput/DateInput.js +37 -33
  36. package/components/DatePicker/DatePicker.js +45 -34
  37. package/components/Dialog/Dialog.css +5 -5
  38. package/components/Dialog/Dialog.js +5 -4
  39. package/components/Drawer/Drawer.js +7 -2
  40. package/components/Dropdown/Dropdown.css +2 -40
  41. package/components/Dropdown/Dropdown.d.ts +10 -2
  42. package/components/Dropdown/Dropdown.js +119 -109
  43. package/components/DropdownMenu/DropdownMenu.css +2 -4
  44. package/components/DropdownMenu/DropdownMenu.js +10 -7
  45. package/components/Field/Field.css +80 -0
  46. package/components/Field/Field.d.ts +50 -0
  47. package/components/Field/Field.js +58 -0
  48. package/components/Field/FieldContext.d.ts +42 -0
  49. package/components/Field/FieldContext.js +7 -0
  50. package/components/FileInput/FileInput.css +0 -36
  51. package/components/FileInput/FileInput.d.ts +5 -1
  52. package/components/FileInput/FileInput.js +111 -103
  53. package/components/Input/Input.css +0 -43
  54. package/components/Input/Input.d.ts +5 -1
  55. package/components/Input/Input.js +51 -46
  56. package/components/Kbd/Kbd.css +28 -0
  57. package/components/Kbd/Kbd.d.ts +19 -0
  58. package/components/Kbd/Kbd.js +14 -0
  59. package/components/LinkList/LinkList.d.ts +1 -0
  60. package/components/Popover/Popover.d.ts +0 -6
  61. package/components/Popover/Popover.js +22 -16
  62. package/components/ProgressBar/ProgressBar.js +1 -1
  63. package/components/RadioButton/RadioButton.d.ts +17 -3
  64. package/components/Reasoning/Reasoning.css +152 -0
  65. package/components/Reasoning/Reasoning.d.ts +36 -0
  66. package/components/Reasoning/Reasoning.js +69 -0
  67. package/components/SelectionCard/SelectionCard.css +6 -2
  68. package/components/SelectionCard/SelectionCard.d.ts +5 -1
  69. package/components/Skeleton/Skeleton.css +2 -0
  70. package/components/Slider/Slider.d.ts +5 -1
  71. package/components/Spinner/Spinner.css +11 -0
  72. package/components/Spinner/Spinner.d.ts +2 -2
  73. package/components/Swatch/Swatch.css +52 -0
  74. package/components/Swatch/Swatch.d.ts +34 -0
  75. package/components/Swatch/Swatch.js +44 -0
  76. package/components/Table/Table.css +19 -0
  77. package/components/Table/Table.js +1 -1
  78. package/components/Textarea/Textarea.css +0 -43
  79. package/components/Textarea/Textarea.d.ts +5 -1
  80. package/components/Textarea/Textarea.js +37 -35
  81. package/components/Timeline/Timeline.d.ts +2 -0
  82. package/components/ToggleGroup/ToggleGroup.d.ts +10 -2
  83. package/components/ToggleSwitch/ToggleSwitch.css +7 -2
  84. package/components/ToggleSwitch/ToggleSwitch.d.ts +10 -2
  85. package/components/ToolCall/ToolCall.css +213 -0
  86. package/components/ToolCall/ToolCall.d.ts +43 -0
  87. package/components/ToolCall/ToolCall.js +89 -0
  88. package/components/registry.d.ts +47 -10
  89. package/components/registry.js +5 -1
  90. package/components/registry.json +523 -56
  91. package/components/registry.json.d.ts +523 -56
  92. package/components/registry.json.js +4 -1
  93. package/index.d.ts +12 -1
  94. package/index.js +24 -1
  95. package/package.json +8 -4
  96. package/tokens/registry.json +1 -0
  97. package/tokens/registry.json.d.ts +1 -0
  98. package/tokens/registry.json.js +1 -1
  99. package/tokens/tokens-motion.css +3 -1
@@ -14,19 +14,6 @@
14
14
  LABEL
15
15
  ============================================ */
16
16
 
17
- .ds-file-input__label {
18
- font-family: var(--font-paragraph-em-family);
19
- font-size: var(--font-paragraph-em-size);
20
- font-weight: var(--font-paragraph-em-weight);
21
- line-height: var(--font-paragraph-em-line-height);
22
- letter-spacing: var(--font-paragraph-em-letter-spacing);
23
- color: var(--color-text-primary);
24
- }
25
-
26
- .ds-file-input__required {
27
- color: var(--color-core-accent-coral);
28
- }
29
-
30
17
  /* ============================================
31
18
  DROPZONE
32
19
  ============================================ */
@@ -237,16 +224,6 @@
237
224
  HELPER TEXT
238
225
  ============================================ */
239
226
 
240
- .ds-file-input__helper {
241
- font-family: var(--font-paragraph-sm-family);
242
- font-size: var(--font-paragraph-sm-size);
243
- font-weight: var(--font-paragraph-sm-weight);
244
- line-height: var(--font-paragraph-sm-line-height);
245
- letter-spacing: var(--font-paragraph-sm-letter-spacing);
246
- color: var(--color-text-tertiary);
247
- margin: 0;
248
- }
249
-
250
227
  /* ============================================
251
228
  ERROR STATE
252
229
  ============================================ */
@@ -255,18 +232,10 @@
255
232
  border-color: var(--color-status-error-border);
256
233
  }
257
234
 
258
- .ds-file-input--error .ds-file-input__helper {
259
- color: var(--color-status-error-border);
260
- }
261
-
262
235
  /* ============================================
263
236
  DISABLED STATE
264
237
  ============================================ */
265
238
 
266
- .ds-file-input--disabled .ds-file-input__label {
267
- color: var(--color-input-text-disabled);
268
- }
269
-
270
239
  .ds-file-input--disabled .ds-file-input__dropzone {
271
240
  background-color: var(--color-input-bg-disabled);
272
241
  border-color: var(--color-input-border-disabled);
@@ -287,11 +256,6 @@
287
256
  padding: var(--padding-lg); /* 20px */
288
257
  }
289
258
 
290
- .ds-file-input--compact .ds-file-input__label {
291
- font-size: var(--font-paragraph-sm-size);
292
- line-height: var(--font-paragraph-sm-line-height);
293
- }
294
-
295
259
  .ds-file-input--compact .ds-file-input__dropzone-icon {
296
260
  --icon-size: var(--icon-size-sm);
297
261
  }
@@ -31,7 +31,11 @@ type FileInputOwnProps = {
31
31
  onFileRemove?: (id: string) => void;
32
32
  /** Additional CSS classes — applied to the wrapper, not the <input> */
33
33
  className?: string;
34
- /** @deprecated Pass the native `aria-label` attribute instead. */
34
+ /**
35
+ * Legacy accessible-name prop.
36
+ *
37
+ * @deprecated Pass the native `aria-label` attribute instead.
38
+ */
35
39
  ariaLabel?: string;
36
40
  };
37
41
  export interface FileInputProps extends FileInputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof FileInputOwnProps | 'type'> {
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useId } from "react";
3
+ import { Field } from "../Field/Field.js";
3
4
  import "./FileInput.css";
4
5
  import "../../fonts/material-symbols.css";
5
6
  const formatBytes = (bytes) => {
@@ -80,114 +81,121 @@ const FileInput = React.forwardRef(
80
81
  isDragging ? `${baseClass}--dragging` : "",
81
82
  className
82
83
  ].filter(Boolean).join(" ");
83
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
84
- label && /* @__PURE__ */ jsxs("label", { className: `${baseClass}__label`, htmlFor: inputId, children: [
84
+ return /* @__PURE__ */ jsxs(
85
+ Field,
86
+ {
87
+ className: classes,
85
88
  label,
86
- required && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__required`, "aria-hidden": "true", children: [
87
- " ",
88
- "*"
89
- ] })
90
- ] }),
91
- /* @__PURE__ */ jsxs(
92
- "div",
93
- {
94
- className: `${baseClass}__dropzone`,
95
- role: "button",
96
- tabIndex: disabled ? -1 : 0,
97
- "aria-label": ariaLabel || rest["aria-label"] || placeholder,
98
- "aria-describedby": helperText ? `${inputId}-helper` : void 0,
99
- "aria-disabled": disabled || void 0,
100
- onClick: openPicker,
101
- onKeyDown: handleKeyDown,
102
- onDrop: handleDrop,
103
- onDragOver: handleDragOver,
104
- onDragLeave: handleDragLeave,
105
- children: [
106
- /* @__PURE__ */ jsx(
107
- "span",
108
- {
109
- className: `${baseClass}__dropzone-icon material-symbols-rounded`,
110
- "aria-hidden": "true",
111
- children: "upload_file"
112
- }
113
- ),
114
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__dropzone-text`, children: placeholder }),
115
- accept && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__dropzone-hint`, children: [
116
- "Accepts ",
117
- accept
118
- ] }),
119
- /* @__PURE__ */ jsx(
120
- "input",
121
- {
122
- ...rest,
123
- ref: setInputRef,
124
- id: inputId,
125
- name,
126
- type: "file",
127
- className: `${baseClass}__native`,
128
- accept,
129
- multiple,
130
- disabled,
131
- required,
132
- tabIndex: -1,
133
- onChange: handleChange
134
- }
135
- )
136
- ]
137
- }
138
- ),
139
- files.length > 0 && /* @__PURE__ */ jsx("ul", { className: `${baseClass}__list`, children: files.map((file) => /* @__PURE__ */ jsxs(
140
- "li",
141
- {
142
- className: [`${baseClass}__file`, file.error ? `${baseClass}__file--error` : ""].filter(Boolean).join(" "),
143
- children: [
144
- /* @__PURE__ */ jsx(
145
- "span",
146
- {
147
- className: `${baseClass}__file-icon material-symbols-rounded`,
148
- "aria-hidden": "true",
149
- children: file.error ? "error" : "description"
150
- }
151
- ),
152
- /* @__PURE__ */ jsxs("span", { className: `${baseClass}__file-details`, children: [
153
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-name`, children: file.name }),
154
- file.error ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-error`, children: file.error }) : file.size !== void 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-size`, children: formatBytes(file.size) }),
155
- file.error === void 0 && file.progress !== void 0 && /* @__PURE__ */ jsx(
156
- "span",
157
- {
158
- className: `${baseClass}__progress`,
159
- role: "progressbar",
160
- "aria-valuenow": file.progress,
161
- "aria-valuemin": 0,
162
- "aria-valuemax": 100,
163
- "aria-label": `Uploading ${file.name}`,
164
- children: /* @__PURE__ */ jsx(
89
+ helperText,
90
+ error,
91
+ required,
92
+ disabled,
93
+ size,
94
+ id: inputId,
95
+ children: [
96
+ /* @__PURE__ */ jsxs(
97
+ "div",
98
+ {
99
+ className: `${baseClass}__dropzone`,
100
+ role: "button",
101
+ tabIndex: disabled ? -1 : 0,
102
+ "aria-label": ariaLabel || rest["aria-label"] || placeholder,
103
+ "aria-describedby": helperText ? `${inputId}-helper` : void 0,
104
+ "aria-invalid": error || void 0,
105
+ "aria-disabled": disabled || void 0,
106
+ onClick: openPicker,
107
+ onKeyDown: handleKeyDown,
108
+ onDrop: handleDrop,
109
+ onDragOver: handleDragOver,
110
+ onDragLeave: handleDragLeave,
111
+ children: [
112
+ /* @__PURE__ */ jsx(
113
+ "span",
114
+ {
115
+ className: `${baseClass}__dropzone-icon material-symbols-rounded`,
116
+ "aria-hidden": "true",
117
+ children: "upload_file"
118
+ }
119
+ ),
120
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__dropzone-text`, children: placeholder }),
121
+ accept && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__dropzone-hint`, children: [
122
+ "Accepts ",
123
+ accept
124
+ ] })
125
+ ]
126
+ }
127
+ ),
128
+ /* @__PURE__ */ jsx(
129
+ "input",
130
+ {
131
+ ...rest,
132
+ ref: setInputRef,
133
+ id: inputId,
134
+ name,
135
+ type: "file",
136
+ className: `${baseClass}__native`,
137
+ accept,
138
+ multiple,
139
+ disabled,
140
+ required,
141
+ tabIndex: -1,
142
+ "aria-label": label ? void 0 : ariaLabel || rest["aria-label"] || placeholder,
143
+ onChange: handleChange
144
+ }
145
+ ),
146
+ files.length > 0 && /* @__PURE__ */ jsx("ul", { className: `${baseClass}__list`, children: files.map((file) => /* @__PURE__ */ jsxs(
147
+ "li",
148
+ {
149
+ className: [`${baseClass}__file`, file.error ? `${baseClass}__file--error` : ""].filter(Boolean).join(" "),
150
+ children: [
151
+ /* @__PURE__ */ jsx(
152
+ "span",
153
+ {
154
+ className: `${baseClass}__file-icon material-symbols-rounded`,
155
+ "aria-hidden": "true",
156
+ children: file.error ? "error" : "description"
157
+ }
158
+ ),
159
+ /* @__PURE__ */ jsxs("span", { className: `${baseClass}__file-details`, children: [
160
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-name`, children: file.name }),
161
+ file.error ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-error`, children: file.error }) : file.size !== void 0 && /* @__PURE__ */ jsx("span", { className: `${baseClass}__file-size`, children: formatBytes(file.size) }),
162
+ file.error === void 0 && file.progress !== void 0 && /* @__PURE__ */ jsx(
165
163
  "span",
166
164
  {
167
- className: `${baseClass}__progress-fill`,
168
- style: { width: `${Math.min(100, Math.max(0, file.progress))}%` }
165
+ className: `${baseClass}__progress`,
166
+ role: "progressbar",
167
+ "aria-valuenow": file.progress,
168
+ "aria-valuemin": 0,
169
+ "aria-valuemax": 100,
170
+ "aria-label": `Uploading ${file.name}`,
171
+ children: /* @__PURE__ */ jsx(
172
+ "span",
173
+ {
174
+ className: `${baseClass}__progress-fill`,
175
+ style: { width: `${Math.min(100, Math.max(0, file.progress))}%` }
176
+ }
177
+ )
169
178
  }
170
179
  )
171
- }
172
- )
173
- ] }),
174
- onFileRemove && /* @__PURE__ */ jsx(
175
- "button",
176
- {
177
- type: "button",
178
- className: `${baseClass}__file-remove`,
179
- onClick: () => onFileRemove(file.id),
180
- disabled,
181
- "aria-label": `Remove ${file.name}`,
182
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
183
- }
184
- )
185
- ]
186
- },
187
- file.id
188
- )) }),
189
- helperText && /* @__PURE__ */ jsx("p", { className: `${baseClass}__helper`, id: `${inputId}-helper`, children: helperText })
190
- ] });
180
+ ] }),
181
+ onFileRemove && /* @__PURE__ */ jsx(
182
+ "button",
183
+ {
184
+ type: "button",
185
+ className: `${baseClass}__file-remove`,
186
+ onClick: () => onFileRemove(file.id),
187
+ disabled,
188
+ "aria-label": `Remove ${file.name}`,
189
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-rounded", "aria-hidden": "true", children: "close" })
190
+ }
191
+ )
192
+ ]
193
+ },
194
+ file.id
195
+ )) })
196
+ ]
197
+ }
198
+ );
191
199
  }
192
200
  );
193
201
  FileInput.displayName = "FileInput";
@@ -10,23 +10,6 @@
10
10
  width: 100%;
11
11
  }
12
12
 
13
- /* ============================================
14
- LABEL
15
- ============================================ */
16
-
17
- .ds-input__label {
18
- font-family: var(--font-paragraph-em-family);
19
- font-size: var(--font-paragraph-em-size);
20
- font-weight: var(--font-paragraph-em-weight);
21
- line-height: var(--font-paragraph-em-line-height);
22
- letter-spacing: var(--font-paragraph-em-letter-spacing);
23
- color: var(--color-text-primary);
24
- }
25
-
26
- .ds-input__required {
27
- color: var(--color-core-accent-coral);
28
- }
29
-
30
13
  /* ============================================
31
14
  FIELD WRAPPER
32
15
  ============================================ */
@@ -103,20 +86,6 @@
103
86
  padding-right: calc(var(--padding-md) + 24px + var(--gap-sm)); /* 16px + 24px icon + 8px gap */
104
87
  }
105
88
 
106
- /* ============================================
107
- HELPER TEXT
108
- ============================================ */
109
-
110
- .ds-input__helper {
111
- font-family: var(--font-paragraph-sm-family);
112
- font-size: var(--font-paragraph-sm-size);
113
- font-weight: var(--font-paragraph-sm-weight);
114
- line-height: var(--font-paragraph-sm-line-height);
115
- letter-spacing: var(--font-paragraph-sm-letter-spacing);
116
- color: var(--color-text-tertiary);
117
- margin: 0;
118
- }
119
-
120
89
  /* ============================================
121
90
  ERROR STATE
122
91
  ============================================ */
@@ -129,18 +98,10 @@
129
98
  border-color: var(--color-status-error-border);
130
99
  }
131
100
 
132
- .ds-input--error .ds-input__helper {
133
- color: var(--color-status-error-border);
134
- }
135
-
136
101
  /* ============================================
137
102
  DISABLED STATE
138
103
  ============================================ */
139
104
 
140
- .ds-input--disabled .ds-input__label {
141
- color: var(--color-input-text-disabled);
142
- }
143
-
144
105
  .ds-input--disabled .ds-input__field {
145
106
  background-color: var(--color-input-bg-disabled);
146
107
  border-color: var(--color-input-border-disabled);
@@ -187,7 +148,3 @@
187
148
  padding-right: calc(var(--padding-sm-md) + 20px + var(--gap-sm)); /* 12px + 20px icon + 8px gap */
188
149
  }
189
150
 
190
- .ds-input--compact .ds-input__label {
191
- font-size: var(--font-paragraph-sm-size);
192
- line-height: var(--font-paragraph-sm-line-height);
193
- }
@@ -25,7 +25,11 @@ type InputOwnProps = {
25
25
  onValueChange?: (value: string) => void;
26
26
  /** Additional CSS classes — applied to the wrapper, not the <input> */
27
27
  className?: string;
28
- /** @deprecated Pass the native `aria-label` attribute instead. */
28
+ /**
29
+ * Legacy accessible-name prop.
30
+ *
31
+ * @deprecated Pass the native `aria-label` attribute instead.
32
+ */
29
33
  ariaLabel?: string;
30
34
  };
31
35
  export interface InputProps extends InputOwnProps, Omit<React.ComponentPropsWithoutRef<'input'>, keyof InputOwnProps> {
@@ -1,5 +1,6 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
+ import { Field } from "../Field/Field.js";
3
4
  import "./Input.css";
4
5
  import "../../fonts/material-symbols.css";
5
6
  const Input = React.forwardRef(
@@ -38,53 +39,57 @@ const Input = React.forwardRef(
38
39
  onValueChange?.(e.target.value);
39
40
  };
40
41
  const inputId = id || name || label?.toLowerCase().replace(/\s+/g, "-");
41
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
42
- label && /* @__PURE__ */ jsxs("label", { className: `${baseClass}__label`, htmlFor: inputId, children: [
42
+ const describedBy = helperText ? `${inputId}-helper` : rest["aria-describedby"];
43
+ return /* @__PURE__ */ jsx(
44
+ Field,
45
+ {
46
+ className: classes,
43
47
  label,
44
- required && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__required`, "aria-hidden": "true", children: [
45
- " ",
46
- "*"
48
+ helperText,
49
+ error,
50
+ required,
51
+ disabled,
52
+ size,
53
+ id: inputId,
54
+ children: /* @__PURE__ */ jsxs("div", { className: `${baseClass}__field-wrapper`, children: [
55
+ iconLeft && /* @__PURE__ */ jsx(
56
+ "span",
57
+ {
58
+ className: `${baseClass}__icon ${baseClass}__icon--left material-symbols-rounded`,
59
+ "aria-hidden": "true",
60
+ children: iconLeft
61
+ }
62
+ ),
63
+ /* @__PURE__ */ jsx(
64
+ "input",
65
+ {
66
+ ...rest,
67
+ ref,
68
+ className: `${baseClass}__field`,
69
+ type,
70
+ id: inputId,
71
+ name,
72
+ value,
73
+ placeholder,
74
+ disabled,
75
+ required,
76
+ "aria-label": ariaLabel || rest["aria-label"] || label,
77
+ "aria-invalid": error,
78
+ "aria-describedby": describedBy,
79
+ onChange: handleChange
80
+ }
81
+ ),
82
+ iconRight && /* @__PURE__ */ jsx(
83
+ "span",
84
+ {
85
+ className: `${baseClass}__icon ${baseClass}__icon--right material-symbols-rounded`,
86
+ "aria-hidden": "true",
87
+ children: iconRight
88
+ }
89
+ )
47
90
  ] })
48
- ] }),
49
- /* @__PURE__ */ jsxs("div", { className: `${baseClass}__field-wrapper`, children: [
50
- iconLeft && /* @__PURE__ */ jsx(
51
- "span",
52
- {
53
- className: `${baseClass}__icon ${baseClass}__icon--left material-symbols-rounded`,
54
- "aria-hidden": "true",
55
- children: iconLeft
56
- }
57
- ),
58
- /* @__PURE__ */ jsx(
59
- "input",
60
- {
61
- ...rest,
62
- ref,
63
- className: `${baseClass}__field`,
64
- type,
65
- id: inputId,
66
- name,
67
- value,
68
- placeholder,
69
- disabled,
70
- required,
71
- "aria-label": ariaLabel || rest["aria-label"] || label,
72
- "aria-invalid": error,
73
- "aria-describedby": helperText ? `${inputId}-helper` : rest["aria-describedby"],
74
- onChange: handleChange
75
- }
76
- ),
77
- iconRight && /* @__PURE__ */ jsx(
78
- "span",
79
- {
80
- className: `${baseClass}__icon ${baseClass}__icon--right material-symbols-rounded`,
81
- "aria-hidden": "true",
82
- children: iconRight
83
- }
84
- )
85
- ] }),
86
- helperText && /* @__PURE__ */ jsx("p", { className: `${baseClass}__helper`, id: `${inputId}-helper`, children: helperText })
87
- ] });
91
+ }
92
+ );
88
93
  }
89
94
  );
90
95
  Input.displayName = "Input";
@@ -0,0 +1,28 @@
1
+ /* ============================================
2
+ KBD COMPONENT
3
+ A single keyboard key rendered as a keycap
4
+ ============================================ */
5
+
6
+ /* Base */
7
+ .ds-kbd {
8
+ display: inline-flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ min-width: 24px;
12
+ padding: var(--padding-xxxs) var(--padding-xs);
13
+ border: var(--border-xs) solid var(--color-bg-container-border);
14
+ border-radius: var(--radius-xs);
15
+ background-color: var(--color-bg-container-primary);
16
+ font-family: var(--font-paragraph-sm-family);
17
+ font-size: var(--font-paragraph-sm-size);
18
+ font-weight: var(--font-paragraph-sm-em-weight);
19
+ line-height: var(--font-paragraph-sm-line-height);
20
+ color: var(--color-text-tertiary);
21
+ white-space: nowrap;
22
+ }
23
+
24
+ /* Sizes */
25
+ .ds-kbd--compact {
26
+ min-width: 20px;
27
+ padding: var(--padding-xxxs) var(--padding-xxs);
28
+ }
@@ -0,0 +1,19 @@
1
+ import { default as React } from 'react';
2
+ /** Props owned by Kbd itself — everything else falls through to the <kbd> element. */
3
+ type KbdOwnProps = {
4
+ /** Key size */
5
+ size?: 'default' | 'compact';
6
+ /** Additional CSS classes */
7
+ className?: string;
8
+ /** The key legend — e.g. ⌘, K, Esc, Shift */
9
+ children?: React.ReactNode;
10
+ };
11
+ export interface KbdProps extends KbdOwnProps, Omit<React.ComponentPropsWithoutRef<'kbd'>, keyof KbdOwnProps> {
12
+ }
13
+ /**
14
+ * Kbd renders a single keyboard key as a keycap, for shortcut hints in
15
+ * menus and docs prose. Compose several for a chord: `<Kbd>⌘</Kbd><Kbd>K</Kbd>`.
16
+ * Purely presentational — safe to render from a Server Component.
17
+ */
18
+ export declare const Kbd: React.ForwardRefExoticComponent<KbdProps & React.RefAttributes<HTMLElement>>;
19
+ export {};
@@ -0,0 +1,14 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import "./Kbd.css";
4
+ const Kbd = React.forwardRef(
5
+ ({ size = "default", className = "", children, ...rest }, ref) => {
6
+ const baseClass = "ds-kbd";
7
+ const classes = [baseClass, `${baseClass}--${size}`, className].filter(Boolean).join(" ");
8
+ return /* @__PURE__ */ jsx("kbd", { ...rest, ref, className: classes, children });
9
+ }
10
+ );
11
+ Kbd.displayName = "Kbd";
12
+ export {
13
+ Kbd
14
+ };
@@ -13,6 +13,7 @@ export interface LinkListItem {
13
13
  icon?: string;
14
14
  }
15
15
  export interface LinkListProps {
16
+ /** Links to render, in display order — each opens in a new tab */
16
17
  items: LinkListItem[];
17
18
  /** Additional CSS classes */
18
19
  className?: string;
@@ -19,10 +19,4 @@ export interface PopoverProps {
19
19
  /** Additional CSS classes for the popover panel */
20
20
  className?: string;
21
21
  }
22
- /**
23
- * Popover component for contextual overlays.
24
- * Wraps a trigger element and displays content in a
25
- * positioned panel. Supports click and hover triggers
26
- * with automatic outside-click dismissal.
27
- */
28
22
  export declare const Popover: ({ children, content, size, position, trigger, open: controlledOpen, onOpenChange, ariaLabel, className, }: PopoverProps) => React.JSX.Element;
@@ -1,6 +1,8 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useState, useRef, useCallback, useEffect } from "react";
2
+ import React, { useState, useRef, useCallback, useEffect } from "react";
3
3
  import "./Popover.css";
4
+ const INTERACTIVE_TAGS = /* @__PURE__ */ new Set(["button", "a", "input", "select", "textarea", "summary"]);
5
+ const canCarryPopoverSemantics = (el) => typeof el.type === "string" ? INTERACTIVE_TAGS.has(el.type) : true;
4
6
  const Popover = ({
5
7
  children,
6
8
  content,
@@ -73,26 +75,30 @@ const Popover = ({
73
75
  onMouseEnter: handleMouseEnter,
74
76
  onMouseLeave: handleMouseLeave,
75
77
  children: [
76
- /* @__PURE__ */ jsx(
77
- "div",
78
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__trigger`, onClick: handleClick, children: React.isValidElement(children) && canCarryPopoverSemantics(children) ? React.cloneElement(children, {
79
+ "aria-expanded": isOpen,
80
+ "aria-haspopup": "dialog",
81
+ ...ariaLabel ? { "aria-label": ariaLabel } : {}
82
+ }) : trigger === "click" ? /* @__PURE__ */ jsx(
83
+ "button",
78
84
  {
79
- className: `${baseClass}__trigger`,
80
- onClick: handleClick,
81
- role: trigger === "click" ? "button" : void 0,
82
- tabIndex: trigger === "click" ? 0 : void 0,
85
+ type: "button",
83
86
  "aria-expanded": isOpen,
84
- "aria-label": ariaLabel,
85
87
  "aria-haspopup": "dialog",
86
- onKeyDown: (e) => {
87
- if (trigger === "click" && (e.key === "Enter" || e.key === " ")) {
88
- e.preventDefault();
89
- setOpen(!isOpen);
90
- }
91
- },
88
+ "aria-label": ariaLabel,
92
89
  children
93
90
  }
94
- ),
95
- /* @__PURE__ */ jsx("div", { className: panelClasses, role: "dialog", "aria-hidden": !isOpen, children: content })
91
+ ) : children }),
92
+ /* @__PURE__ */ jsx(
93
+ "div",
94
+ {
95
+ className: panelClasses,
96
+ role: "dialog",
97
+ "aria-label": ariaLabel || "Popover",
98
+ "aria-hidden": !isOpen,
99
+ children: content
100
+ }
101
+ )
96
102
  ]
97
103
  }
98
104
  );
@@ -14,7 +14,7 @@ const ProgressBar = ({
14
14
  `${baseClass}--${size}`,
15
15
  className
16
16
  ].filter(Boolean).join(" ");
17
- return /* @__PURE__ */ jsxs("div", { className: classes, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": ariaLabel, children: [
17
+ return /* @__PURE__ */ jsxs("div", { className: classes, role: "progressbar", "aria-valuenow": clamped, "aria-valuemin": 0, "aria-valuemax": 100, "aria-label": ariaLabel || "Progress", children: [
18
18
  /* @__PURE__ */ jsx("div", { className: `${baseClass}__track`, children: /* @__PURE__ */ jsx("div", { className: `${baseClass}__fill`, style: { width: `${clamped}%` } }) }),
19
19
  showLabel && /* @__PURE__ */ jsxs("span", { className: `${baseClass}__label`, children: [
20
20
  clamped,