@ceed/cds 1.22.2 → 1.22.4

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 (46) hide show
  1. package/dist/components/data-display/InfoSign.md +74 -91
  2. package/dist/components/data-display/Typography.md +363 -63
  3. package/dist/components/feedback/CircularProgress.md +257 -0
  4. package/dist/components/feedback/Dialog.md +8 -4
  5. package/dist/components/feedback/Modal.md +7 -3
  6. package/dist/components/feedback/Skeleton.md +280 -0
  7. package/dist/components/feedback/llms.txt +2 -0
  8. package/dist/components/inputs/ButtonGroup.md +115 -104
  9. package/dist/components/inputs/CurrencyInput.md +181 -8
  10. package/dist/components/inputs/DatePicker.md +108 -436
  11. package/dist/components/inputs/DateRangePicker.md +130 -496
  12. package/dist/components/inputs/FilterableCheckboxGroup.md +141 -20
  13. package/dist/components/inputs/FormControl.md +368 -0
  14. package/dist/components/inputs/IconButton.md +137 -88
  15. package/dist/components/inputs/Input.md +203 -77
  16. package/dist/components/inputs/MonthPicker.md +95 -427
  17. package/dist/components/inputs/MonthRangePicker.md +89 -471
  18. package/dist/components/inputs/PercentageInput.md +183 -19
  19. package/dist/components/inputs/RadioButton.md +163 -35
  20. package/dist/components/inputs/RadioList.md +241 -0
  21. package/dist/components/inputs/RadioTileGroup.md +146 -62
  22. package/dist/components/inputs/Select.md +219 -328
  23. package/dist/components/inputs/Slider.md +334 -0
  24. package/dist/components/inputs/Switch.md +143 -376
  25. package/dist/components/inputs/Textarea.md +209 -11
  26. package/dist/components/inputs/Uploader/Uploader.md +145 -66
  27. package/dist/components/inputs/llms.txt +3 -0
  28. package/dist/components/navigation/Breadcrumbs.md +57 -308
  29. package/dist/components/navigation/Drawer.md +180 -0
  30. package/dist/components/navigation/Dropdown.md +98 -215
  31. package/dist/components/navigation/IconMenuButton.md +40 -502
  32. package/dist/components/navigation/InsetDrawer.md +281 -650
  33. package/dist/components/navigation/Link.md +31 -348
  34. package/dist/components/navigation/Menu.md +92 -285
  35. package/dist/components/navigation/MenuButton.md +55 -448
  36. package/dist/components/navigation/Pagination.md +47 -338
  37. package/dist/components/navigation/Stepper.md +160 -28
  38. package/dist/components/navigation/Tabs.md +57 -316
  39. package/dist/components/surfaces/Accordions.md +49 -804
  40. package/dist/components/surfaces/Card.md +97 -157
  41. package/dist/components/surfaces/Divider.md +83 -234
  42. package/dist/components/surfaces/Sheet.md +152 -327
  43. package/dist/guides/ThemeProvider.md +89 -0
  44. package/dist/guides/llms.txt +9 -0
  45. package/dist/llms.txt +8 -0
  46. package/package.json +1 -1
@@ -0,0 +1,257 @@
1
+ # CircularProgress
2
+
3
+ ## Introduction
4
+
5
+ The CircularProgress component displays a circular loading indicator. It is based on Joy UI's CircularProgress and supports both indeterminate (spinning) and determinate (progress percentage) modes. Use it to provide visual feedback during asynchronous operations like data fetching, file uploads, or form submissions.
6
+
7
+ ```tsx
8
+ <CircularProgress />
9
+ ```
10
+
11
+ | Field | Description | Default |
12
+ | ----------- | ----------- | ------- |
13
+ | size | — | — |
14
+ | color | — | — |
15
+ | variant | — | — |
16
+ | value | — | — |
17
+ | determinate | — | — |
18
+ | thickness | — | — |
19
+
20
+ ## Usage
21
+
22
+ ```tsx
23
+ import { CircularProgress } from '@ceed/cds';
24
+
25
+ function MyComponent() {
26
+ return <CircularProgress />;
27
+ }
28
+ ```
29
+
30
+ ## Sizes
31
+
32
+ CircularProgress supports three sizes: `sm`, `md` (default), and `lg`.
33
+
34
+ ```tsx
35
+ <>
36
+ <CircularProgress size="sm" />
37
+ <CircularProgress size="md" />
38
+ <CircularProgress size="lg" />
39
+ </>
40
+ ```
41
+
42
+ ```tsx
43
+ <CircularProgress size="sm" />
44
+ <CircularProgress size="md" />
45
+ <CircularProgress size="lg" />
46
+ ```
47
+
48
+ ## Colors
49
+
50
+ Five semantic colors are available via the `color` prop. The default is `primary`.
51
+
52
+ ```tsx
53
+ <>
54
+ <CircularProgress color="primary" />
55
+ <CircularProgress color="neutral" />
56
+ <CircularProgress color="danger" />
57
+ <CircularProgress color="success" />
58
+ <CircularProgress color="warning" />
59
+ </>
60
+ ```
61
+
62
+ ```tsx
63
+ <CircularProgress color="primary" />
64
+ <CircularProgress color="neutral" />
65
+ <CircularProgress color="danger" />
66
+ <CircularProgress color="success" />
67
+ <CircularProgress color="warning" />
68
+ ```
69
+
70
+ ## Variants
71
+
72
+ Four visual variants are supported: `solid`, `soft` (default), `outlined`, and `plain`.
73
+
74
+ ```tsx
75
+ <>
76
+ <CircularProgress variant="solid" />
77
+ <CircularProgress variant="soft" />
78
+ <CircularProgress variant="outlined" />
79
+ <CircularProgress variant="plain" />
80
+ </>
81
+ ```
82
+
83
+ ```tsx
84
+ <CircularProgress variant="solid" />
85
+ <CircularProgress variant="soft" />
86
+ <CircularProgress variant="outlined" />
87
+ <CircularProgress variant="plain" />
88
+ ```
89
+
90
+ ## Determinate Mode
91
+
92
+ Set `determinate` to `true` and provide a `value` (0–100) to display specific progress.
93
+
94
+ ```tsx
95
+ <>
96
+ <CircularProgress determinate value={25} />
97
+ <CircularProgress determinate value={50} />
98
+ <CircularProgress determinate value={75} />
99
+ <CircularProgress determinate value={100} />
100
+ </>
101
+ ```
102
+
103
+ ```tsx
104
+ <CircularProgress determinate value={25} />
105
+ <CircularProgress determinate value={50} />
106
+ <CircularProgress determinate value={75} />
107
+ <CircularProgress determinate value={100} />
108
+ ```
109
+
110
+ ## With Label
111
+
112
+ Pass children to display a label inside the progress ring. This is useful for showing the current percentage.
113
+
114
+ ```tsx
115
+ <CircularProgress determinate value={66}>
116
+ <Typography level="body-xs">66%</Typography>
117
+ </CircularProgress>
118
+ ```
119
+
120
+ ```tsx
121
+ <CircularProgress determinate value={66}>
122
+ <Typography level="body-xs">66%</Typography>
123
+ </CircularProgress>
124
+ ```
125
+
126
+ ## In Button
127
+
128
+ Use CircularProgress inside a Button to indicate a loading state.
129
+
130
+ ```tsx
131
+ <>
132
+ <Button startDecorator={<CircularProgress variant="solid" size="sm" />}>
133
+ Loading…
134
+ </Button>
135
+ <Button disabled>
136
+ <CircularProgress variant="soft" size="sm" />
137
+ </Button>
138
+ </>
139
+ ```
140
+
141
+ ```tsx
142
+ <Button startDecorator={<CircularProgress variant="solid" size="sm" />}>
143
+ Loading…
144
+ </Button>
145
+
146
+ <Button disabled>
147
+ <CircularProgress variant="soft" size="sm" />
148
+ </Button>
149
+ ```
150
+
151
+ ## Thickness
152
+
153
+ Control the stroke thickness of the progress ring with the `thickness` prop.
154
+
155
+ ```tsx
156
+ <>
157
+ <CircularProgress thickness={2} />
158
+ <CircularProgress thickness={4} />
159
+ <CircularProgress thickness={8} />
160
+ </>
161
+ ```
162
+
163
+ ```tsx
164
+ <CircularProgress thickness={2} />
165
+ <CircularProgress thickness={4} />
166
+ <CircularProgress thickness={8} />
167
+ ```
168
+
169
+ ## Common Use Cases
170
+
171
+ ### Data Fetching Indicator
172
+
173
+ ```tsx
174
+ function DataLoader() {
175
+ const [loading, setLoading] = React.useState(true);
176
+ const [data, setData] = React.useState(null);
177
+
178
+ React.useEffect(() => {
179
+ fetchData().then((result) => {
180
+ setData(result);
181
+ setLoading(false);
182
+ });
183
+ }, []);
184
+
185
+ if (loading) {
186
+ return (
187
+ <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
188
+ <CircularProgress />
189
+ </Box>
190
+ );
191
+ }
192
+
193
+ return <DataView data={data} />;
194
+ }
195
+ ```
196
+
197
+ ### File Upload Progress
198
+
199
+ ```tsx
200
+ function FileUpload({ progress }: { progress: number }) {
201
+ return (
202
+ <Stack direction="row" alignItems="center" spacing={2}>
203
+ <CircularProgress determinate value={progress} size="lg">
204
+ <Typography level="body-xs">{progress}%</Typography>
205
+ </CircularProgress>
206
+ <Typography level="body-md">Uploading file…</Typography>
207
+ </Stack>
208
+ );
209
+ }
210
+ ```
211
+
212
+ ### Inline Loading Button
213
+
214
+ ```tsx
215
+ function SubmitButton({ isSubmitting }: { isSubmitting: boolean }) {
216
+ return (
217
+ <Button
218
+ disabled={isSubmitting}
219
+ startDecorator={
220
+ isSubmitting ? <CircularProgress variant="solid" size="sm" /> : null
221
+ }
222
+ >
223
+ {isSubmitting ? 'Submitting…' : 'Submit'}
224
+ </Button>
225
+ );
226
+ }
227
+ ```
228
+
229
+ ## Best Practices
230
+
231
+ 1. **Use indeterminate for unknown durations**: When you cannot estimate progress, use the default spinning mode. Switch to determinate only when you can track actual progress.
232
+
233
+ ```tsx
234
+ // ✅ Unknown duration — indeterminate
235
+ <CircularProgress />
236
+
237
+ // ✅ Known progress — determinate
238
+ <CircularProgress determinate value={uploadProgress} />
239
+
240
+ // ❌ Don't use determinate with a static value for loading
241
+ <CircularProgress determinate value={50} />
242
+ ```
243
+
244
+ 2. **Size appropriately**: Use `sm` for inline contexts (buttons, table cells), `md` for general use, and `lg` for prominent page-level loading states.
245
+
246
+ 3. **Match variant with context**: Use `solid` variant inside solid buttons, and `soft` (default) for standalone usage to maintain visual consistency.
247
+
248
+ 4. **Add descriptive text**: Always pair the indicator with text or an `aria-label` so users understand what is loading.
249
+
250
+ 5. **Avoid multiple spinners**: Show one loading indicator per distinct loading region. Multiple spinners on a page create visual clutter.
251
+
252
+ ## Accessibility
253
+
254
+ - CircularProgress has `role="progressbar"` by default.
255
+ - In determinate mode, `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` are set automatically.
256
+ - Add `aria-label` or nearby descriptive text to explain what is loading (e.g., `aria-label="Loading user data"`).
257
+ - Ensure sufficient color contrast between the progress ring and its background, especially with the `plain` variant.
@@ -98,12 +98,16 @@ DialogFrame can be used without a Modal wrapper for embedding dialog-style layou
98
98
 
99
99
  > ⚠️ **Important** ⚠️
100
100
  >
101
- > When using DialogFrame without Modal, the parent container **must** provide explicit `width` and `height` values.
102
- > DialogFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay.
103
- > Without these constraints, the component will not render with correct dimensions.
101
+ > When using DialogFrame without Modal, the parent container **must** meet the following requirements:
102
+ >
103
+ > - `position: 'relative'` DialogFrame uses absolute positioning internally
104
+ > - Explicit `width` and `height` — `height` must be an absolute value (e.g., `300`, `'400px'`), not a relative value like `%` or `auto`
105
+ >
106
+ > DialogFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay. Without these constraints, the component will not render with correct dimensions.
104
107
 
105
108
  ```tsx
106
109
  <Box sx={{
110
+ position: 'relative',
107
111
  width: 480,
108
112
  height: 300
109
113
  }}>
@@ -124,7 +128,7 @@ import { DialogFrame, Button, Box } from '@ceed/cds';
124
128
  // Standalone usage requires explicit container dimensions
125
129
  function EmbeddedDialog() {
126
130
  return (
127
- <Box sx={{ width: 480, height: 300 }}>
131
+ <Box sx={{ position: 'relative', width: 480, height: 300 }}>
128
132
  <DialogFrame
129
133
  title="Settings"
130
134
  actions={
@@ -442,12 +442,16 @@ ModalFrame can be used without a Modal wrapper for embedding dialog-style layout
442
442
 
443
443
  > ⚠️ **Important** ⚠️
444
444
  >
445
- > When using ModalFrame without Modal, the parent container **must** provide explicit `width` and `height` values.
446
- > ModalFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay.
447
- > Without these constraints, the component will not render with correct dimensions.
445
+ > When using ModalFrame without Modal, the parent container **must** meet the following requirements:
446
+ >
447
+ > - `position: 'relative'` ModalFrame uses absolute positioning internally
448
+ > - Explicit `width` and `height` — `height` must be an absolute value (e.g., `300`, `'400px'`), not a relative value like `%` or `auto`
449
+ >
450
+ > ModalFrame inherits its dimensions from `ModalDialog`, which normally receives sizing from the Modal overlay. Without these constraints, the component will not render with correct dimensions.
448
451
 
449
452
  ```tsx
450
453
  <Box sx={{
454
+ position: 'relative',
451
455
  width: 480,
452
456
  height: 300
453
457
  }}>
@@ -0,0 +1,280 @@
1
+ # Skeleton
2
+
3
+ ## Introduction
4
+
5
+ The Skeleton component provides placeholder previews of content before data is loaded. It is based on Joy UI's Skeleton and helps reduce perceived loading time by showing an approximation of the page layout. Skeletons improve the user experience by preventing layout shifts and giving users a visual cue that content is on its way.
6
+
7
+ ```tsx
8
+ <Skeleton
9
+ variant="rectangular"
10
+ width={200}
11
+ height={24}
12
+ />
13
+ ```
14
+
15
+ | Field | Description | Default |
16
+ | ---------------------------- | ----------- | ------- |
17
+ | Controls resolved at runtime | — | — |
18
+
19
+ ## Usage
20
+
21
+ ```tsx
22
+ import { Skeleton } from '@ceed/cds';
23
+
24
+ function MyComponent() {
25
+ return <Skeleton variant="rectangular" width={200} height={24} />;
26
+ }
27
+ ```
28
+
29
+ ## Variants
30
+
31
+ Skeleton supports three variants: `rectangular`, `circular`, and `text`.
32
+
33
+ - **rectangular**: Block-shaped placeholder for images, cards, and content areas.
34
+ - **circular**: Round placeholder for avatars and icons.
35
+ - **text**: Matches the height and spacing of text content at a given `level`.
36
+
37
+ ```tsx
38
+ <>
39
+ <Skeleton variant="rectangular" width={200} height={24} />
40
+ <Skeleton variant="circular" width={48} height={48} />
41
+ <Skeleton variant="text" width={200} />
42
+ </>
43
+ ```
44
+
45
+ ```tsx
46
+ <Skeleton variant="rectangular" width={200} height={24} />
47
+ <Skeleton variant="circular" width={48} height={48} />
48
+ <Skeleton variant="text" width={200} />
49
+ ```
50
+
51
+ ## Animations
52
+
53
+ Skeleton supports `wave` (default) and `pulse` animations. Set `animation={false}` to disable animation entirely.
54
+
55
+ ```tsx
56
+ <Stack gap={3}>
57
+ <Box>
58
+ <Typography level="body-sm" sx={{
59
+ mb: 1
60
+ }}>
61
+ Wave (default)
62
+ </Typography>
63
+ <Skeleton animation="wave" variant="rectangular" width={200} height={24} />
64
+ </Box>
65
+ <Box>
66
+ <Typography level="body-sm" sx={{
67
+ mb: 1
68
+ }}>
69
+ Pulse
70
+ </Typography>
71
+ <Skeleton animation="pulse" variant="rectangular" width={200} height={24} />
72
+ </Box>
73
+ <Box>
74
+ <Typography level="body-sm" sx={{
75
+ mb: 1
76
+ }}>
77
+ No animation (false)
78
+ </Typography>
79
+ <Skeleton animation={false} variant="rectangular" width={200} height={24} />
80
+ </Box>
81
+ </Stack>
82
+ ```
83
+
84
+ ```tsx
85
+ <Skeleton animation="wave" variant="rectangular" width={200} height={24} />
86
+ <Skeleton animation="pulse" variant="rectangular" width={200} height={24} />
87
+ <Skeleton animation={false} variant="rectangular" width={200} height={24} />
88
+ ```
89
+
90
+ ## Text Skeleton
91
+
92
+ Use `variant="text"` with the `level` prop to match Typography sizing. This is useful for creating text content placeholders that accurately reflect the final layout.
93
+
94
+ ```tsx
95
+ <Stack gap={1} sx={{
96
+ width: 300
97
+ }}>
98
+ <Skeleton variant="text" level="h3" />
99
+ <Skeleton variant="text" level="body-md" />
100
+ <Skeleton variant="text" level="body-md" />
101
+ <Skeleton variant="text" level="body-md" width="80%" />
102
+ </Stack>
103
+ ```
104
+
105
+ ```tsx
106
+ <Skeleton variant="text" level="h3" />
107
+ <Skeleton variant="text" level="body-md" />
108
+ <Skeleton variant="text" level="body-md" />
109
+ <Skeleton variant="text" level="body-md" width="80%" />
110
+ ```
111
+
112
+ ## Card Skeleton
113
+
114
+ Compose multiple Skeleton elements to create placeholder layouts for complex components like cards.
115
+
116
+ ```tsx
117
+ <Box sx={{
118
+ width: 300,
119
+ p: 2,
120
+ border: '1px solid',
121
+ borderColor: 'divider',
122
+ borderRadius: 'sm'
123
+ }}>
124
+ <Skeleton variant="rectangular" width="100%" height={140} sx={{
125
+ borderRadius: 'sm',
126
+ mb: 2
127
+ }} />
128
+ <Skeleton variant="text" level="title-md" sx={{
129
+ mb: 1
130
+ }} />
131
+ <Skeleton variant="text" level="body-sm" />
132
+ <Skeleton variant="text" level="body-sm" width="60%" />
133
+ <Stack direction="row" gap={1} sx={{
134
+ mt: 2
135
+ }}>
136
+ <Skeleton variant="rectangular" width={80} height={32} sx={{
137
+ borderRadius: 'sm'
138
+ }} />
139
+ <Skeleton variant="rectangular" width={80} height={32} sx={{
140
+ borderRadius: 'sm'
141
+ }} />
142
+ </Stack>
143
+ </Box>
144
+ ```
145
+
146
+ ## Data Loading List
147
+
148
+ Combine circular and text skeletons to represent list items during loading.
149
+
150
+ ```tsx
151
+ <Stack gap={2} sx={{
152
+ width: 400
153
+ }}>
154
+ {[1, 2, 3].map(i => <Stack key={i} direction="row" gap={2} alignItems="center">
155
+ <Skeleton variant="circular" width={40} height={40} />
156
+ <Box sx={{
157
+ flex: 1
158
+ }}>
159
+ <Skeleton variant="text" level="title-sm" width="60%" />
160
+ <Skeleton variant="text" level="body-xs" width="40%" />
161
+ </Box>
162
+ </Stack>)}
163
+ </Stack>
164
+ ```
165
+
166
+ ## Inline Wrapping
167
+
168
+ Wrap existing content with Skeleton to overlay it while loading. Set the `loading` prop to control visibility.
169
+
170
+ ```tsx
171
+ <Typography level="body-md">
172
+ <Skeleton loading>
173
+ This text will be hidden behind a skeleton while loading.
174
+ </Skeleton>
175
+ </Typography>
176
+ ```
177
+
178
+ ```tsx
179
+ <Typography level="body-md">
180
+ <Skeleton loading>
181
+ This text will be hidden behind a skeleton while loading.
182
+ </Skeleton>
183
+ </Typography>
184
+ ```
185
+
186
+ ## Common Use Cases
187
+
188
+ ### Page Content Loading
189
+
190
+ ```tsx
191
+ function PageSkeleton() {
192
+ return (
193
+ <Stack gap={3}>
194
+ <Skeleton variant="text" level="h1" width="50%" />
195
+ <Skeleton variant="text" level="body-md" />
196
+ <Skeleton variant="text" level="body-md" />
197
+ <Skeleton variant="text" level="body-md" width="75%" />
198
+
199
+ <Skeleton variant="rectangular" width="100%" height={200} sx={{ borderRadius: 'sm' }} />
200
+
201
+ <Skeleton variant="text" level="body-md" />
202
+ <Skeleton variant="text" level="body-md" />
203
+ </Stack>
204
+ );
205
+ }
206
+ ```
207
+
208
+ ### User List Loading
209
+
210
+ ```tsx
211
+ function UserListSkeleton({ count = 5 }: { count?: number }) {
212
+ return (
213
+ <Stack gap={2}>
214
+ {Array.from({ length: count }).map((_, i) => (
215
+ <Stack key={i} direction="row" gap={2} alignItems="center">
216
+ <Skeleton variant="circular" width={40} height={40} />
217
+ <Box sx={{ flex: 1 }}>
218
+ <Skeleton variant="text" level="title-sm" width="40%" />
219
+ <Skeleton variant="text" level="body-xs" width="25%" />
220
+ </Box>
221
+ </Stack>
222
+ ))}
223
+ </Stack>
224
+ );
225
+ }
226
+ ```
227
+
228
+ ### Conditional Rendering
229
+
230
+ ```tsx
231
+ function UserProfile({ loading, user }: { loading: boolean; user?: User }) {
232
+ return (
233
+ <Stack direction="row" gap={2} alignItems="center">
234
+ {loading ? (
235
+ <Skeleton variant="circular" width={48} height={48} />
236
+ ) : (
237
+ <Avatar src={user?.avatar} />
238
+ )}
239
+ <Box>
240
+ <Typography level="title-md">
241
+ <Skeleton loading={loading}>{user?.name || 'Placeholder Name'}</Skeleton>
242
+ </Typography>
243
+ <Typography level="body-sm">
244
+ <Skeleton loading={loading}>{user?.email || 'email@example.com'}</Skeleton>
245
+ </Typography>
246
+ </Box>
247
+ </Stack>
248
+ );
249
+ }
250
+ ```
251
+
252
+ ## Best Practices
253
+
254
+ 1. **Match the final layout**: Skeleton placeholders should closely approximate the size and position of the real content to prevent layout shifts.
255
+
256
+ ```tsx
257
+ // ✅ Matches the actual content structure
258
+ <Stack gap={1}>
259
+ <Skeleton variant="text" level="title-md" width="60%" />
260
+ <Skeleton variant="text" level="body-sm" />
261
+ </Stack>
262
+
263
+ // ❌ Generic rectangle that doesn't match
264
+ <Skeleton variant="rectangular" width={300} height={100} />
265
+ ```
266
+
267
+ 2. **Use `variant="text"` with `level`**: When replacing Typography, use the text variant with the matching level to get accurate line heights.
268
+
269
+ 3. **Avoid over-skeletonizing**: Only skeleton the main content areas. Don't add skeletons for static elements like navigation or headers that are always present.
270
+
271
+ 4. **Use consistent animation**: Keep the same animation type (`wave` or `pulse`) across the entire application for a cohesive loading experience.
272
+
273
+ 5. **Set appropriate widths**: Vary skeleton widths (e.g., 60%, 80%, 100%) to mimic natural text line lengths rather than using uniform widths.
274
+
275
+ ## Accessibility
276
+
277
+ - Skeleton elements are purely decorative. Screen readers should focus on the loading state announcement, not individual skeleton elements.
278
+ - Use `aria-busy="true"` on the container element while content is loading.
279
+ - Provide an `aria-label` or visually hidden text that describes the loading state (e.g., "Loading user profile").
280
+ - Ensure the animation respects `prefers-reduced-motion` — Joy UI handles this automatically.
@@ -3,8 +3,10 @@
3
3
  ## Documentation
4
4
 
5
5
  - [Alert](./Alert.md)
6
+ - [CircularProgress](./CircularProgress.md)
6
7
  - [DialogFrame](./Dialog.md)
7
8
  - [Modal](./Modal.md)
9
+ - [Skeleton](./Skeleton.md)
8
10
 
9
11
  ## Parent
10
12