You Might Also Like
Area Chart — SVG Trend Chart HTML CSS JS Snippet
Area Chart · Charts · Plain HTML, CSS & JS · Live preview
What's included
Features
smooth derives Bézier control points from each point's neighbours, producing a natural curve that passes through every value.render maps values into the plot with 15% padding so the line never touches the edges and small changes stay visible.linearGradient that clips to the curve.mousemove listener snaps a dot and a dashed vertical line to the nearest point using getBoundingClientRect mapping.touchmove handler reuses the same nearest-point logic so the crosshair works on phones.preserveAspectRatio="none" stretches the viewBox to the card width; the math is resolution-independent, so no resize code.About this UI Snippet
Area Chart — Smooth Catmull-Rom Curve, Gradient Fill & Hover Crosshair

An area chart is the go-to for showing a single metric's trend over time — active users, revenue, temperature — where the filled area under the line emphasises volume and growth. This snippet draws one with an SVG <path> in HTML, CSS, and vanilla JavaScript: a smooth curve through the data points, a gradient area fill, baseline gridlines, and an interactive hover crosshair with a dot, a value readout, and a tooltip.
Smooth curve via Catmull-Rom
Straight line segments between points look jagged; a true smooth curve reads as a polished chart. The smooth function converts the data points into a cubic Bézier path using the Catmull-Rom-to-Bézier formula: for each point it derives two control points from the slope of its neighbours ((p2 - p0) / 6), producing a curve that passes through every data point while flowing naturally between them. This is the same technique charting libraries use for their "smooth"/"natural" line mode, implemented in a dozen lines.
Auto-scaled, gradient-filled area
render computes the data's min and max, adds 15% padding so the line never touches the chart edges, and maps each value into the plot area. The line path is built once with smooth; the area path reuses that exact line and closes it down to the baseline (L X1 BOT L X0 BOT Z), then fills with an SVG linearGradient that fades from translucent to transparent. Using an in-markup SVG gradient (not a CSS background) means the fill clips perfectly to the curved area and renders identically across every framework export.
Hover crosshair and readout
A mousemove listener on the plot maps the cursor's horizontal position to the nearest data index using getBoundingClientRect (so it works regardless of the chart's CSS size versus its viewBox). It then positions a marker dot on that point, draws a dashed vertical crosshair line, updates the big value/month readout in the header, and floats a small tooltip above the point. Leaving the chart hides them and resets the readout. A touchmove handler maps the same logic for mobile.
Responsive by default
preserveAspectRatio="none" lets the 320×170 viewBox stretch to the card width, and the coordinate mapping in render is resolution-independent, so the chart is fluid with no resize handling. The x-axis labels render every other month to avoid crowding on narrow screens.
Swap the DATA array for your figures and it redraws. Pair this with a realtime line chart for live streams, a stacked bar chart for composition, or a sparkline chart for compact trends.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Rather than deriving the Bezier control point formula by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to walk through exactly why the smooth() function divides the neighbor-point differences by 6 to get its control points, or how the min/max padding of 15% keeps the curve from touching the plot edges. The same assistant is useful for optimizing it — ask whether recomputing the entire pts array and both path strings on every render call makes sense for a chart that updates frequently, or whether the nearest-point lookup in onMove could be replaced with a binary search for a much larger DATA array. It's also a good way to extend the chart: ask it to render two overlapping series with independent gradients, add a pinch-to-zoom range selector, or animate the area path drawing in on load with a stroke-dashoffset reveal. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a smooth "area chart" in plain HTML, CSS, and JavaScript using only inline SVG — no charting library, no canvas.
Requirements:
- An SVG with preserveAspectRatio="none" so a fixed-size viewBox stretches fluidly to the container's actual rendered width, without any JS resize handling.
- Convert an array of { label, value } data points into pixel coordinates by computing the data's min and max, adding roughly 15 percent padding to both ends so the curve never touches the top or bottom of the plot area, then linearly mapping each value into that padded range.
- Connect the mapped points with a true smooth curve using the Catmull-Rom-to-Bezier technique: for each point, derive two cubic Bezier control points from the positions of its immediate neighbors (not just a generic spline library), so the resulting path passes exactly through every original data point rather than merely approximating them.
- Reuse that exact line path string to build a second, closed path that drops down to a shared baseline and back up to the first point, and fill it with an SVG linearGradient (defined in the SVG's defs, not a CSS background) that fades from a translucent color at the top to fully transparent at the bottom.
- Add a mousemove (and touchmove) listener on the chart container that uses getBoundingClientRect to convert the cursor's x position into the nearest data index, then moves a small circle marker and a dashed vertical crosshair line to that point's exact coordinates, and updates a header readout and a floating tooltip with that data point's value and label.
- On mouseleave, hide the marker, crosshair, and tooltip and reset the header readout to its idle state.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA "Monthly active users" card appears with a smooth gradient-filled area curve across twelve months and a peak readout.
- 2Hover the chartA dot snaps to the nearest month, a dashed vertical line marks it, and the header shows that month's value.
- 3Read the tooltipA small tooltip floats above the hovered point with the exact value, tracking your cursor across the months.
- 4Move along the curveAs you sweep across, the crosshair and readout update to the nearest data point in real time.
- 5On mobileDrag a finger across the chart — the same crosshair and readout follow your touch.
- 6Plug in your dataReplace the
DATAarray with your{ m, v }points; the curve auto-scales to the min/max with padding.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Replace the DATA array with your { m, v } points (any length) and call render. It recomputes the min/max with padding and remaps every point, so the curve, area, gridlines, and peak adapt automatically. For non-monthly data, just change the label field and the x-axis renderer.
Render a second line and area from another data array with its own gradient and colour, layering the paths in the SVG. Add both values to the hover tooltip. For comparison readability, give the second series a distinct hue and a slightly lower area opacity so the overlap stays legible.
Catmull-Rom produces a smooth curve that still passes through every actual data point, so values are not visually distorted — unlike a basis spline, which only approximates the points. Straight segments are accurate but jagged. Catmull-Rom is the sweet spot most dashboards use for "smooth" lines, and it is cheap to compute.
Give the SVG role="img" with an aria-label summarising the trend, and expose the underlying data as an off-screen or toggleable data table for screen-reader users. The hover readout is mouse-only, so also ensure the key figures (latest, peak) appear as real text in the header, which they do here.
In React, compute the path strings with useMemo from your data and render them in JSX; track the hovered index in useState for the crosshair, updating it in an onMouseMove. In Vue, use a computed for the paths and a ref for hover. In Angular, compute in the component and bind [attr.d]. The Catmull-Rom function and gradient port unchanged.