@neynar/ui 1.0.0 → 1.0.2
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.
- package/README.md +1 -1
- package/context7.json +17 -0
- package/llm/components/accordion.llm.md +205 -0
- package/llm/components/alert-dialog.llm.md +289 -0
- package/llm/components/alert.llm.md +310 -0
- package/llm/components/aspect-ratio.llm.md +110 -0
- package/llm/components/avatar.llm.md +282 -0
- package/llm/components/badge.llm.md +185 -0
- package/llm/components/blockquote.llm.md +86 -0
- package/llm/components/breadcrumb.llm.md +245 -0
- package/llm/components/button-group.llm.md +248 -0
- package/llm/components/button.llm.md +247 -0
- package/llm/components/calendar.llm.md +252 -0
- package/llm/components/card.llm.md +356 -0
- package/llm/components/carousel.llm.md +281 -0
- package/llm/components/chart.llm.md +278 -0
- package/llm/components/checkbox.llm.md +234 -0
- package/llm/components/code.llm.md +75 -0
- package/llm/components/collapsible.llm.md +271 -0
- package/llm/components/color-mode.llm.md +196 -0
- package/llm/components/combobox.llm.md +346 -0
- package/llm/components/command.llm.md +353 -0
- package/llm/components/context-menu.llm.md +368 -0
- package/llm/components/dialog.llm.md +283 -0
- package/llm/components/drawer.llm.md +326 -0
- package/llm/components/dropdown-menu.llm.md +404 -0
- package/llm/components/empty.llm.md +282 -0
- package/llm/components/field.llm.md +303 -0
- package/llm/components/first-light.llm.md +129 -0
- package/llm/components/hover-card.llm.md +278 -0
- package/llm/components/input-group.llm.md +334 -0
- package/llm/components/input-otp.llm.md +270 -0
- package/llm/components/input.llm.md +197 -0
- package/llm/components/item.llm.md +347 -0
- package/llm/components/kbd.llm.md +221 -0
- package/llm/components/label.llm.md +219 -0
- package/llm/components/menubar.llm.md +378 -0
- package/llm/components/navigation-menu.llm.md +320 -0
- package/llm/components/pagination.llm.md +337 -0
- package/llm/components/popover.llm.md +278 -0
- package/llm/components/progress.llm.md +259 -0
- package/llm/components/radio-group.llm.md +269 -0
- package/llm/components/resizable.llm.md +222 -0
- package/llm/components/scroll-area.llm.md +290 -0
- package/llm/components/select.llm.md +338 -0
- package/llm/components/separator.llm.md +129 -0
- package/llm/components/sheet.llm.md +275 -0
- package/llm/components/sidebar.llm.md +528 -0
- package/llm/components/skeleton.llm.md +140 -0
- package/llm/components/slider.llm.md +213 -0
- package/llm/components/sonner.llm.md +299 -0
- package/llm/components/spinner.llm.md +187 -0
- package/llm/components/switch.llm.md +258 -0
- package/llm/components/table.llm.md +334 -0
- package/llm/components/tabs.llm.md +245 -0
- package/llm/components/text.llm.md +108 -0
- package/llm/components/textarea.llm.md +236 -0
- package/llm/components/title.llm.md +88 -0
- package/llm/components/toggle-group.llm.md +228 -0
- package/llm/components/toggle.llm.md +235 -0
- package/llm/components/tooltip.llm.md +191 -0
- package/llm/contributing.llm.md +273 -0
- package/llm/hooks.llm.md +91 -0
- package/llm/index.llm.md +178 -0
- package/llm/theming.llm.md +381 -0
- package/llm/utilities.llm.md +97 -0
- package/llms-full.txt +15995 -0
- package/llms.txt +182 -0
- package/package.json +6 -2
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Carousel
|
|
2
|
+
|
|
3
|
+
Embla-powered carousel component with horizontal/vertical scrolling, keyboard navigation, and programmatic control.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import {
|
|
9
|
+
Carousel,
|
|
10
|
+
CarouselContent,
|
|
11
|
+
CarouselItem,
|
|
12
|
+
CarouselNext,
|
|
13
|
+
CarouselPrevious,
|
|
14
|
+
type CarouselApi,
|
|
15
|
+
} from "@neynar/ui/carousel"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Anatomy
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<Carousel>
|
|
22
|
+
<CarouselContent>
|
|
23
|
+
<CarouselItem>...</CarouselItem>
|
|
24
|
+
<CarouselItem>...</CarouselItem>
|
|
25
|
+
</CarouselContent>
|
|
26
|
+
<CarouselPrevious />
|
|
27
|
+
<CarouselNext />
|
|
28
|
+
</Carousel>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Components
|
|
32
|
+
|
|
33
|
+
| Component | Description |
|
|
34
|
+
|-----------|-------------|
|
|
35
|
+
| Carousel | Root container, manages state and keyboard navigation |
|
|
36
|
+
| CarouselContent | Scrollable container with overflow handling |
|
|
37
|
+
| CarouselItem | Individual slide wrapper |
|
|
38
|
+
| CarouselPrevious | Previous button (auto-disabled at start) |
|
|
39
|
+
| CarouselNext | Next button (auto-disabled at end) |
|
|
40
|
+
|
|
41
|
+
## Props
|
|
42
|
+
|
|
43
|
+
### Carousel
|
|
44
|
+
|
|
45
|
+
| Prop | Type | Default | Description |
|
|
46
|
+
|------|------|---------|-------------|
|
|
47
|
+
| orientation | "horizontal" \| "vertical" | "horizontal" | Scroll direction |
|
|
48
|
+
| opts | EmblaOptionsType | - | Embla carousel options (loop, align, etc.) |
|
|
49
|
+
| plugins | EmblaPluginType[] | - | Embla carousel plugins |
|
|
50
|
+
| setApi | (api: CarouselApi) => void | - | Callback to receive carousel API for programmatic control |
|
|
51
|
+
|
|
52
|
+
### CarouselContent
|
|
53
|
+
|
|
54
|
+
Standard div props. Automatically applies flex layout and orientation-based spacing.
|
|
55
|
+
|
|
56
|
+
### CarouselItem
|
|
57
|
+
|
|
58
|
+
Standard div props. Use Tailwind `basis-*` classes to control items per view:
|
|
59
|
+
- `basis-full` - 1 item per view (default)
|
|
60
|
+
- `md:basis-1/2` - 2 items on medium screens
|
|
61
|
+
- `lg:basis-1/3` - 3 items on large screens
|
|
62
|
+
|
|
63
|
+
### CarouselPrevious / CarouselNext
|
|
64
|
+
|
|
65
|
+
Extends Button props with defaults: `variant="outline"`, `size="icon-sm"`.
|
|
66
|
+
|
|
67
|
+
## Embla Options (opts prop)
|
|
68
|
+
|
|
69
|
+
Common options passed to `opts`:
|
|
70
|
+
|
|
71
|
+
| Option | Type | Description |
|
|
72
|
+
|--------|------|-------------|
|
|
73
|
+
| loop | boolean | Enable infinite looping |
|
|
74
|
+
| align | "start" \| "center" \| "end" | Slide alignment |
|
|
75
|
+
| slidesToScroll | number | Number of slides to scroll at once |
|
|
76
|
+
| skipSnaps | boolean | Skip snapping to slides that are out of view |
|
|
77
|
+
|
|
78
|
+
Full options: https://www.embla-carousel.com/api/options/
|
|
79
|
+
|
|
80
|
+
## Data Attributes
|
|
81
|
+
|
|
82
|
+
| Attribute | Element | Description |
|
|
83
|
+
|-----------|---------|-------------|
|
|
84
|
+
| data-slot="carousel" | Carousel root | For styling hooks |
|
|
85
|
+
| data-slot="carousel-content" | Content wrapper | For styling hooks |
|
|
86
|
+
| data-slot="carousel-item" | Individual item | For styling hooks |
|
|
87
|
+
| data-slot="carousel-previous" | Previous button | For styling hooks |
|
|
88
|
+
| data-slot="carousel-next" | Next button | For styling hooks |
|
|
89
|
+
|
|
90
|
+
## Examples
|
|
91
|
+
|
|
92
|
+
### Basic Usage
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Carousel className="w-full max-w-sm">
|
|
96
|
+
<CarouselContent>
|
|
97
|
+
{items.map((item, index) => (
|
|
98
|
+
<CarouselItem key={index}>
|
|
99
|
+
<Card>
|
|
100
|
+
<CardContent>{item.content}</CardContent>
|
|
101
|
+
</Card>
|
|
102
|
+
</CarouselItem>
|
|
103
|
+
))}
|
|
104
|
+
</CarouselContent>
|
|
105
|
+
<CarouselPrevious />
|
|
106
|
+
<CarouselNext />
|
|
107
|
+
</Carousel>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Multiple Items Per View
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<Carousel className="w-full">
|
|
114
|
+
<CarouselContent>
|
|
115
|
+
{items.map((item, index) => (
|
|
116
|
+
<CarouselItem key={index} className="md:basis-1/2 lg:basis-1/3">
|
|
117
|
+
<Card>
|
|
118
|
+
<CardContent>{item.content}</CardContent>
|
|
119
|
+
</Card>
|
|
120
|
+
</CarouselItem>
|
|
121
|
+
))}
|
|
122
|
+
</CarouselContent>
|
|
123
|
+
<CarouselPrevious />
|
|
124
|
+
<CarouselNext />
|
|
125
|
+
</Carousel>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Loop Mode
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<Carousel
|
|
132
|
+
opts={{
|
|
133
|
+
loop: true,
|
|
134
|
+
align: "start",
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<CarouselContent>
|
|
138
|
+
{items.map((item, index) => (
|
|
139
|
+
<CarouselItem key={index}>
|
|
140
|
+
<Card>
|
|
141
|
+
<CardContent>{item.content}</CardContent>
|
|
142
|
+
</Card>
|
|
143
|
+
</CarouselItem>
|
|
144
|
+
))}
|
|
145
|
+
</CarouselContent>
|
|
146
|
+
<CarouselPrevious />
|
|
147
|
+
<CarouselNext />
|
|
148
|
+
</Carousel>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Vertical Orientation
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<Carousel orientation="vertical" className="w-full max-w-xs">
|
|
155
|
+
<CarouselContent className="h-[300px]">
|
|
156
|
+
{items.map((item, index) => (
|
|
157
|
+
<CarouselItem key={index}>
|
|
158
|
+
<Card>
|
|
159
|
+
<CardContent>{item.content}</CardContent>
|
|
160
|
+
</Card>
|
|
161
|
+
</CarouselItem>
|
|
162
|
+
))}
|
|
163
|
+
</CarouselContent>
|
|
164
|
+
<CarouselPrevious />
|
|
165
|
+
<CarouselNext />
|
|
166
|
+
</Carousel>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Programmatic Control with Custom Indicators
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
function ControlledCarousel() {
|
|
173
|
+
const [api, setApi] = useState<CarouselApi>()
|
|
174
|
+
const [current, setCurrent] = useState(0)
|
|
175
|
+
const [count, setCount] = useState(0)
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (!api) return
|
|
179
|
+
|
|
180
|
+
setCount(api.scrollSnapList().length)
|
|
181
|
+
setCurrent(api.selectedScrollSnap())
|
|
182
|
+
|
|
183
|
+
api.on("select", () => {
|
|
184
|
+
setCurrent(api.selectedScrollSnap())
|
|
185
|
+
})
|
|
186
|
+
}, [api])
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div className="space-y-4">
|
|
190
|
+
<Carousel setApi={setApi} className="w-full max-w-sm">
|
|
191
|
+
<CarouselContent>
|
|
192
|
+
{items.map((item, index) => (
|
|
193
|
+
<CarouselItem key={index}>
|
|
194
|
+
<Card>
|
|
195
|
+
<CardContent>{item.content}</CardContent>
|
|
196
|
+
</Card>
|
|
197
|
+
</CarouselItem>
|
|
198
|
+
))}
|
|
199
|
+
</CarouselContent>
|
|
200
|
+
<CarouselPrevious />
|
|
201
|
+
<CarouselNext />
|
|
202
|
+
</Carousel>
|
|
203
|
+
|
|
204
|
+
{/* Custom indicators */}
|
|
205
|
+
<div className="flex justify-center gap-2">
|
|
206
|
+
{Array.from({ length: count }).map((_, index) => (
|
|
207
|
+
<button
|
|
208
|
+
key={index}
|
|
209
|
+
className={cn(
|
|
210
|
+
"size-2 rounded-full transition-all",
|
|
211
|
+
index === current
|
|
212
|
+
? "bg-primary w-6"
|
|
213
|
+
: "bg-muted-foreground/30 hover:bg-muted-foreground/50"
|
|
214
|
+
)}
|
|
215
|
+
onClick={() => api?.scrollTo(index)}
|
|
216
|
+
aria-label={`Go to slide ${index + 1}`}
|
|
217
|
+
/>
|
|
218
|
+
))}
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<p className="text-center text-sm text-muted-foreground">
|
|
222
|
+
Slide {current + 1} of {count}
|
|
223
|
+
</p>
|
|
224
|
+
</div>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Keyboard
|
|
230
|
+
|
|
231
|
+
| Key | Action |
|
|
232
|
+
|-----|--------|
|
|
233
|
+
| ArrowLeft | Previous slide (horizontal) |
|
|
234
|
+
| ArrowRight | Next slide (horizontal) |
|
|
235
|
+
|
|
236
|
+
## Accessibility
|
|
237
|
+
|
|
238
|
+
- Root has `role="region"` and `aria-roledescription="carousel"`
|
|
239
|
+
- Items have `role="group"` and `aria-roledescription="slide"`
|
|
240
|
+
- Navigation buttons include screen reader text ("Previous slide", "Next slide")
|
|
241
|
+
- Buttons automatically disabled when navigation not available
|
|
242
|
+
- Full keyboard support with arrow key navigation
|
|
243
|
+
|
|
244
|
+
## useCarousel Hook
|
|
245
|
+
|
|
246
|
+
Access carousel context in child components:
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
function CustomCarouselControl() {
|
|
250
|
+
const { api, scrollPrev, scrollNext, canScrollPrev, canScrollNext } = useCarousel()
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<div>
|
|
254
|
+
<Button onClick={scrollPrev} disabled={!canScrollPrev}>Prev</Button>
|
|
255
|
+
<Button onClick={scrollNext} disabled={!canScrollNext}>Next</Button>
|
|
256
|
+
</div>
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## CarouselApi Methods
|
|
262
|
+
|
|
263
|
+
Key methods available via `setApi` callback:
|
|
264
|
+
|
|
265
|
+
| Method | Description |
|
|
266
|
+
|--------|-------------|
|
|
267
|
+
| `scrollTo(index)` | Scroll to specific slide |
|
|
268
|
+
| `scrollPrev()` | Scroll to previous slide |
|
|
269
|
+
| `scrollNext()` | Scroll to next slide |
|
|
270
|
+
| `canScrollPrev()` | Check if can scroll backward |
|
|
271
|
+
| `canScrollNext()` | Check if can scroll forward |
|
|
272
|
+
| `selectedScrollSnap()` | Get current slide index |
|
|
273
|
+
| `scrollSnapList()` | Get array of all snap points |
|
|
274
|
+
| `on(event, callback)` | Subscribe to events (select, init, reInit) |
|
|
275
|
+
|
|
276
|
+
Full API: https://www.embla-carousel.com/api/
|
|
277
|
+
|
|
278
|
+
## Related
|
|
279
|
+
|
|
280
|
+
- Card - Common carousel item container
|
|
281
|
+
- Button - Used for navigation controls
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Chart
|
|
2
|
+
|
|
3
|
+
Flexible chart container built on Recharts with theming, configuration, and accessibility support for all chart types.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import {
|
|
9
|
+
ChartContainer,
|
|
10
|
+
ChartTooltip,
|
|
11
|
+
ChartTooltipContent,
|
|
12
|
+
ChartLegend,
|
|
13
|
+
ChartLegendContent,
|
|
14
|
+
type ChartConfig,
|
|
15
|
+
} from "@neynar/ui/chart"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Anatomy
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<ChartContainer config={chartConfig}>
|
|
22
|
+
<LineChart data={data}>
|
|
23
|
+
<XAxis dataKey="name" />
|
|
24
|
+
<YAxis />
|
|
25
|
+
<ChartTooltip content={<ChartTooltipContent />} />
|
|
26
|
+
<ChartLegend content={<ChartLegendContent />} />
|
|
27
|
+
<Line dataKey="value" />
|
|
28
|
+
</LineChart>
|
|
29
|
+
</ChartContainer>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Components
|
|
33
|
+
|
|
34
|
+
| Component | Description |
|
|
35
|
+
|-----------|-------------|
|
|
36
|
+
| ChartContainer | Root container providing responsive layout, theme context, and CSS variables |
|
|
37
|
+
| ChartTooltip | Re-export of Recharts Tooltip for positioning (use with ChartTooltipContent) |
|
|
38
|
+
| ChartTooltipContent | Custom tooltip content with series indicators and formatting |
|
|
39
|
+
| ChartLegend | Re-export of Recharts Legend for positioning (use with ChartLegendContent) |
|
|
40
|
+
| ChartLegendContent | Custom legend content with icons and colors |
|
|
41
|
+
|
|
42
|
+
## Props
|
|
43
|
+
|
|
44
|
+
### ChartContainer
|
|
45
|
+
|
|
46
|
+
| Prop | Type | Default | Description |
|
|
47
|
+
|------|------|---------|-------------|
|
|
48
|
+
| config | ChartConfig | - | Chart configuration defining series labels, colors, and icons |
|
|
49
|
+
| children | ReactNode | - | Recharts chart component (LineChart, BarChart, AreaChart, etc.) |
|
|
50
|
+
| className | string | - | Additional classes for sizing (e.g., "h-[400px]") |
|
|
51
|
+
| id | string | - | Optional unique identifier for the chart |
|
|
52
|
+
|
|
53
|
+
### ChartConfig
|
|
54
|
+
|
|
55
|
+
The config object maps series keys to their display configuration:
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
const config = {
|
|
59
|
+
revenue: {
|
|
60
|
+
label: "Revenue",
|
|
61
|
+
color: "hsl(142 76% 36%)", // Single color
|
|
62
|
+
},
|
|
63
|
+
expenses: {
|
|
64
|
+
label: "Expenses",
|
|
65
|
+
theme: { // Theme-aware colors
|
|
66
|
+
light: "hsl(0 84% 60%)",
|
|
67
|
+
dark: "hsl(0 72% 50%)",
|
|
68
|
+
},
|
|
69
|
+
icon: TrendingDown, // Optional icon component
|
|
70
|
+
},
|
|
71
|
+
} satisfies ChartConfig
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Each series can have:
|
|
75
|
+
- `label`: Display name for tooltips and legends
|
|
76
|
+
- `color`: Single color for both themes, OR
|
|
77
|
+
- `theme`: Object with `light` and `dark` colors
|
|
78
|
+
- `icon`: Optional React component for legend
|
|
79
|
+
|
|
80
|
+
### ChartTooltipContent
|
|
81
|
+
|
|
82
|
+
| Prop | Type | Default | Description |
|
|
83
|
+
|------|------|---------|-------------|
|
|
84
|
+
| indicator | "dot" \| "line" \| "dashed" | "dot" | Visual indicator style for series |
|
|
85
|
+
| hideLabel | boolean | false | Hide the tooltip label (x-axis value) |
|
|
86
|
+
| hideIndicator | boolean | false | Hide series color indicators |
|
|
87
|
+
| nameKey | string | - | Override key for series names |
|
|
88
|
+
| labelKey | string | - | Override key for tooltip label |
|
|
89
|
+
| formatter | function | - | Custom value formatter |
|
|
90
|
+
| labelFormatter | function | - | Custom label formatter |
|
|
91
|
+
|
|
92
|
+
### ChartLegendContent
|
|
93
|
+
|
|
94
|
+
| Prop | Type | Default | Description |
|
|
95
|
+
|------|------|---------|-------------|
|
|
96
|
+
| hideIcon | boolean | false | Hide series icons, showing only color squares |
|
|
97
|
+
| nameKey | string | - | Override key for series names |
|
|
98
|
+
| verticalAlign | "top" \| "bottom" | "bottom" | Legend position |
|
|
99
|
+
|
|
100
|
+
## Data Attributes
|
|
101
|
+
|
|
102
|
+
| Attribute | Applied To | Description |
|
|
103
|
+
|-----------|------------|-------------|
|
|
104
|
+
| data-slot | ChartContainer | Always "chart" |
|
|
105
|
+
| data-chart | ChartContainer | Unique chart ID for CSS variable scoping |
|
|
106
|
+
|
|
107
|
+
## Chart Types
|
|
108
|
+
|
|
109
|
+
The container works with all Recharts chart types:
|
|
110
|
+
|
|
111
|
+
- **LineChart**: Trends over time
|
|
112
|
+
- **BarChart**: Comparing values (vertical or horizontal)
|
|
113
|
+
- **AreaChart**: Filled regions showing volume
|
|
114
|
+
- **PieChart**: Proportional data
|
|
115
|
+
- **RadialBarChart**: Circular progress or gauges
|
|
116
|
+
|
|
117
|
+
## Examples
|
|
118
|
+
|
|
119
|
+
### Basic Line Chart
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { Line, LineChart, XAxis, YAxis } from "recharts"
|
|
123
|
+
|
|
124
|
+
const data = [
|
|
125
|
+
{ month: "Jan", value: 2400 },
|
|
126
|
+
{ month: "Feb", value: 1398 },
|
|
127
|
+
{ month: "Mar", value: 9800 },
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
const config = {
|
|
131
|
+
value: {
|
|
132
|
+
label: "Revenue",
|
|
133
|
+
color: "var(--chart-1)",
|
|
134
|
+
},
|
|
135
|
+
} satisfies ChartConfig
|
|
136
|
+
|
|
137
|
+
<ChartContainer config={config} className="h-[300px]">
|
|
138
|
+
<LineChart data={data}>
|
|
139
|
+
<XAxis dataKey="month" />
|
|
140
|
+
<YAxis />
|
|
141
|
+
<ChartTooltip content={<ChartTooltipContent />} />
|
|
142
|
+
<Line dataKey="value" stroke="var(--color-value)" strokeWidth={2} />
|
|
143
|
+
</LineChart>
|
|
144
|
+
</ChartContainer>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Multi-Series with Legend
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { Area, AreaChart, XAxis, YAxis } from "recharts"
|
|
151
|
+
|
|
152
|
+
const data = [
|
|
153
|
+
{ month: "Jan", revenue: 12400, expenses: 8200 },
|
|
154
|
+
{ month: "Feb", revenue: 18200, expenses: 9100 },
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
const config = {
|
|
158
|
+
revenue: { label: "Revenue", color: "hsl(142 76% 36%)" },
|
|
159
|
+
expenses: { label: "Expenses", color: "hsl(0 84% 60%)" },
|
|
160
|
+
} satisfies ChartConfig
|
|
161
|
+
|
|
162
|
+
<ChartContainer config={config} className="h-[400px]">
|
|
163
|
+
<AreaChart data={data}>
|
|
164
|
+
<XAxis dataKey="month" />
|
|
165
|
+
<YAxis />
|
|
166
|
+
<ChartTooltip content={<ChartTooltipContent />} />
|
|
167
|
+
<ChartLegend content={<ChartLegendContent />} />
|
|
168
|
+
<Area
|
|
169
|
+
dataKey="revenue"
|
|
170
|
+
fill="var(--color-revenue)"
|
|
171
|
+
stroke="var(--color-revenue)"
|
|
172
|
+
fillOpacity={0.6}
|
|
173
|
+
/>
|
|
174
|
+
<Area
|
|
175
|
+
dataKey="expenses"
|
|
176
|
+
fill="var(--color-expenses)"
|
|
177
|
+
stroke="var(--color-expenses)"
|
|
178
|
+
fillOpacity={0.4}
|
|
179
|
+
/>
|
|
180
|
+
</AreaChart>
|
|
181
|
+
</ChartContainer>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Bar Chart with Custom Tooltip
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { Bar, BarChart, XAxis, YAxis } from "recharts"
|
|
188
|
+
|
|
189
|
+
const config = {
|
|
190
|
+
requests: { label: "API Requests", color: "var(--chart-1)" },
|
|
191
|
+
} satisfies ChartConfig
|
|
192
|
+
|
|
193
|
+
<ChartContainer config={config} className="h-[300px]">
|
|
194
|
+
<BarChart data={apiData}>
|
|
195
|
+
<XAxis dataKey="endpoint" />
|
|
196
|
+
<YAxis />
|
|
197
|
+
<ChartTooltip
|
|
198
|
+
content={
|
|
199
|
+
<ChartTooltipContent
|
|
200
|
+
formatter={(value) => [value.toLocaleString(), "Requests"]}
|
|
201
|
+
/>
|
|
202
|
+
}
|
|
203
|
+
/>
|
|
204
|
+
<Bar dataKey="requests" fill="var(--color-requests)" radius={4} />
|
|
205
|
+
</BarChart>
|
|
206
|
+
</ChartContainer>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Theme-Aware Colors
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
const config = {
|
|
213
|
+
active: {
|
|
214
|
+
label: "Active Users",
|
|
215
|
+
theme: {
|
|
216
|
+
light: "hsl(220 70% 50%)",
|
|
217
|
+
dark: "hsl(220 70% 60%)",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
} satisfies ChartConfig
|
|
221
|
+
|
|
222
|
+
<ChartContainer config={config}>
|
|
223
|
+
<LineChart data={data}>
|
|
224
|
+
{/* Color automatically switches with theme */}
|
|
225
|
+
<Line dataKey="active" stroke="var(--color-active)" />
|
|
226
|
+
</LineChart>
|
|
227
|
+
</ChartContainer>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Tooltip Indicators
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// Dot indicator (default)
|
|
234
|
+
<ChartTooltip content={<ChartTooltipContent indicator="dot" />} />
|
|
235
|
+
|
|
236
|
+
// Line indicator
|
|
237
|
+
<ChartTooltip content={<ChartTooltipContent indicator="line" />} />
|
|
238
|
+
|
|
239
|
+
// Dashed indicator
|
|
240
|
+
<ChartTooltip content={<ChartTooltipContent indicator="dashed" />} />
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Color Variables
|
|
244
|
+
|
|
245
|
+
ChartContainer generates CSS variables for each config key:
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
const config = {
|
|
249
|
+
revenue: { color: "hsl(142 76% 36%)" },
|
|
250
|
+
}
|
|
251
|
+
// Generates: --color-revenue: hsl(142 76% 36%)
|
|
252
|
+
// Use in charts: var(--color-revenue)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Recharts built-in chart colors are also available:
|
|
256
|
+
- `var(--chart-1)` through `var(--chart-5)`: Pre-defined theme colors
|
|
257
|
+
|
|
258
|
+
## Accessibility
|
|
259
|
+
|
|
260
|
+
- Uses Recharts' `accessibilityLayer` prop for keyboard navigation
|
|
261
|
+
- Tooltips provide screen reader-friendly descriptions of data points
|
|
262
|
+
- Color indicators supplement with text labels in tooltips/legends
|
|
263
|
+
- CSS variables respect color mode preferences
|
|
264
|
+
|
|
265
|
+
## Best Practices
|
|
266
|
+
|
|
267
|
+
- Set explicit height via className (e.g., "h-[400px]") - default is aspect-video
|
|
268
|
+
- Use `satisfies ChartConfig` for type-safe configuration
|
|
269
|
+
- Reference colors with `var(--color-{key})` to match config
|
|
270
|
+
- Include `accessibilityLayer` prop on Recharts components
|
|
271
|
+
- Format large numbers in tooltips with `formatter` prop
|
|
272
|
+
- Use theme-aware colors for charts that will be viewed in both modes
|
|
273
|
+
|
|
274
|
+
## Related
|
|
275
|
+
|
|
276
|
+
- [Recharts Documentation](https://recharts.org/) - Full chart component reference
|
|
277
|
+
- Card - Container for chart sections with headers
|
|
278
|
+
- Tooltip - Related tooltip pattern for non-chart UI
|