About
Most dashboards need one honest trend line, not a charting library. This draws exactly that.
Use it when
- A trend over time is the point.
Skip it when
- You need tooltips, zoom or many series: use a charting library.
Install
- 1Add the VibeBox registry to
components.json:{ "registries": { "@vibebox": "https://motion.vibeboxph.com/r/{name}.json" } } - 2Add the component with the shadcn CLI:
npx shadcn@latest add @vibebox/area-chart - 3Use it:
import { AreaChart } from "@/components/vibebox/area-chart"; <AreaChart accent="#7c5cff" />
AI prompt
Everything an AI coding tool needs to rebuild this component the way it looks here: colours, fonts, the art it draws, props and rules.
Read the prompt
Build the VibeBox Motion component "Area Chart" (Data) in a Next.js App Router project with Tailwind CSS v4.
What it is
- A smooth area chart in your accent with gridlines, a marked last point and axis labels, in plain SVG.
- A single-series area chart drawn in SVG with a smooth curve, a soft accent gradient under it, four gridlines, a marked last point and axis labels. No chart library, so it is tiny, server rendered and recolours with `--app-accent`. The accessible name states the first and last values.
Install
- `npx shadcn@latest add @vibebox/area-chart`
- Registry: add `"@vibebox": "https://motion.vibeboxph.com/r/{name}.json"` to components.json, then run the command. The source lands in `components/vibebox/area-chart` and is yours to edit.
Visual spec
- Colours (the VibeBox spectrum, the same in light and dark):
- --spectrum-1: #00c5fc (cyan)
- --spectrum-2: #1d6ef0 (blue)
- --spectrum-3: #5225ed (indigo)
- --spectrum-4: #e434dc (magenta)
- --spectrum-5: #fd3aa9 (pink)
- --spectrum-6: #f98472 (coral)
- --spectrum-7: #fcbe72 (amber)
- Ink surface (dark): background #05070c, card #0a0d15, text #f5f6fa, muted text #a3abbd, border rgba(255,255,255,0.08).
- Paper surface (light): background #f3f0ea, card #fbfaf7, text #0c0e14, muted text #4a4f5c, border rgba(12,14,20,0.12).
- Fonts:
- Display and body: Figtree (400, 500, 600, 700), falling back to ui-sans-serif, system-ui.
- Emphasis: Fraunces italic, for one or two words of a heading.
- Labels and code: JetBrains Mono (400, 500), uppercase labels with 0.14em tracking.
- Shape: radius 8 to 12px on cards and panels; pills may be fully round. Hairline borders, soft glow instead of heavy shadow.
- Motion: animate transform and opacity only; fast ease-out (cubic-bezier(0.16, 1, 0.3, 1)), 140ms for micro-interactions, 320 to 520ms for reveals.
Art and media
- Built from HTML, CSS and inline SVG only; no image files or video.
Props
- `title`: `string` (default "Bookings this year"). The chart's title and part of its accessible name.
- `points`: `number[]`. The values, oldest first.
- `labels`: `string[]`. Evenly spaced axis labels.
- `accent`: `string` (default "blue"). The accent colour: blue, violet, emerald, amber, rose, or any CSS colour. It sets --app-accent on the root, so a parent can set it once for a whole screen.
- `className`: `string`. Merged with cn(); give it a width.
Behaviour and access
- Reduced motion: Nothing animates on its own.
- Keyboard: Every control is a native element in reading order.
- Screen readers: Semantic elements with accessible names; decorative marks are hidden.
- Touch: Targets are 36 px or larger; nothing depends on hover.
- Budget: about 0 KB of JavaScript, WebGL none, GPU none. It pauses while off screen.
Theming
Everything reads `--app-accent` (set by the `accent` prop, or by a parent) and the mode's tokens, so light and dark and any brand colour work without code changes. Pass a named accent or any CSS colour; Launchpad sets it once per app from the client's theme.
Usage
```tsx
import { AreaChart } from "@/components/vibebox/area-chart";
<AreaChart accent="#7c5cff" />
```
Notes
Add the VibeBox Motion Area Chart with `npx shadcn@latest add @vibebox/area-chart`. Pass your series as numbers and a few labels. Set the brand colour once with `--app-accent` on a parent (or the `accent` prop). It follows light and dark through the tokens; keep the licence header.
Rules
- Keep the licence header on every installed file.
- Original code only: do not copy components from other libraries, and do not add GSAP.
- Use tokens for colour, type and radius; no hard-coded brand values.
- Server Components by default; `"use client"` only for the island.Code
1 filescomponents/vibebox/area-chart/index.tsx
/**
* VibeBox Motion · Area Chart
* Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
* https://motion.vibeboxph.com/license
* Use it in unlimited personal, commercial and client projects.
* Do not sell or redistribute it as a component, kit, template or builder.
*/
import { cn } from "@/lib/utils";
import { accentStyle } from "@/lib/vibebox/app-accent";
export type AreaChartProps = { title?: string; points?: number[]; labels?: string[]; accent?: string; className?: string };
const POINTS = [12, 18, 15, 24, 22, 31, 28, 38, 35, 46, 42, 52];
const LABELS = ["Jan", "Mar", "May", "Jul", "Sep", "Nov"];
/** A smooth area chart in the accent with gridlines and end labels. SVG, no library. */
export function AreaChart({ title = "Bookings this year", points = POINTS, labels = LABELS, accent, className }: AreaChartProps) {
const W = 600;
const H = 220;
const pad = 8;
const max = Math.max(...points) * 1.1;
const x = (i: number) => pad + (i / (points.length - 1)) * (W - pad * 2);
const y = (v: number) => H - 24 - (v / max) * (H - 40);
const smooth = points.map((v, i) => {
if (i === 0) return `M${x(i)} ${y(v)}`;
const cx = (x(i - 1) + x(i)) / 2;
return `C${cx} ${y(points[i - 1]!)} ${cx} ${y(v)} ${x(i)} ${y(v)}`;
}).join(" ");
const id = "vb-area-fill";
return (
<figure data-vb="area-chart" style={accentStyle(accent)} className={cn("rounded-xl border border-border bg-card p-4", className)}>
<figcaption className="mb-2 text-small font-medium">{title}</figcaption>
<svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: from ${points[0]} to ${points[points.length - 1]}`} className="h-auto w-full">
<defs>
<linearGradient id={id} x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stopColor="var(--app-accent)" stopOpacity="0.32" />
<stop offset="1" stopColor="var(--app-accent)" stopOpacity="0" />
</linearGradient>
</defs>
{[0, 1, 2, 3].map((g) => (
<line key={g} x1={pad} x2={W - pad} y1={y((max / 4) * g)} y2={y((max / 4) * g)} stroke="var(--border)" strokeWidth="1" />
))}
<path d={`${smooth} L${x(points.length - 1)} ${H - 24} L${x(0)} ${H - 24} Z`} fill={`url(#${id})`} />
<path d={smooth} fill="none" stroke="var(--app-accent)" strokeWidth="2.4" strokeLinecap="round" />
<circle cx={x(points.length - 1)} cy={y(points[points.length - 1]!)} r="4.5" fill="var(--app-accent)" stroke="var(--card)" strokeWidth="2" />
{labels.map((l, i) => (
<text key={l} x={pad + (i / (labels.length - 1)) * (W - pad * 2)} y={H - 6} textAnchor={i === 0 ? "start" : i === labels.length - 1 ? "end" : "middle"} className="fill-[var(--subtle-foreground)] text-[11px]">{l}</text>
))}
</svg>
</figure>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | "Bookings this year" | The chart's title and part of its accessible name. |
| points | number[] | — | The values, oldest first. |
| labels | string[] | — | Evenly spaced axis labels. |
| accent | string | "blue" | The accent colour: blue, violet, emerald, amber, rose, or any CSS colour. It sets --app-accent on the root, so a parent can set it once for a whole screen. |
| className | string | — | Merged with cn(); give it a width. |
Accessibility and motion
- Keyboard
- Every control is a native element in reading order.
- Screen readers
- Semantic elements with accessible names; decorative marks are hidden.
- Touch
- Targets are 36 px or larger; nothing depends on hover.
- Reduced motion
- Nothing animates on its own.
Edge cases and limits
- Fewer than two points draw nothing meaningful.
- Labels are spaced evenly, not by date.
Related

Data Table
FreeA data table with a caption, aligned numbers and status pills, scrolling sideways inside its frame.
Data · Server

