@cdx-ui/components 0.3.1 → 0.3.3

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 (94) hide show
  1. package/README.md +2 -2
  2. package/coverage.svg +1 -1
  3. package/docs/components/bottom-sheet/content.md +2 -2
  4. package/docs/components/content.md +2 -0
  5. package/docs/components/currency-input/content.md +94 -5
  6. package/docs/components/data-grid/content.md +125 -1
  7. package/docs/components/insight/content.md +644 -0
  8. package/docs/components/insight-stack/content.md +231 -0
  9. package/docs/foundations/styles/tokens/all-tokens/content.md +23 -3
  10. package/docs/llms.txt +4 -0
  11. package/docs/patterns/forms/content.md +255 -0
  12. package/docs/resources/release-notes/0.3.1/content.md +13 -0
  13. package/docs/resources/release-notes/0.3.2/content.md +17 -0
  14. package/docs/resources/release-notes/content.md +2 -0
  15. package/lib/commonjs/components/BottomSheet/index.js +63 -16
  16. package/lib/commonjs/components/BottomSheet/index.js.map +1 -1
  17. package/lib/commonjs/components/CurrencyInput/index.js +35 -10
  18. package/lib/commonjs/components/CurrencyInput/index.js.map +1 -1
  19. package/lib/commonjs/components/CurrencyInput/styles.js +40 -3
  20. package/lib/commonjs/components/CurrencyInput/styles.js.map +1 -1
  21. package/lib/commonjs/components/Insight/index.js +438 -0
  22. package/lib/commonjs/components/Insight/index.js.map +1 -0
  23. package/lib/commonjs/components/Insight/insetRadius.js +35 -0
  24. package/lib/commonjs/components/Insight/insetRadius.js.map +1 -0
  25. package/lib/commonjs/components/Insight/styles.js +186 -0
  26. package/lib/commonjs/components/Insight/styles.js.map +1 -0
  27. package/lib/commonjs/components/InsightStack/index.js +93 -0
  28. package/lib/commonjs/components/InsightStack/index.js.map +1 -0
  29. package/lib/commonjs/components/InsightStack/styles.js +61 -0
  30. package/lib/commonjs/components/InsightStack/styles.js.map +1 -0
  31. package/lib/commonjs/components/index.js +24 -0
  32. package/lib/commonjs/components/index.js.map +1 -1
  33. package/lib/commonjs/figma/CurrencyInput.figma.js +8 -8
  34. package/lib/commonjs/figma/CurrencyInput.figma.js.map +1 -1
  35. package/lib/commonjs/figma/Insight.figma.js +73 -0
  36. package/lib/commonjs/figma/Insight.figma.js.map +1 -0
  37. package/lib/commonjs/figma/InsightStack.figma.js +39 -0
  38. package/lib/commonjs/figma/InsightStack.figma.js.map +1 -0
  39. package/lib/module/components/BottomSheet/index.js +64 -17
  40. package/lib/module/components/BottomSheet/index.js.map +1 -1
  41. package/lib/module/components/CurrencyInput/index.js +37 -12
  42. package/lib/module/components/CurrencyInput/index.js.map +1 -1
  43. package/lib/module/components/CurrencyInput/styles.js +38 -3
  44. package/lib/module/components/CurrencyInput/styles.js.map +1 -1
  45. package/lib/module/components/Insight/index.js +433 -0
  46. package/lib/module/components/Insight/index.js.map +1 -0
  47. package/lib/module/components/Insight/insetRadius.js +30 -0
  48. package/lib/module/components/Insight/insetRadius.js.map +1 -0
  49. package/lib/module/components/Insight/styles.js +183 -0
  50. package/lib/module/components/Insight/styles.js.map +1 -0
  51. package/lib/module/components/InsightStack/index.js +89 -0
  52. package/lib/module/components/InsightStack/index.js.map +1 -0
  53. package/lib/module/components/InsightStack/styles.js +58 -0
  54. package/lib/module/components/InsightStack/styles.js.map +1 -0
  55. package/lib/module/components/index.js +2 -0
  56. package/lib/module/components/index.js.map +1 -1
  57. package/lib/module/figma/CurrencyInput.figma.js +8 -8
  58. package/lib/module/figma/CurrencyInput.figma.js.map +1 -1
  59. package/lib/module/figma/Insight.figma.js +67 -0
  60. package/lib/module/figma/Insight.figma.js.map +1 -0
  61. package/lib/module/figma/InsightStack.figma.js +33 -0
  62. package/lib/module/figma/InsightStack.figma.js.map +1 -0
  63. package/lib/typescript/components/BottomSheet/index.d.ts.map +1 -1
  64. package/lib/typescript/components/CurrencyInput/index.d.ts +4 -1
  65. package/lib/typescript/components/CurrencyInput/index.d.ts.map +1 -1
  66. package/lib/typescript/components/CurrencyInput/styles.d.ts +17 -6
  67. package/lib/typescript/components/CurrencyInput/styles.d.ts.map +1 -1
  68. package/lib/typescript/components/Insight/index.d.ts +87 -0
  69. package/lib/typescript/components/Insight/index.d.ts.map +1 -0
  70. package/lib/typescript/components/Insight/insetRadius.d.ts +8 -0
  71. package/lib/typescript/components/Insight/insetRadius.d.ts.map +1 -0
  72. package/lib/typescript/components/Insight/styles.d.ts +33 -0
  73. package/lib/typescript/components/Insight/styles.d.ts.map +1 -0
  74. package/lib/typescript/components/InsightStack/index.d.ts +21 -0
  75. package/lib/typescript/components/InsightStack/index.d.ts.map +1 -0
  76. package/lib/typescript/components/InsightStack/styles.d.ts +11 -0
  77. package/lib/typescript/components/InsightStack/styles.d.ts.map +1 -0
  78. package/lib/typescript/components/index.d.ts +2 -0
  79. package/lib/typescript/components/index.d.ts.map +1 -1
  80. package/lib/typescript/figma/CurrencyInput.figma.d.ts.map +1 -1
  81. package/lib/typescript/figma/Insight.figma.d.ts +8 -0
  82. package/lib/typescript/figma/Insight.figma.d.ts.map +1 -0
  83. package/lib/typescript/figma/InsightStack.figma.d.ts +8 -0
  84. package/lib/typescript/figma/InsightStack.figma.d.ts.map +1 -0
  85. package/package.json +5 -5
  86. package/src/components/BottomSheet/index.tsx +88 -13
  87. package/src/components/CurrencyInput/index.tsx +36 -12
  88. package/src/components/CurrencyInput/styles.ts +51 -4
  89. package/src/components/Insight/index.tsx +531 -0
  90. package/src/components/Insight/insetRadius.ts +30 -0
  91. package/src/components/Insight/styles.ts +193 -0
  92. package/src/components/InsightStack/index.tsx +86 -0
  93. package/src/components/InsightStack/styles.ts +61 -0
  94. package/src/components/index.ts +2 -0
@@ -0,0 +1,231 @@
1
+ # Insight Stack (https://design.candescent.com/docs/components/insight-stack)
2
+
3
+ Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--Components?node-id=13652-32306), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/surfaces-insightstack--default)
4
+
5
+ `InsightStack` wraps a summary [`Insight`](https://design.candescent.com/docs/components/insight/content.md) and draws up to two cards behind it, so a single card on Home can stand for a list of insights. The layers are decorative and never interactive.
6
+
7
+ ```tsx
8
+ import { InsightStack } from '@cdx-ui/components';
9
+ ```
10
+
11
+ ## Basic
12
+
13
+ Pass the number of insights as `count`. The layers don't announce it, so include the count in the front Insight's accessible label.
14
+
15
+ ```tsx
16
+ import { Box, Insight, InsightStack } from '@cdx-ui/components';
17
+
18
+ const insightCount = 3;
19
+ const summary = 'Your spending this month is 12% lower than last month.';
20
+
21
+ export default function InsightStackBasicDemo() {
22
+ return (
23
+ <Box className="w-full max-w-md">
24
+ <InsightStack count={insightCount}>
25
+ <Insight
26
+ aiGenerated
27
+ onPress={() => {}}
28
+ accessibilityLabel={`${summary} ${insightCount} insights.`}
29
+ >
30
+ <Insight.Description>{summary}</Insight.Description>
31
+ </Insight>
32
+ </InsightStack>
33
+ </Box>
34
+ );
35
+ }
36
+ ```
37
+
38
+ ## Item count
39
+
40
+ A `count` of 1 draws no layers, 2 draws one, and 3 or more draws two. When pressed, only the front Insight scales; the layers stay still.
41
+
42
+ ```tsx
43
+ import { Box, Insight, InsightStack } from '@cdx-ui/components';
44
+
45
+ const counts = [1, 2, 3] as const;
46
+
47
+ export default function InsightStackCountsDemo() {
48
+ return (
49
+ <Box className="w-full max-w-md gap-4">
50
+ {counts.map((count) => {
51
+ const summary = count === 1 ? 'You have 1 new insight.' : `You have ${count} new insights.`;
52
+
53
+ return (
54
+ <InsightStack key={count} count={count}>
55
+ <Insight onPress={() => {}}>
56
+ <Insight.Description>{summary}</Insight.Description>
57
+ </Insight>
58
+ </InsightStack>
59
+ );
60
+ })}
61
+ </Box>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## Colors
67
+
68
+ The layers are always neutral. The front Insight's `color` and `aiGenerated` tint only the front card.
69
+
70
+ ```tsx
71
+ import { Box, Insight, InsightStack } from '@cdx-ui/components';
72
+
73
+ const stacks = [
74
+ { key: 'warning', color: 'warning', count: 2, copy: 'You have 2 bills due before payday.' },
75
+ { key: 'danger', color: 'danger', count: 3, copy: 'You have 3 overdue payments.' },
76
+ { key: 'ai', color: 'info', count: 3, copy: 'You have 3 new ways to save.', aiGenerated: true },
77
+ ] as const;
78
+
79
+ export default function InsightStackColorsDemo() {
80
+ return (
81
+ <Box className="w-full max-w-md gap-4">
82
+ {stacks.map((stack) => (
83
+ <InsightStack key={stack.key} count={stack.count}>
84
+ <Insight color={stack.color} aiGenerated={'aiGenerated' in stack} onPress={() => {}}>
85
+ <Insight.Description>{stack.copy}</Insight.Description>
86
+ </Insight>
87
+ </InsightStack>
88
+ ))}
89
+ </Box>
90
+ );
91
+ }
92
+ ```
93
+
94
+ ## Dismissible
95
+
96
+ Pass the number of insights left as `count`, so the layers drop away as the user dismisses cards. Keep rendering the same front Insight and swap its copy, so `ref.current?.focus()` returns focus to it after each dismissal. When none is left, remove the stack and move focus elsewhere, here to the button below it.
97
+
98
+ ```tsx
99
+ import { useRef, useState, type ComponentRef } from 'react';
100
+ import { Box, Button, Insight, InsightStack, Text } from '@cdx-ui/components';
101
+
102
+ const insights = [
103
+ { id: 'spending', copy: 'Your spending this month is 12% lower than last month.' },
104
+ { id: 'subscriptions', copy: 'You have three subscriptions renewing in the next seven days.' },
105
+ { id: 'savings', copy: 'You could save $45.00 a month with annual billing.' },
106
+ { id: 'paycheck', copy: 'Your paycheck arrived a day early.' },
107
+ ];
108
+
109
+ export default function InsightStackDismissibleDemo() {
110
+ const [remaining, setRemaining] = useState(insights);
111
+ const insightRef = useRef<ComponentRef<typeof Insight>>(null);
112
+ const restoreRef = useRef<ComponentRef<typeof Button>>(null);
113
+ const front = remaining.at(0);
114
+ const count = remaining.length;
115
+
116
+ const dismiss = () => {
117
+ setRemaining((current) => current.slice(1));
118
+ if (count > 1) {
119
+ insightRef.current?.focus();
120
+ } else {
121
+ restoreRef.current?.focus();
122
+ }
123
+ };
124
+
125
+ return (
126
+ <Box className="w-full max-w-md gap-4">
127
+ {front ? (
128
+ <InsightStack count={count}>
129
+ <Insight
130
+ ref={insightRef}
131
+ aiGenerated
132
+ onPress={() => {}}
133
+ onClose={dismiss}
134
+ accessibilityLabel={`${front.copy} ${count} ${count === 1 ? 'insight' : 'insights'}.`}
135
+ >
136
+ <Insight.Description>{front.copy}</Insight.Description>
137
+ </Insight>
138
+ </InsightStack>
139
+ ) : (
140
+ <Box className="min-h-14 justify-center">
141
+ <Text color="secondary">{"You're all caught up."}</Text>
142
+ </Box>
143
+ )}
144
+ <Box className="items-start">
145
+ <Button
146
+ ref={restoreRef}
147
+ variant="outline"
148
+ size="small"
149
+ onPress={() => setRemaining(insights)}
150
+ >
151
+ <Button.Label>Restore insights</Button.Label>
152
+ </Button>
153
+ </Box>
154
+ </Box>
155
+ );
156
+ }
157
+ ```
158
+
159
+ ## Detail
160
+
161
+ The front card can also be a detail Insight. The layers follow its height, so they peek below it however tall it grows.
162
+
163
+ ```tsx
164
+ import { Box, Button, Insight, InsightStack } from '@cdx-ui/components';
165
+
166
+ export default function InsightStackDetailDemo() {
167
+ return (
168
+ <Box className="w-full max-w-md gap-6">
169
+ <InsightStack count={3}>
170
+ <Insight layout="detail" onClose={() => {}}>
171
+ <Insight.Tag>Spending</Insight.Tag>
172
+ <Insight.Header>
173
+ <Insight.Title>Dining is trending higher</Insight.Title>
174
+ <Insight.Description>
175
+ You have spent $320.00 of your $400.00 dining budget, with 10 days left this month.
176
+ </Insight.Description>
177
+ </Insight.Header>
178
+ <Insight.Footer>
179
+ <Button variant="outline" size="small">
180
+ <Button.Label>View transactions</Button.Label>
181
+ </Button>
182
+ <Button size="small">
183
+ <Button.Label>Adjust budget</Button.Label>
184
+ </Button>
185
+ </Insight.Footer>
186
+ </Insight>
187
+ </InsightStack>
188
+ <InsightStack count={2}>
189
+ <Insight layout="detail" aiGenerated onClose={() => {}}>
190
+ <Insight.Tag>Savings</Insight.Tag>
191
+ <Insight.Header>
192
+ <Insight.Title>Grow your emergency fund</Insight.Title>
193
+ <Insight.Description>
194
+ Setting aside $150.00 each payday would reach your savings goal by June.
195
+ </Insight.Description>
196
+ </Insight.Header>
197
+ <Insight.Footer>
198
+ <Button size="small">
199
+ <Button.Label>Set up transfer</Button.Label>
200
+ </Button>
201
+ </Insight.Footer>
202
+ </Insight>
203
+ </InsightStack>
204
+ </Box>
205
+ );
206
+ }
207
+ ```
208
+
209
+ ## API
210
+
211
+ ### InsightStack
212
+
213
+ | Prop | Type | Default | Description |
214
+ | --- | --- | --- | --- |
215
+ | `className?` | `string` | — | — |
216
+ | `children?` | `ReactNode` | — | The front card, usually a summary `Insight`. |
217
+ | `count` | `number` | — | How many insights the stack stands for. `1` or less draws no layers, `2` draws one, and `3` or more draws two. The layers are decorative, so put the count in the front Insight's accessible label or hint. |
218
+ | `asChild?` | `boolean` | — | Merges the root props onto a single child element instead of rendering a `View`. |
219
+
220
+ Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
221
+
222
+ ## Anatomy
223
+
224
+ | `data-slot` | Element |
225
+ | --------------------- | ---------------------------------------- |
226
+ | `insight-stack` | Root |
227
+ | `insight-stack-layer` | Decorative card behind the front Insight |
228
+
229
+ ## Accessibility
230
+
231
+ The root adds no role, so the front Insight stays the only interactive element. The layers are hidden from assistive tech, so `count` is not announced. Include the count in the front Insight's accessible label, for example by appending "3 insights" to its copy in `accessibilityLabel`, or in its `accessibilityHint`.
@@ -64,13 +64,13 @@ Text and icon colors, used as `text-*` utilities. Light and Dark name the primit
64
64
  | `content.neutral-on-subtle` | `text-content-neutral-on-subtle` | neutral, on subtle | `color.base.800` | `color.base.100` |
65
65
  | `content.info` | `text-content-info` | info | `color.sky.700` | `color.sky.500` |
66
66
  | `content.info-on-strong` | `text-content-info-on-strong` | info, on strong | `color.white` | `color.white` |
67
- | `content.info-on-subtle` | `text-content-info-on-subtle` | info, on subtle | `color.sky.900` | `color.sky.900` |
67
+ | `content.info-on-subtle` | `text-content-info-on-subtle` | info, on subtle | `color.sky.900` | `color.sky.100` |
68
68
  | `content.success` | `text-content-success` | success | `color.green.700` | `color.green.600` |
69
69
  | `content.success-on-strong` | `text-content-success-on-strong` | success, on strong | `color.white` | `color.white` |
70
- | `content.success-on-subtle` | `text-content-success-on-subtle` | success, on subtle | `color.green.900` | `color.green.900` |
70
+ | `content.success-on-subtle` | `text-content-success-on-subtle` | success, on subtle | `color.green.900` | `color.green.100` |
71
71
  | `content.warning` | `text-content-warning` | warning | `color.amber.700` | `color.amber.600` |
72
72
  | `content.warning-on-strong` | `text-content-warning-on-strong` | warning, on strong | `color.black` | `color.black` |
73
- | `content.warning-on-subtle` | `text-content-warning-on-subtle` | warning, on subtle | `color.amber.900` | `color.amber.900` |
73
+ | `content.warning-on-subtle` | `text-content-warning-on-subtle` | warning, on subtle | `color.amber.900` | `color.amber.100` |
74
74
  | `content.danger` | `text-content-danger` | danger | `color.red.700` | `color.red.300` |
75
75
  | `content.danger-on-strong` | `text-content-danger-on-strong` | danger, on strong | `color.white` | `color.red.900` |
76
76
  | `content.danger-on-subtle` | `text-content-danger-on-subtle` | danger, on subtle | `color.red.900` | `color.red.100` |
@@ -106,12 +106,24 @@ Background colors, used as `bg-*` utilities. Light and Dark name the primitive e
106
106
  | `surface.action.tint-hover` | `bg-surface-action-tint-hover` | Action, tint, hover | `color.brand.100` | `color.brand.900` |
107
107
  | `surface.action.tint-active` | `bg-surface-action-tint-active` | Action, tint, active | `color.brand.200` | `color.brand.800` |
108
108
  | `surface.brand.strong` | `bg-surface-brand-strong` | Brand, strong | `color.brand.700` | `color.brand.300` |
109
+ | `surface.brand.strong-hover` | `bg-surface-brand-strong-hover` | Brand, strong, hover | `color.brand.800` | `color.brand.200` |
110
+ | `surface.brand.strong-active` | `bg-surface-brand-strong-active` | Brand, strong, active | `color.brand.900` | `color.brand.100` |
109
111
  | `surface.brand.subtle` | `bg-surface-brand-subtle` | Brand, subtle | `color.brand.200` | `color.brand.900` |
112
+ | `surface.brand.subtle-hover` | `bg-surface-brand-subtle-hover` | Brand, subtle, hover | `color.brand.300` | `color.brand.800` |
113
+ | `surface.brand.subtle-active` | `bg-surface-brand-subtle-active` | Brand, subtle, active | `color.brand.400` | `color.brand.700` |
110
114
  | `surface.brand.tint` | `bg-surface-brand-tint` | Brand, tint | `color.brand.50` | `color.brand.950` |
115
+ | `surface.brand.tint-hover` | `bg-surface-brand-tint-hover` | Brand, tint, hover | `color.brand.100` | `color.brand.900` |
116
+ | `surface.brand.tint-active` | `bg-surface-brand-tint-active` | Brand, tint, active | `color.brand.200` | `color.brand.800` |
111
117
  | `surface.brand.core` | `bg-surface-brand-core` | Brand, core | `color.brand.input` | `color.brand.input` |
112
118
  | `surface.accent.strong` | `bg-surface-accent-strong` | Accent, strong | `color.accent.700` | `color.accent.300` |
119
+ | `surface.accent.strong-hover` | `bg-surface-accent-strong-hover` | Accent, strong, hover | `color.accent.800` | `color.accent.200` |
120
+ | `surface.accent.strong-active` | `bg-surface-accent-strong-active` | Accent, strong, active | `color.accent.900` | `color.accent.100` |
113
121
  | `surface.accent.subtle` | `bg-surface-accent-subtle` | Accent, subtle | `color.accent.200` | `color.accent.900` |
122
+ | `surface.accent.subtle-hover` | `bg-surface-accent-subtle-hover` | Accent, subtle, hover | `color.accent.300` | `color.accent.800` |
123
+ | `surface.accent.subtle-active` | `bg-surface-accent-subtle-active` | Accent, subtle, active | `color.accent.400` | `color.accent.700` |
114
124
  | `surface.accent.tint` | `bg-surface-accent-tint` | Accent, tint | `color.accent.50` | `color.accent.950` |
125
+ | `surface.accent.tint-hover` | `bg-surface-accent-tint-hover` | Accent, tint, hover | `color.accent.100` | `color.accent.900` |
126
+ | `surface.accent.tint-active` | `bg-surface-accent-tint-active` | Accent, tint, active | `color.accent.200` | `color.accent.800` |
115
127
  | `surface.accent.core` | `bg-surface-accent-core` | Accent, core | `color.accent.input` | `color.accent.input` |
116
128
  | `surface.danger.strong` | `bg-surface-danger-strong` | Danger, strong | `color.red.700` | `color.red.200` |
117
129
  | `surface.danger.strong-hover` | `bg-surface-danger-strong-hover` | Danger, strong, hover | `color.red.800` | `color.red.100` |
@@ -175,6 +187,14 @@ Border colors, used as `border-*` utilities. Light and Dark name the primitive e
175
187
  | `stroke.focus` | `border-stroke-focus` | focus | `color.brand.700` | `color.brand.300` |
176
188
  | `stroke.hover` | `border-stroke-hover` | hover | `color.base.950` | `color.base.50` |
177
189
  | `stroke.ring` | `border-stroke-ring` | ring | `color.base.950` | `color.white` |
190
+ | `stroke.accent` | `border-stroke-accent` | accent | `color.accent.700` | `color.accent.300` |
191
+ | `stroke.ai-end` | `border-stroke-ai-end` | ai-end | `color.accent.300` | `color.accent.300` |
192
+ | `stroke.ai-start` | `border-stroke-ai-start` | ai-start | `color.brand.400` | `color.brand.400` |
193
+ | `stroke.brand` | `border-stroke-brand` | brand | `color.brand.700` | `color.brand.300` |
194
+ | `stroke.danger-subtle` | `border-stroke-danger-subtle` | danger-subtle | `color.red.300` | `color.red.300` |
195
+ | `stroke.info-subtle` | `border-stroke-info-subtle` | info-subtle | `color.sky.300` | `color.sky.300` |
196
+ | `stroke.success-subtle` | `border-stroke-success-subtle` | success-subtle | `color.green.300` | `color.green.300` |
197
+ | `stroke.warning-subtle` | `border-stroke-warning-subtle` | warning-subtle | `color.amber.300` | `color.amber.300` |
178
198
 
179
199
  ### Chart
180
200
 
package/docs/llms.txt CHANGED
@@ -104,6 +104,8 @@ Links point at each page's `content.md`, which carries the page's prose, demo so
104
104
  - [Box](components/box/content.md): The layout container for building screens, with semantic web elements through role and an asChild escape hatch. Resources: [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/layout-box--default)
105
105
  - [Card](components/card/content.md): A composable surface for grouping related content, with optional brand stripe, header, content, and footer regions. Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--React-Native-Components?node-id=456-14), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/surfaces-card--with-stripe)
106
106
  - [Drawer](components/drawer/content.md): A panel attached to an edge of the viewport or container, with modal and permanent presentations. Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--Components?node-id=9956-15204), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/overlays-drawer--default)
107
+ - [Insight](components/insight/content.md): A bordered card for a personalised, often AI-generated, insight, shown as a pressable summary or a detail card. Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--Components?node-id=13158-18419), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/surfaces-insight--default)
108
+ - [Insight Stack](components/insight-stack/content.md): Draws decorative cards behind a summary Insight to show that more insights are waiting. Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--Components?node-id=13652-32306), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/surfaces-insightstack--default)
107
109
  - [Scoped Theme](components/scoped-theme/content.md): Forces a specific theme on a component subtree, regardless of the application's global theme. Resources: [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/theming-scopedtheme--default)
108
110
  - [Scroll Shadow](components/scroll-shadow/content.md): Fades the edges of a scroll view so overflowing content reads as scrollable on iOS, Android, and web. Resources: [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/layout-scrollshadow--default)
109
111
  - [Stack](components/stack/content.md): Horizontal and vertical layout primitives with token-based spacing and optional reversed order. Resources: [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/layout-vstack--default)
@@ -138,6 +140,8 @@ Links point at each page's `content.md`, which carries the page's prose, demo so
138
140
 
139
141
  ## Release notes
140
142
 
143
+ - [0.3.2](resources/release-notes/0.3.2/content.md): CurrencyInput adds an outline variant for bordered amount fields; BottomSheet opens reliably in optimized web production builds.
144
+ - [0.3.1](resources/release-notes/0.3.1/content.md): DataGrid adds filterMode for server-side search and filters; IconButton adds warning, success, and info color options.
141
145
  - [0.3.0](resources/release-notes/0.3.0/content.md): Calendar and MonthCalendar year pickers scroll the full selectable range; BottomSheet adds fullBleed and updated Content and Footer padding.
142
146
  - [0.2.5](resources/release-notes/0.2.5/content.md): Box is the supported layout primitive with hardened stacks; View from @cdx-ui/components is deprecated. Align Expo apps with SDK 57 and React Native 0.86.
143
147
  - [0.2.4](resources/release-notes/0.2.4/content.md): ScrollShadow fades scroll edges on iOS, Android, and web; ListItem.SectionHeader adds a gradient variant for account-style lists.
@@ -105,4 +105,259 @@ export default function DateRangeBlock() {
105
105
  </Card>
106
106
  );
107
107
  }
108
+ ```
109
+
110
+ ## Add Payee Form
111
+
112
+ A full-screen form with a contact card, sectioned fields, a bottom-sheet date picker, e-bill consent checkboxes, and a pinned submit button.
113
+
114
+ **Platforms:** iOS, Android
115
+
116
+ ```tsx
117
+ import { useState } from 'react';
118
+ import {
119
+ Avatar,
120
+ BottomSheet,
121
+ Box,
122
+ Button,
123
+ Card,
124
+ Checkbox,
125
+ CurrencyInput,
126
+ DateField,
127
+ DatePicker,
128
+ Divider,
129
+ Field,
130
+ Form,
131
+ FullScreenDialog,
132
+ Heading,
133
+ Input,
134
+ ListItem,
135
+ ScrollView,
136
+ Select,
137
+ Text,
138
+ VStack,
139
+ } from '@cdx-ui/components';
140
+ import type { DateId } from '@cdx-ui/formatters';
141
+
142
+ const PAYEE_TYPES = [
143
+ { value: 'utility', label: 'Utility' },
144
+ { value: 'credit-card', label: 'Credit card' },
145
+ { value: 'loan', label: 'Loan' },
146
+ { value: 'other', label: 'Other' },
147
+ ];
148
+
149
+ const STATES = [
150
+ { value: 'ca', label: 'California' },
151
+ { value: 'ma', label: 'Massachusetts' },
152
+ { value: 'ny', label: 'New York' },
153
+ { value: 'tx', label: 'Texas' },
154
+ ];
155
+
156
+ export default function AddPayeeFormBlock() {
157
+ const [open, setOpen] = useState(true);
158
+ const [amount, setAmount] = useState(0);
159
+ const [date, setDate] = useState<DateId | undefined>();
160
+ const [dateOpen, setDateOpen] = useState(false);
161
+ const [consents, setConsents] = useState<string[]>([]);
162
+
163
+ return (
164
+ <FullScreenDialog isOpen={open} onOpenChange={setOpen}>
165
+ <FullScreenDialog.Trigger asChild>
166
+ <Button>
167
+ <Button.Label>Add payee</Button.Label>
168
+ </Button>
169
+ </FullScreenDialog.Trigger>
170
+ <FullScreenDialog.Popup>
171
+ <FullScreenDialog.Header title="Add payee" />
172
+ <FullScreenDialog.Content>
173
+ <ScrollView className="flex-1">
174
+ <Box className="p-4">
175
+ <Form>
176
+ <Card fullBleed>
177
+ <Card.Stripe />
178
+ <Card.Content>
179
+ <ListItem showSeparator={false}>
180
+ <ListItem.LeadingSlot>
181
+ <Avatar color="info">
182
+ <Avatar.Text>Wellesley Bank</Avatar.Text>
183
+ </Avatar>
184
+ </ListItem.LeadingSlot>
185
+ <ListItem.Content>
186
+ <ListItem.Title>Wellesley Bank MA Checking</ListItem.Title>
187
+ </ListItem.Content>
188
+ </ListItem>
189
+ </Card.Content>
190
+ </Card>
191
+
192
+ <Text color="secondary">
193
+ Add a company or person you pay regularly. Details are saved to your payee list,
194
+ and you can schedule a payment as soon as the payee is added.
195
+ </Text>
196
+
197
+ <Card>
198
+ <Card.Header>
199
+ <Heading size="sm">Payee details</Heading>
200
+ </Card.Header>
201
+ <Card.Content>
202
+ <VStack space="lg">
203
+ <Field>
204
+ <Field.Label>Payee name</Field.Label>
205
+ <Input>
206
+ <Input.Field placeholder="Enter a name" />
207
+ </Input>
208
+ <Field.Helper>As it appears on your statement.</Field.Helper>
209
+ </Field>
210
+ <Field>
211
+ <Field.Label>Email</Field.Label>
212
+ <Input>
213
+ <Input.Field
214
+ autoCapitalize="none"
215
+ autoComplete="email"
216
+ keyboardType="email-address"
217
+ placeholder="example@domain.com"
218
+ />
219
+ </Input>
220
+ </Field>
221
+ <Field>
222
+ <Field.Label>Amount</Field.Label>
223
+ <CurrencyInput variant="outline" value={amount} onChangeValue={setAmount} />
224
+ </Field>
225
+ <Field>
226
+ <DatePicker
227
+ value={date}
228
+ onValueChange={setDate}
229
+ isOpen={dateOpen}
230
+ onOpenChange={setDateOpen}
231
+ >
232
+ <Field.Label>Date</Field.Label>
233
+ <DateField.Group fullWidth>
234
+ <DateField.Input />
235
+ <DateField.Suffix>
236
+ <DatePicker.Trigger>
237
+ <DatePicker.TriggerIndicator />
238
+ </DatePicker.Trigger>
239
+ </DateField.Suffix>
240
+ </DateField.Group>
241
+ <DatePicker.BottomSheet>
242
+ <BottomSheet.Header
243
+ title="Select date"
244
+ onClose={() => setDateOpen(false)}
245
+ />
246
+ <BottomSheet.Content>
247
+ <DatePicker.Calendar aria-label="Date" />
248
+ </BottomSheet.Content>
249
+ </DatePicker.BottomSheet>
250
+ </DatePicker>
251
+ <Field.Helper>The first date you want to pay this payee.</Field.Helper>
252
+ </Field>
253
+ <Field>
254
+ <Field.Label>Payee type</Field.Label>
255
+ <Select items={PAYEE_TYPES}>
256
+ <Select.Trigger>
257
+ <Select.Value placeholder="Select a type" />
258
+ </Select.Trigger>
259
+ <Select.Content>
260
+ {PAYEE_TYPES.map((type) => (
261
+ <Select.Item key={type.value} value={type.value}>
262
+ <Select.ItemLabel>{type.label}</Select.ItemLabel>
263
+ </Select.Item>
264
+ ))}
265
+ </Select.Content>
266
+ </Select>
267
+ </Field>
268
+ </VStack>
269
+ </Card.Content>
270
+ </Card>
271
+
272
+ <Card>
273
+ <Card.Header>
274
+ <Heading size="sm">Service address</Heading>
275
+ </Card.Header>
276
+ <Card.Content>
277
+ <VStack space="lg">
278
+ <Text color="secondary">
279
+ The service address is where the payee provides the service. It may be
280
+ different from your current primary address.
281
+ </Text>
282
+ <Field>
283
+ <Field.Label>Address line 1</Field.Label>
284
+ <Input>
285
+ <Input.Field autoComplete="address-line1" />
286
+ </Input>
287
+ </Field>
288
+ <Field>
289
+ <Field.Label>City</Field.Label>
290
+ <Input>
291
+ <Input.Field autoComplete="postal-address-locality" />
292
+ </Input>
293
+ </Field>
294
+ <Field>
295
+ <Field.Label>State</Field.Label>
296
+ <Select items={STATES}>
297
+ <Select.Trigger>
298
+ <Select.Value placeholder="Select a state" />
299
+ </Select.Trigger>
300
+ <Select.Content>
301
+ {STATES.map((state) => (
302
+ <Select.Item key={state.value} value={state.value}>
303
+ <Select.ItemLabel>{state.label}</Select.ItemLabel>
304
+ </Select.Item>
305
+ ))}
306
+ </Select.Content>
307
+ </Select>
308
+ </Field>
309
+ <Field>
310
+ <Field.Label>Zip code</Field.Label>
311
+ <Input>
312
+ <Input.Field autoComplete="postal-code" keyboardType="number-pad" />
313
+ </Input>
314
+ </Field>
315
+ </VStack>
316
+ </Card.Content>
317
+ </Card>
318
+
319
+ <Text color="secondary" size="sm">
320
+ Please read the terms of use carefully. They include important information about
321
+ receiving e-bills from this payee.
322
+ </Text>
323
+
324
+ <Checkbox.Group
325
+ value={consents}
326
+ onChange={setConsents}
327
+ aria-label="E-bill delivery"
328
+ >
329
+ <Card>
330
+ <Card.Content>
331
+ <Checkbox value="paperless">
332
+ <Checkbox.Label>
333
+ Send my bill here and stop delivering paper in the mail.
334
+ </Checkbox.Label>
335
+ </Checkbox>
336
+ </Card.Content>
337
+ </Card>
338
+ <Card>
339
+ <Card.Content>
340
+ <Checkbox value="trial">
341
+ <Checkbox.Label>
342
+ I want to try it first, for 15 days, and I will decide later if I want to
343
+ stop my paper bill. Send my bill both here and in the mail.
344
+ </Checkbox.Label>
345
+ </Checkbox>
346
+ </Card.Content>
347
+ </Card>
348
+ </Checkbox.Group>
349
+ </Form>
350
+ </Box>
351
+ </ScrollView>
352
+ <Divider />
353
+ <Box className="p-4">
354
+ <Button onPress={() => setOpen(false)}>
355
+ <Button.Label>Submit</Button.Label>
356
+ </Button>
357
+ </Box>
358
+ </FullScreenDialog.Content>
359
+ </FullScreenDialog.Popup>
360
+ </FullScreenDialog>
361
+ );
362
+ }
108
363
  ```
@@ -0,0 +1,13 @@
1
+ # 0.3.1 (https://design.candescent.com/docs/resources/release-notes/0.3.1)
2
+
3
+ DataGrid adds filterMode for server-side search and filters; IconButton adds warning, success, and info color options.
4
+
5
+ This release extends **DataGrid** for server-driven filtering and adds status-colored variants to **[IconButton](https://design.candescent.com/docs/components/icon-button/content.md)** and **[Toast](https://design.candescent.com/docs/components/toast/content.md)**.
6
+
7
+ ## What's new
8
+
9
+ **DataGrid** — Set `filterMode="server"` when pagination or sorting already runs on the server so quick search and column filters no longer run only against the rows on the current page. The grid emits `quickFilter` and `filterModel` changes and leaves filtering to your data source; `filterMode` defaults to match `paginationMode`, so server pagination does not silently filter a single page. `GridFilterItem.operator` is optional and defaults to `contains` ([#813](https://github.com/dfh-candescent/forge-design-system/pull/813)).
10
+
11
+ **[IconButton](https://design.candescent.com/docs/components/icon-button/content.md)** — `color` accepts `warning`, `success`, and `info` across solid, subtle, and ghost variants so icon actions can match semantic status without custom styling ([#793](https://github.com/dfh-candescent/forge-design-system/pull/793)).
12
+
13
+ **[Toast](https://design.candescent.com/docs/components/toast/content.md)** — The close control uses the toast’s semantic color so dismiss actions stay visually aligned with warning, success, info, and error toasts ([#793](https://github.com/dfh-candescent/forge-design-system/pull/793)).
@@ -0,0 +1,17 @@
1
+ # 0.3.2 (https://design.candescent.com/docs/resources/release-notes/0.3.2)
2
+
3
+ CurrencyInput adds an outline variant for bordered amount fields; BottomSheet opens reliably in optimized web production builds.
4
+
5
+ This release adds a bordered **[CurrencyInput](https://design.candescent.com/docs/components/currency-input/content.md)** style and fixes **[BottomSheet](https://design.candescent.com/docs/components/bottom-sheet/content.md)** sizing on optimized web builds.
6
+
7
+ ## What's new
8
+
9
+ **[CurrencyInput](https://design.candescent.com/docs/components/currency-input/content.md)** — Set `variant="outline"` for a bordered field that matches the outlined currency control in design specs. The outline layout keeps the currency prefix in secondary content color with tighter spacing to the amount, and pairs with `Field` for labels, hints, and validation like the default ghost style ([#826](https://github.com/dfh-candescent/forge-design-system/pull/826)).
10
+
11
+ ## Bug fixes
12
+
13
+ **[BottomSheet](https://design.candescent.com/docs/components/bottom-sheet/content.md)** — Modal sheets open at the correct height in production web bundles, so dynamic detents are no longer clipped below the backdrop and footers no longer cover scrollable content ([#829](https://github.com/dfh-candescent/forge-design-system/pull/829)).
14
+
15
+ ## Dependencies
16
+
17
+ **@cdx-ui/\*** — Upgrade every installed Forge UI package to `0.3.2` together when you adopt this release.
@@ -8,6 +8,8 @@ Notes are generated from the release commit itself, so the versions and commit r
8
8
  each entry are exact. Releases that contained nothing but backlog or dependency bookkeeping
9
9
  are omitted.
10
10
 
11
+ - [0.3.2](https://design.candescent.com/docs/resources/release-notes/0.3.2/content.md) — 2026-10-13 — CurrencyInput adds an outline variant for bordered amount fields; BottomSheet opens reliably in optimized web production builds.
12
+ - [0.3.1](https://design.candescent.com/docs/resources/release-notes/0.3.1/content.md) — 2026-10-13 — DataGrid adds filterMode for server-side search and filters; IconButton adds warning, success, and info color options.
11
13
  - [0.3.0](https://design.candescent.com/docs/resources/release-notes/0.3.0/content.md) — 2026-10-13 — Calendar and MonthCalendar year pickers scroll the full selectable range; BottomSheet adds fullBleed and updated Content and Footer padding.
12
14
  - [0.2.5](https://design.candescent.com/docs/resources/release-notes/0.2.5/content.md) — 2026-10-12 — Box is the supported layout primitive with hardened stacks; View from @cdx-ui/components is deprecated. Align Expo apps with SDK 57 and React Native 0.86.
13
15
  - [0.2.4](https://design.candescent.com/docs/resources/release-notes/0.2.4/content.md) — 2026-10-09 — ScrollShadow fades scroll edges on iOS, Android, and web; ListItem.SectionHeader adds a gradient variant for account-style lists.