@cdx-ui/components 0.2.3 → 0.2.5

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 (296) hide show
  1. package/AGENTS.md +24 -0
  2. package/README.md +2 -0
  3. package/coverage.svg +1 -1
  4. package/docs/components/accordion/content.md +538 -0
  5. package/docs/components/alert/content.md +203 -0
  6. package/docs/components/alert-dialog/content.md +319 -0
  7. package/docs/components/avatar/content.md +368 -0
  8. package/docs/components/avatar/design.md +202 -0
  9. package/docs/components/badge/content.md +225 -0
  10. package/docs/components/bottom-sheet/content.md +353 -0
  11. package/docs/components/bottom-sheet/design.md +249 -0
  12. package/docs/components/box/content.md +141 -0
  13. package/docs/components/breadcrumbs/content.md +169 -0
  14. package/docs/components/button/content.md +398 -0
  15. package/docs/components/button/design.md +258 -0
  16. package/docs/components/calendar/content.md +137 -0
  17. package/docs/components/card/content.md +283 -0
  18. package/docs/components/carousel/content.md +417 -0
  19. package/docs/components/checkbox/content.md +236 -0
  20. package/docs/components/checkbox/design.md +214 -0
  21. package/docs/components/chip/content.md +439 -0
  22. package/docs/components/chip/design.md +418 -0
  23. package/docs/components/color-area/content.md +84 -0
  24. package/docs/components/color-field/content.md +132 -0
  25. package/docs/components/color-picker/content.md +209 -0
  26. package/docs/components/color-slider/content.md +118 -0
  27. package/docs/components/color-swatch/content.md +62 -0
  28. package/docs/components/color-swatch-picker/content.md +124 -0
  29. package/docs/components/content.md +98 -0
  30. package/docs/components/currency-input/content.md +205 -0
  31. package/docs/components/data-grid/content.md +521 -0
  32. package/docs/components/date-field/content.md +147 -0
  33. package/docs/components/date-picker/content.md +360 -0
  34. package/docs/components/date-range-picker/content.md +301 -0
  35. package/docs/components/date-time-field/content.md +157 -0
  36. package/docs/components/date-time-list/content.md +105 -0
  37. package/docs/components/date-time-picker/content.md +272 -0
  38. package/docs/components/dialog/content.md +436 -0
  39. package/docs/components/dialog/design.md +192 -0
  40. package/docs/components/divider/content.md +112 -0
  41. package/docs/components/drawer/content.md +428 -0
  42. package/docs/components/field/content.md +277 -0
  43. package/docs/components/form/content.md +62 -0
  44. package/docs/components/full-screen-dialog/content.md +881 -0
  45. package/docs/components/heading/content.md +55 -0
  46. package/docs/components/icon/content.md +190 -0
  47. package/docs/components/icon-button/content.md +200 -0
  48. package/docs/components/icon-button/design.md +165 -0
  49. package/docs/components/image/content.md +104 -0
  50. package/docs/components/input/content.md +346 -0
  51. package/docs/components/input/design.md +389 -0
  52. package/docs/components/insurance-disclosure/content.md +103 -0
  53. package/docs/components/link/content.md +180 -0
  54. package/docs/components/link/design.md +160 -0
  55. package/docs/components/list-item/content.md +695 -0
  56. package/docs/components/menu/content.md +1254 -0
  57. package/docs/components/month-calendar/content.md +84 -0
  58. package/docs/components/month-picker/content.md +273 -0
  59. package/docs/components/otp-input/content.md +327 -0
  60. package/docs/components/progress-bar/content.md +87 -0
  61. package/docs/components/progress-circle/content.md +176 -0
  62. package/docs/components/progress-segmented/content.md +119 -0
  63. package/docs/components/quick-action-button/content.md +154 -0
  64. package/docs/components/quick-action-button/design.md +170 -0
  65. package/docs/components/radio/content.md +224 -0
  66. package/docs/components/range-calendar/content.md +123 -0
  67. package/docs/components/reorderable-list/content.md +311 -0
  68. package/docs/components/scoped-theme/content.md +47 -0
  69. package/docs/components/scroll-shadow/content.md +693 -0
  70. package/docs/components/select/content.md +473 -0
  71. package/docs/components/select/design.md +301 -0
  72. package/docs/components/skeleton/content.md +104 -0
  73. package/docs/components/slider/content.md +423 -0
  74. package/docs/components/stack/content.md +161 -0
  75. package/docs/components/switch/content.md +150 -0
  76. package/docs/components/switch/design.md +208 -0
  77. package/docs/components/table/content.md +548 -0
  78. package/docs/components/table-pagination/content.md +177 -0
  79. package/docs/components/tabs/content.md +297 -0
  80. package/docs/components/text/content.md +142 -0
  81. package/docs/components/textarea/content.md +222 -0
  82. package/docs/components/tile/content.md +702 -0
  83. package/docs/components/toast/content.md +456 -0
  84. package/docs/components/toggle-button/content.md +505 -0
  85. package/docs/components/tooltip/content.md +350 -0
  86. package/docs/components/virtualized-list/content.md +250 -0
  87. package/docs/foundations/content.md +9 -0
  88. package/docs/foundations/expressions/icons/content.md +19 -0
  89. package/docs/foundations/styles/tokens/all-tokens/content.md +192 -0
  90. package/docs/foundations/styles/tokens/what-are-tokens/content.md +33 -0
  91. package/docs/foundations/styles/tokens/what-are-tokens/design.md +33 -0
  92. package/docs/getting-started/content.md +4 -0
  93. package/docs/getting-started/designers/content.md +13 -0
  94. package/docs/getting-started/designers/design.md +13 -0
  95. package/docs/getting-started/developers/accessibility/content.md +31 -0
  96. package/docs/getting-started/developers/content.md +612 -0
  97. package/docs/getting-started/developers/styling/content.md +195 -0
  98. package/docs/llms.txt +195 -0
  99. package/docs/patterns/account-management/content.md +75 -0
  100. package/docs/patterns/authentication/content.md +50 -0
  101. package/docs/patterns/content.md +10 -0
  102. package/docs/patterns/dashboard/content.md +34 -0
  103. package/docs/patterns/empty-states/content.md +32 -0
  104. package/docs/patterns/forms/content.md +108 -0
  105. package/docs/patterns/onboarding/content.md +69 -0
  106. package/docs/patterns/payments/content.md +35 -0
  107. package/docs/patterns/settings/content.md +72 -0
  108. package/docs/resources/content.md +9 -0
  109. package/docs/resources/figma-libraries/content.md +42 -0
  110. package/docs/resources/release-notes/0.0.1-beta.118/content.md +27 -0
  111. package/docs/resources/release-notes/0.0.1-beta.122/content.md +21 -0
  112. package/docs/resources/release-notes/0.0.1-beta.124/content.md +16 -0
  113. package/docs/resources/release-notes/0.0.1-beta.126/content.md +10 -0
  114. package/docs/resources/release-notes/0.0.1-beta.127/content.md +14 -0
  115. package/docs/resources/release-notes/0.0.1-beta.128/content.md +9 -0
  116. package/docs/resources/release-notes/0.0.1-beta.129/content.md +18 -0
  117. package/docs/resources/release-notes/0.0.1-beta.130/content.md +18 -0
  118. package/docs/resources/release-notes/0.0.1-beta.133/content.md +7 -0
  119. package/docs/resources/release-notes/0.0.1-beta.134/content.md +17 -0
  120. package/docs/resources/release-notes/0.0.1-beta.136/content.md +7 -0
  121. package/docs/resources/release-notes/0.0.1-beta.137/content.md +10 -0
  122. package/docs/resources/release-notes/0.0.1-beta.138/content.md +7 -0
  123. package/docs/resources/release-notes/0.0.1-beta.139/content.md +13 -0
  124. package/docs/resources/release-notes/0.0.1-beta.140/content.md +26 -0
  125. package/docs/resources/release-notes/0.0.1-beta.141/content.md +63 -0
  126. package/docs/resources/release-notes/0.0.1-beta.142/content.md +7 -0
  127. package/docs/resources/release-notes/0.0.1-beta.143/content.md +12 -0
  128. package/docs/resources/release-notes/0.0.1-beta.144/content.md +7 -0
  129. package/docs/resources/release-notes/0.0.1-beta.145/content.md +7 -0
  130. package/docs/resources/release-notes/0.0.1-beta.147/content.md +10 -0
  131. package/docs/resources/release-notes/0.0.1-beta.149/content.md +10 -0
  132. package/docs/resources/release-notes/0.0.1-beta.150/content.md +13 -0
  133. package/docs/resources/release-notes/0.0.1-beta.152/content.md +7 -0
  134. package/docs/resources/release-notes/0.0.1-beta.154/content.md +15 -0
  135. package/docs/resources/release-notes/0.0.1-beta.156/content.md +7 -0
  136. package/docs/resources/release-notes/0.0.1-beta.158/content.md +9 -0
  137. package/docs/resources/release-notes/0.0.1-beta.159/content.md +11 -0
  138. package/docs/resources/release-notes/0.0.1-beta.160/content.md +7 -0
  139. package/docs/resources/release-notes/0.0.1-beta.161/content.md +7 -0
  140. package/docs/resources/release-notes/0.0.1-beta.163/content.md +7 -0
  141. package/docs/resources/release-notes/0.0.1-beta.164/content.md +9 -0
  142. package/docs/resources/release-notes/0.0.1-beta.166/content.md +17 -0
  143. package/docs/resources/release-notes/0.0.1-beta.167/content.md +7 -0
  144. package/docs/resources/release-notes/0.0.1-beta.168/content.md +7 -0
  145. package/docs/resources/release-notes/0.0.1-beta.169/content.md +7 -0
  146. package/docs/resources/release-notes/0.0.1-beta.171/content.md +16 -0
  147. package/docs/resources/release-notes/0.0.1-beta.172/content.md +24 -0
  148. package/docs/resources/release-notes/0.0.1-beta.173/content.md +15 -0
  149. package/docs/resources/release-notes/0.0.1-beta.174/content.md +7 -0
  150. package/docs/resources/release-notes/0.0.1-beta.176/content.md +32 -0
  151. package/docs/resources/release-notes/0.0.1-beta.179/content.md +11 -0
  152. package/docs/resources/release-notes/0.0.1-beta.182/content.md +13 -0
  153. package/docs/resources/release-notes/0.0.1-beta.185/content.md +7 -0
  154. package/docs/resources/release-notes/0.0.1-beta.187/content.md +27 -0
  155. package/docs/resources/release-notes/0.1.1/content.md +23 -0
  156. package/docs/resources/release-notes/0.1.2/content.md +7 -0
  157. package/docs/resources/release-notes/0.1.4/content.md +19 -0
  158. package/docs/resources/release-notes/0.1.5/content.md +7 -0
  159. package/docs/resources/release-notes/0.1.6/content.md +7 -0
  160. package/docs/resources/release-notes/0.2.0/content.md +31 -0
  161. package/docs/resources/release-notes/0.2.1/content.md +17 -0
  162. package/docs/resources/release-notes/0.2.2/content.md +11 -0
  163. package/docs/resources/release-notes/0.2.3/content.md +15 -0
  164. package/docs/resources/release-notes/0.2.4/content.md +17 -0
  165. package/docs/resources/release-notes/content.md +65 -0
  166. package/lib/commonjs/components/Box/Box.js +9 -1
  167. package/lib/commonjs/components/Box/Box.js.map +1 -1
  168. package/lib/commonjs/components/Breadcrumbs/index.js +28 -3
  169. package/lib/commonjs/components/Breadcrumbs/index.js.map +1 -1
  170. package/lib/commonjs/components/Breadcrumbs/styles.js +27 -0
  171. package/lib/commonjs/components/Breadcrumbs/styles.js.map +1 -1
  172. package/lib/commonjs/components/DateTimeList/index.js +11 -26
  173. package/lib/commonjs/components/DateTimeList/index.js.map +1 -1
  174. package/lib/commonjs/components/DateTimeList/styles.js +2 -12
  175. package/lib/commonjs/components/DateTimeList/styles.js.map +1 -1
  176. package/lib/commonjs/components/ListItem/index.js.map +1 -1
  177. package/lib/commonjs/components/ListItem/styles.js +2 -1
  178. package/lib/commonjs/components/ListItem/styles.js.map +1 -1
  179. package/lib/commonjs/components/Menu/index.js +11 -5
  180. package/lib/commonjs/components/Menu/index.js.map +1 -1
  181. package/lib/commonjs/components/ScrollShadow/gradient.js +29 -0
  182. package/lib/commonjs/components/ScrollShadow/gradient.js.map +1 -0
  183. package/lib/commonjs/components/ScrollShadow/index.js +171 -44
  184. package/lib/commonjs/components/ScrollShadow/index.js.map +1 -1
  185. package/lib/commonjs/components/ScrollShadow/styles.js +58 -14
  186. package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -1
  187. package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js +63 -0
  188. package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
  189. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +48 -8
  190. package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
  191. package/lib/commonjs/components/Stack/HStack.js +6 -2
  192. package/lib/commonjs/components/Stack/HStack.js.map +1 -1
  193. package/lib/commonjs/components/Stack/VStack.js +6 -2
  194. package/lib/commonjs/components/Stack/VStack.js.map +1 -1
  195. package/lib/commonjs/components/Tabs/index.js +16 -23
  196. package/lib/commonjs/components/Tabs/index.js.map +1 -1
  197. package/lib/commonjs/components/Tabs/styles.js +1 -10
  198. package/lib/commonjs/components/Tabs/styles.js.map +1 -1
  199. package/lib/commonjs/figma/ListItem.SectionHeader.figma.js +20 -5
  200. package/lib/commonjs/figma/ListItem.SectionHeader.figma.js.map +1 -1
  201. package/lib/commonjs/index.js +10 -10
  202. package/lib/commonjs/index.js.map +1 -1
  203. package/lib/module/components/Box/Box.js +9 -1
  204. package/lib/module/components/Box/Box.js.map +1 -1
  205. package/lib/module/components/Breadcrumbs/index.js +29 -4
  206. package/lib/module/components/Breadcrumbs/index.js.map +1 -1
  207. package/lib/module/components/Breadcrumbs/styles.js +26 -0
  208. package/lib/module/components/Breadcrumbs/styles.js.map +1 -1
  209. package/lib/module/components/DateTimeList/index.js +12 -27
  210. package/lib/module/components/DateTimeList/index.js.map +1 -1
  211. package/lib/module/components/DateTimeList/styles.js +1 -11
  212. package/lib/module/components/DateTimeList/styles.js.map +1 -1
  213. package/lib/module/components/ListItem/index.js.map +1 -1
  214. package/lib/module/components/ListItem/styles.js +2 -1
  215. package/lib/module/components/ListItem/styles.js.map +1 -1
  216. package/lib/module/components/Menu/index.js +11 -5
  217. package/lib/module/components/Menu/index.js.map +1 -1
  218. package/lib/module/components/ScrollShadow/gradient.js +24 -0
  219. package/lib/module/components/ScrollShadow/gradient.js.map +1 -0
  220. package/lib/module/components/ScrollShadow/index.js +170 -48
  221. package/lib/module/components/ScrollShadow/index.js.map +1 -1
  222. package/lib/module/components/ScrollShadow/styles.js +57 -13
  223. package/lib/module/components/ScrollShadow/styles.js.map +1 -1
  224. package/lib/module/components/ScrollShadow/useComposedScrollHandler.js +57 -0
  225. package/lib/module/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
  226. package/lib/module/components/ScrollShadow/useScrollShadow.js +46 -8
  227. package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
  228. package/lib/module/components/Stack/HStack.js +6 -2
  229. package/lib/module/components/Stack/HStack.js.map +1 -1
  230. package/lib/module/components/Stack/VStack.js +6 -2
  231. package/lib/module/components/Stack/VStack.js.map +1 -1
  232. package/lib/module/components/Tabs/index.js +17 -24
  233. package/lib/module/components/Tabs/index.js.map +1 -1
  234. package/lib/module/components/Tabs/styles.js +0 -9
  235. package/lib/module/components/Tabs/styles.js.map +1 -1
  236. package/lib/module/figma/ListItem.SectionHeader.figma.js +20 -5
  237. package/lib/module/figma/ListItem.SectionHeader.figma.js.map +1 -1
  238. package/lib/module/index.js +6 -1
  239. package/lib/module/index.js.map +1 -1
  240. package/lib/typescript/animation/hooks/usePopupDialogContentAnimation.d.ts +2 -2
  241. package/lib/typescript/animation/hooks/usePopupOverlayAnimation.d.ts +2 -2
  242. package/lib/typescript/components/Box/Box.d.ts +4 -0
  243. package/lib/typescript/components/Box/Box.d.ts.map +1 -1
  244. package/lib/typescript/components/Breadcrumbs/index.d.ts +3 -1
  245. package/lib/typescript/components/Breadcrumbs/index.d.ts.map +1 -1
  246. package/lib/typescript/components/Breadcrumbs/styles.d.ts +2 -0
  247. package/lib/typescript/components/Breadcrumbs/styles.d.ts.map +1 -1
  248. package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -1
  249. package/lib/typescript/components/DateTimeList/styles.d.ts +1 -4
  250. package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -1
  251. package/lib/typescript/components/ListItem/index.d.ts +5 -4
  252. package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
  253. package/lib/typescript/components/ListItem/styles.d.ts +1 -1
  254. package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
  255. package/lib/typescript/components/Menu/index.d.ts.map +1 -1
  256. package/lib/typescript/components/ScrollShadow/gradient.d.ts +7 -0
  257. package/lib/typescript/components/ScrollShadow/gradient.d.ts.map +1 -0
  258. package/lib/typescript/components/ScrollShadow/index.d.ts +57 -15
  259. package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -1
  260. package/lib/typescript/components/ScrollShadow/styles.d.ts +8 -2
  261. package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -1
  262. package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts +13 -0
  263. package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts.map +1 -0
  264. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +15 -2
  265. package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
  266. package/lib/typescript/components/Stack/HStack.d.ts +3 -2
  267. package/lib/typescript/components/Stack/HStack.d.ts.map +1 -1
  268. package/lib/typescript/components/Stack/VStack.d.ts +3 -2
  269. package/lib/typescript/components/Stack/VStack.d.ts.map +1 -1
  270. package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
  271. package/lib/typescript/components/Tabs/styles.d.ts +0 -7
  272. package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
  273. package/lib/typescript/components/Toast/animation.d.ts +3 -3
  274. package/lib/typescript/components/Toast/animation.d.ts.map +1 -1
  275. package/lib/typescript/figma/ListItem.SectionHeader.figma.d.ts.map +1 -1
  276. package/lib/typescript/index.d.ts +6 -1
  277. package/lib/typescript/index.d.ts.map +1 -1
  278. package/package.json +11 -9
  279. package/src/components/Box/Box.tsx +18 -4
  280. package/src/components/Breadcrumbs/index.tsx +28 -4
  281. package/src/components/Breadcrumbs/styles.ts +30 -0
  282. package/src/components/DateTimeList/index.tsx +13 -26
  283. package/src/components/DateTimeList/styles.ts +1 -10
  284. package/src/components/ListItem/index.tsx +5 -4
  285. package/src/components/ListItem/styles.ts +1 -0
  286. package/src/components/Menu/index.tsx +11 -8
  287. package/src/components/ScrollShadow/gradient.ts +48 -0
  288. package/src/components/ScrollShadow/index.tsx +300 -69
  289. package/src/components/ScrollShadow/styles.ts +45 -28
  290. package/src/components/ScrollShadow/useComposedScrollHandler.ts +58 -0
  291. package/src/components/ScrollShadow/useScrollShadow.ts +72 -10
  292. package/src/components/Stack/HStack.tsx +8 -4
  293. package/src/components/Stack/VStack.tsx +8 -4
  294. package/src/components/Tabs/index.tsx +25 -35
  295. package/src/components/Tabs/styles.ts +0 -9
  296. package/src/index.ts +8 -1
@@ -0,0 +1,473 @@
1
+ # Select (https://design.candescent.com/docs/components/select)
2
+
3
+ Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--React-Native-Components?node-id=427-2296), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/inputs-select--default)
4
+
5
+ `Select` provides a compound interface for a trigger, selected value, dropdown content, and options.
6
+
7
+ ```tsx
8
+ import { Button, Icon, Select } from '@cdx-ui/components';
9
+ import { CheckCircle, KeyboardArrowDown } from '@cdx-ui/icons';
10
+ ```
11
+
12
+ ## Basic
13
+
14
+ Provide the same options to `items` and render them as `Select.Item` children so value labels and keyboard navigation stay in sync.
15
+
16
+ ```tsx
17
+ import { Select } from '@cdx-ui/components';
18
+ import { accounts } from './_data';
19
+
20
+ export default function SelectBasicDemo() {
21
+ return (
22
+ <Select items={accounts} className="w-full" accessibilityLabel="Account type">
23
+ <Select.Trigger>
24
+ <Select.Value placeholder="Select an account…" />
25
+ </Select.Trigger>
26
+ <Select.Content>
27
+ {accounts.map((account) => (
28
+ <Select.Item key={account.value} value={account.value}>
29
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
30
+ </Select.Item>
31
+ ))}
32
+ </Select.Content>
33
+ </Select>
34
+ );
35
+ }
36
+ ```
37
+
38
+ ## Sizes
39
+
40
+ Use the default size for most forms and `small` for compact layouts.
41
+
42
+ ```tsx
43
+ import { Box, Select, Text, type SelectProps } from '@cdx-ui/components';
44
+ import { accounts } from './_data';
45
+
46
+ const sizes = ['default', 'small'] as const satisfies readonly NonNullable<SelectProps['size']>[];
47
+
48
+ export default function SelectSizesDemo() {
49
+ return (
50
+ <Box className="w-full gap-4">
51
+ {sizes.map((size) => (
52
+ <Box key={size} className="gap-2">
53
+ <Text size="sm" color="secondary">
54
+ {size}
55
+ </Text>
56
+ <Select
57
+ items={accounts}
58
+ size={size}
59
+ defaultValue="savings"
60
+ accessibilityLabel={`${size} account type`}
61
+ >
62
+ <Select.Trigger>
63
+ <Select.Value placeholder="Select an account…" />
64
+ </Select.Trigger>
65
+ <Select.Content>
66
+ {accounts.map((account) => (
67
+ <Select.Item key={account.value} value={account.value}>
68
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
69
+ </Select.Item>
70
+ ))}
71
+ </Select.Content>
72
+ </Select>
73
+ </Box>
74
+ ))}
75
+ </Box>
76
+ );
77
+ }
78
+ ```
79
+
80
+ ## With field
81
+
82
+ Compose with `Field` to provide a visible label, helper text, and shared required or validation state.
83
+
84
+ ```tsx
85
+ import { Field, Select } from '@cdx-ui/components';
86
+ import { accounts } from './_data';
87
+
88
+ export default function SelectWithFieldDemo() {
89
+ return (
90
+ <Field className="w-full" isRequired>
91
+ <Field.Label>Account type</Field.Label>
92
+ <Select items={accounts} isRequired>
93
+ <Select.Trigger>
94
+ <Select.Value placeholder="Select an account…" />
95
+ </Select.Trigger>
96
+ <Select.Content>
97
+ {accounts.map((account) => (
98
+ <Select.Item key={account.value} value={account.value}>
99
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
100
+ </Select.Item>
101
+ ))}
102
+ </Select.Content>
103
+ </Select>
104
+ <Field.Helper>Choose the account type to open.</Field.Helper>
105
+ </Field>
106
+ );
107
+ }
108
+ ```
109
+
110
+ ## Custom trigger
111
+
112
+ Set `asChild` on `Select.Trigger` to use a Forge pressable such as `Button`, `IconButton`, or `Chip`. Use the trigger and item `indicator` props to replace their default icons, or pass `null` to suppress an indicator.
113
+
114
+ ```tsx
115
+ import { Button, Icon, Select } from '@cdx-ui/components';
116
+ import { CheckCircle, KeyboardArrowDown } from '@cdx-ui/icons';
117
+ import { accounts } from './_data';
118
+
119
+ export default function SelectCustomTriggerDemo() {
120
+ return (
121
+ <Select items={accounts} accessibilityLabel="Filter accounts">
122
+ <Select.Trigger
123
+ asChild
124
+ indicator={<Button.Icon as={KeyboardArrowDown} position="trailing" />}
125
+ >
126
+ <Button variant="outline">
127
+ <Button.Label>Filter accounts</Button.Label>
128
+ </Button>
129
+ </Select.Trigger>
130
+ <Select.Content className="min-w-64">
131
+ {accounts.map((account) => (
132
+ <Select.Item
133
+ key={account.value}
134
+ value={account.value}
135
+ indicator={<Icon as={CheckCircle} color="action" />}
136
+ >
137
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
138
+ </Select.Item>
139
+ ))}
140
+ </Select.Content>
141
+ </Select>
142
+ );
143
+ }
144
+ ```
145
+
146
+ ## Custom value display
147
+
148
+ Pass children to `Select.Value` to replace the default label. Read the selected option from your own state, and render the placeholder by leaving the children empty when nothing is selected.
149
+
150
+ ```tsx
151
+ import { useState } from 'react';
152
+ import { Select, Text } from '@cdx-ui/components';
153
+ import { accountDetails } from './_data';
154
+
155
+ export default function SelectCustomValueDemo() {
156
+ const [value, setValue] = useState<string>();
157
+ const selected = accountDetails.find((account) => account.value === value);
158
+
159
+ return (
160
+ <Select
161
+ items={accountDetails}
162
+ value={value}
163
+ onValueChange={setValue}
164
+ accessibilityLabel="Account"
165
+ className="w-full"
166
+ >
167
+ <Select.Trigger>
168
+ <Select.Value placeholder="Select an account…">
169
+ {selected && (
170
+ <>
171
+ {selected.label} <Text color="secondary">{selected.mask}</Text>
172
+ </>
173
+ )}
174
+ </Select.Value>
175
+ </Select.Trigger>
176
+ <Select.Content>
177
+ {accountDetails.map((account) => (
178
+ <Select.Item key={account.value} value={account.value}>
179
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
180
+ </Select.Item>
181
+ ))}
182
+ </Select.Content>
183
+ </Select>
184
+ );
185
+ }
186
+ ```
187
+
188
+ ## Custom item display
189
+
190
+ Compose any content inside `Select.Item` to show more than a label, such as a secondary line. Keep `Select.ItemLabel` for the primary text so the selected state and keyboard navigation stay in sync.
191
+
192
+ ```tsx
193
+ import { Select, Text, VStack } from '@cdx-ui/components';
194
+ import { accountDetails } from './_data';
195
+
196
+ export default function SelectCustomItemDemo() {
197
+ return (
198
+ <Select items={accountDetails} accessibilityLabel="Account" className="w-full">
199
+ <Select.Trigger>
200
+ <Select.Value placeholder="Select an account…" />
201
+ </Select.Trigger>
202
+ <Select.Content>
203
+ {accountDetails.map((account) => (
204
+ <Select.Item key={account.value} value={account.value}>
205
+ <VStack className="flex-1">
206
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
207
+ <Text size="sm" color="secondary">
208
+ {account.mask} · {account.balance}
209
+ </Text>
210
+ </VStack>
211
+ </Select.Item>
212
+ ))}
213
+ </Select.Content>
214
+ </Select>
215
+ );
216
+ }
217
+ ```
218
+
219
+ ## Placement
220
+
221
+ The default placement is `bottom-start`. Choose a side alone to center the overlay, or append `-start` or `-end` to align it with the corresponding edge of the trigger. The requested side is preferred rather than guaranteed: when the overlay would overflow the viewport it still flips to the opposite side and shifts along the cross axis.
222
+
223
+ ```tsx
224
+ import { Box, Select, Text, type SelectContentProps } from '@cdx-ui/components';
225
+ import { accounts } from './_data';
226
+
227
+ interface PlacementSelectProps {
228
+ placement: NonNullable<SelectContentProps['placement']>;
229
+ }
230
+
231
+ const PLACEMENT_GROUPS = [
232
+ {
233
+ label: 'Top',
234
+ placements: ['top-start', 'top', 'top-end'],
235
+ },
236
+ {
237
+ label: 'Right',
238
+ placements: ['right-start', 'right', 'right-end'],
239
+ },
240
+ {
241
+ label: 'Bottom',
242
+ placements: ['bottom-start', 'bottom', 'bottom-end'],
243
+ },
244
+ {
245
+ label: 'Left',
246
+ placements: ['left-start', 'left', 'left-end'],
247
+ },
248
+ ] as const satisfies readonly {
249
+ label: string;
250
+ placements: readonly PlacementSelectProps['placement'][];
251
+ }[];
252
+
253
+ function PlacementSelect({ placement }: PlacementSelectProps) {
254
+ return (
255
+ <Select
256
+ items={accounts}
257
+ className="min-w-0 flex-1"
258
+ accessibilityLabel={`${placement} account type`}
259
+ >
260
+ <Select.Trigger>
261
+ <Select.Value placeholder={placement} />
262
+ </Select.Trigger>
263
+ <Select.Content placement={placement}>
264
+ {accounts.map((account) => (
265
+ <Select.Item key={account.value} value={account.value}>
266
+ <Select.ItemLabel>{account.label}</Select.ItemLabel>
267
+ </Select.Item>
268
+ ))}
269
+ </Select.Content>
270
+ </Select>
271
+ );
272
+ }
273
+
274
+ export default function SelectPlacementDemo() {
275
+ return (
276
+ <Box className="w-full max-w-140 gap-6">
277
+ {PLACEMENT_GROUPS.map(({ label, placements }) => (
278
+ <Box key={label} className="w-full items-center gap-2">
279
+ <Text size="sm" color="secondary">
280
+ {label}
281
+ </Text>
282
+ <Box className="w-full flex-row gap-2">
283
+ {placements.map((placement) => (
284
+ <PlacementSelect key={placement} placement={placement} />
285
+ ))}
286
+ </Box>
287
+ </Box>
288
+ ))}
289
+ </Box>
290
+ );
291
+ }
292
+ ```
293
+
294
+ ## API
295
+
296
+ ### Select
297
+
298
+ The root manages the selected value, open state, options, and form-control state.
299
+
300
+ | Prop | Type | Default | Description |
301
+ | --- | --- | --- | --- |
302
+ | `className?` | `string` | — | — |
303
+ | `children?` | `ReactNode` | — | — |
304
+ | `variant?` | `'outline' \| null` | — | — |
305
+ | `size?` | `'default' \| 'small' \| null` | — | — |
306
+ | `items` | `SelectItemData[]` | — | Options used to resolve `Select.Value` labels and drive keyboard navigation. Render matching `Select.Item` elements inside `Select.Content`. |
307
+ | `value?` | `string` | — | — |
308
+ | `defaultValue?` | `string` | — | — |
309
+ | `onValueChange?` | `((value: string) => void)` | — | — |
310
+ | `isOpen?` | `boolean` | — | Controlled open state. Use `defaultOpen` for the uncontrolled variant. |
311
+ | `defaultOpen?` | `boolean` | — | — |
312
+ | `onOpenChange?` | `((open: boolean) => void)` | — | — |
313
+ | `disabled?` | `boolean` | — | — |
314
+ | `isRequired?` | `boolean` | — | — |
315
+ | `isInvalid?` | `boolean` | — | — |
316
+ | `isReadOnly?` | `boolean` | — | — |
317
+ | `native?` | `boolean` | — | Prevents `Select.Content` from rendering. Reserved for native picker integration. |
318
+
319
+ Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
320
+
321
+ | Attribute | Values | Description |
322
+ | --------------- | ----------------- | --------------- |
323
+ | `data-slot` | `select` | Root container |
324
+ | `data-disabled` | `true` \| `false` | Disabled state |
325
+ | `data-required` | `true` \| `false` | Required state |
326
+ | `data-invalid` | `true` \| `false` | Invalid state |
327
+ | `data-readonly` | `true` \| `false` | Read-only state |
328
+
329
+ ## Sub-components
330
+
331
+ ### Select.Trigger
332
+
333
+ Pressable that opens and closes the dropdown. By default it appends a chevron that reflects the open state. The `indicator` prop replaces that chevron, and `indicator={null}` suppresses it. An `asChild` trigger keeps its existing children unless an indicator is explicitly provided.
334
+
335
+ | Prop | Type | Default | Description |
336
+ | --- | --- | --- | --- |
337
+ | `className?` | `string` | — | — |
338
+ | `children?` | `ReactNode` | — | Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. |
339
+ | `asChild?` | `boolean` | — | Substitute the rendered trigger element with the single child. The child must be another Forge primitive (IconButton, Chip, Button) — anything whose underlying Pressable accepts Pressable-shaped handlers. When set, the auto-injected arrow indicator is skipped and `selectTriggerVariants` is not applied so the substituted component's own appearance wins. On web, `<label htmlFor>` association is preserved via a `focus()` bridge registered by the primitive. NOTE — `data-slot` overlap: the substituted component's own `data-slot` (e.g. `button`, `icon-button`, `chip`) may win on the merged element. Selectors that need guaranteed `select-trigger` access should wrap the trigger instead of relying on `data-slot`. |
340
+ | `indicator?` | `ReactNode` | — | Replaces the default open/closed chevron. Pass `null` to suppress the indicator. When `asChild` is set, only an explicitly provided indicator is appended to the substituted child. |
341
+ | `isHovered?` | `boolean` | — | — |
342
+ | `isActive?` | `boolean` | — | — |
343
+ | `isFocused?` | `boolean` | — | — |
344
+ | `isFocusVisible?` | `boolean` | — | — |
345
+ | `isDisabled?` | `boolean` | — | — |
346
+ | `aria-describedby?` | `string` | — | — |
347
+
348
+ Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
349
+
350
+ | Attribute | Values | Description |
351
+ | ------------------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
352
+ | `data-slot` | `select-trigger` | Trigger element; a substituted child's own slot may take precedence when using `asChild` |
353
+ | `data-state` | `open` \| `closed` | Dropdown state |
354
+ | `data-focus` | `true` \| `false` | Focus state |
355
+ | `data-active` | `true` \| `false` | Pressed state |
356
+ | `data-disabled` | `true` \| `false` | Disabled state |
357
+ | `data-required` | `true` \| `false` | Required state |
358
+ | `data-invalid` | `true` \| `false` | Invalid state |
359
+ | `data-readonly` | `true` \| `false` | Read-only state |
360
+ | `data-field-state` | `idle` \| `invalid` \| `focused` \| `focused-invalid` | Combined focus and validation state |
361
+
362
+ ### Select.Value
363
+
364
+ Displays the label from `items` that matches the selected value, or the placeholder when no value is selected.
365
+
366
+ | Prop | Type | Default | Description |
367
+ | --- | --- | --- | --- |
368
+ | `className?` | `string` | — | — |
369
+ | `children?` | `ReactNode` | — | — |
370
+ | `asChild?` | `boolean` | — | — |
371
+ | `placeholder?` | `string` | — | — |
372
+
373
+ Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
374
+
375
+ On web viewports below 600px, value text renders at 16px (`text-base`) for both sizes so it matches
376
+ `Input` and `Textarea` and iOS Safari does not auto-zoom the page when the trigger is focused. At
377
+ 600px and above, web matches native sizes (14px default, 12px small).
378
+
379
+ ### Select.Icon
380
+
381
+ Renders a Forge icon sized to the select. Use it as the `indicator` value when a replacement should retain Select icon sizing and state styling.
382
+
383
+ | Prop | Type | Default | Description |
384
+ | --- | --- | --- | --- |
385
+ | `className?` | `string` | — | Utility classes for size (`size-*`) and placement, merged after the defaults. |
386
+ | `filled?` | `boolean` | — | When `true`, renders the filled variant of the icon (if available). Defaults to `false` (outlined). |
387
+ | `size?` | `string \| number` | — | Width and height of the icon, in pixels or any SVG length. The bare icon renders at 24 when omitted. |
388
+ | `data-testid?` | `string` | — | Test identifier forwarded to the rendered SVG. |
389
+ | `as?` | `ForgeIcon` | — | Forge icon component to render. Required unless `asChild` is set, in which case the consumer's substituted element is rendered instead. |
390
+ | `asChild?` | `boolean` | — | — |
391
+ | `position?` | `'leading' \| 'trailing'` | — | Position hint that emits `data-icon="inline-start"` or `data-icon="inline-end"`. CVA uses this attribute to apply leading/trailing padding logically (RTL-aware). Omit to apply the symmetric baseline gap. |
392
+
393
+ Inherits all `ForgeIconProps` (react-native-svg `SvgProps` plus `filled` and `size`).
394
+
395
+ ### Select.Content
396
+
397
+ Dropdown panel that positions itself around the trigger, scrolls long option lists, and closes when dismissed.
398
+
399
+ | Prop | Type | Default | Description |
400
+ | --- | --- | --- | --- |
401
+ | `className?` | `string` | — | — |
402
+ | `children?` | `ReactNode` | — | — |
403
+ | `asChild?` | `boolean` | — | — |
404
+ | `placement?` | `'auto' \| 'auto-start' \| 'auto-end' \| 'top' \| 'top-start' \| 'top-end' \| 'right' \| 'right-start' \| 'right-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end' \| 'left' \| 'left-start' \| 'left-end'` | — | Preferred dropdown placement relative to the trigger. Defaults to `bottom-start`. |
405
+ | `offset?` | `number` | — | Distance between the trigger and dropdown in pixels. Defaults to 4. |
406
+ | `crossOffset?` | `number` | — | Cross-axis adjustment from the aligned dropdown position in pixels. Defaults to 0. |
407
+ | `enteringAnimation?` | `EntryOrExitLayoutType` | — | Enter animation for the dropdown. Defaults to a 150ms fade. |
408
+ | `exitingAnimation?` | `EntryOrExitLayoutType` | — | Exit animation for the dropdown. Defaults to a 100ms fade. |
409
+
410
+ Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
411
+
412
+ ### Select.Item
413
+
414
+ An individual option. Its `value` must match an entry in the root `items` array. The `indicator` prop replaces the default selected-item checkmark, and `indicator={null}` suppresses it.
415
+
416
+ | Prop | Type | Default | Description |
417
+ | --- | --- | --- | --- |
418
+ | `className?` | `string` | — | — |
419
+ | `children?` | `ReactNode` | — | Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. |
420
+ | `disabled?` | `boolean` | — | Whether the press behavior is disabled. |
421
+ | `asChild?` | `boolean` | — | — |
422
+ | `indicator?` | `ReactNode` | — | Replaces the default selected-item checkmark. The replacement is rendered only while the item is selected. Pass `null` to suppress the indicator. |
423
+ | `value` | `string` | — | — |
424
+ | `isHovered?` | `boolean` | — | — |
425
+ | `isActive?` | `boolean` | — | — |
426
+ | `isFocused?` | `boolean` | — | — |
427
+ | `isFocusVisible?` | `boolean` | — | — |
428
+ | `isDisabled?` | `boolean` | — | — |
429
+
430
+ Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
431
+
432
+ | Attribute | Values | Description |
433
+ | -------------------- | ------------------------ | ---------------------------------- |
434
+ | `data-slot` | `select-item` | Option element |
435
+ | `data-state` | `checked` \| `unchecked` | Selection state |
436
+ | `data-highlighted` | `true` \| `false` | Current keyboard-navigation target |
437
+ | `data-hover` | `true` \| `false` | Hover state |
438
+ | `data-focus` | `true` \| `false` | Focus state |
439
+ | `data-active` | `true` \| `false` | Pressed state |
440
+ | `data-disabled` | `true` \| `false` | Disabled state |
441
+ | `data-focus-visible` | `true` \| `false` | Keyboard focus ring |
442
+
443
+ ### Select.ItemLabel
444
+
445
+ Text label rendered inside a `Select.Item`.
446
+
447
+ | Prop | Type | Default | Description |
448
+ | --- | --- | --- | --- |
449
+ | `className?` | `string` | — | — |
450
+ | `children?` | `ReactNode` | — | — |
451
+ | `asChild?` | `boolean` | — | — |
452
+
453
+ Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
454
+
455
+ ## Anatomy
456
+
457
+ | `data-slot` | Element |
458
+ | ------------------- | ----------------------------- |
459
+ | `select` | Root container |
460
+ | `select-trigger` | Dropdown trigger |
461
+ | `select-value` | Selected label or placeholder |
462
+ | `select-icon` | Dropdown indicator |
463
+ | `select-content` | Dropdown panel |
464
+ | `select-item` | Option |
465
+ | `select-item-label` | Option label |
466
+
467
+ ## Accessibility
468
+
469
+ The trigger uses `role="combobox"` with `aria-haspopup="listbox"`, reflects open, required, invalid, and read-only state, and points to the dropdown content. The content uses `role="listbox"` and each item uses `role="option"` with selected and disabled state.
470
+
471
+ Compose with `Field.Label` for a visible associated label. When no visible label is present, provide `accessibilityLabel` on `Select`.
472
+
473
+ On web, Enter, Space, Arrow Down, or Arrow Up opens the dropdown. Arrow keys, Home, and End move through enabled options; Enter or Space selects; Escape closes; and Tab closes while continuing focus navigation.