Area Chart
An area chart for volume or cumulative totals over a continuous axis.
Installation
Charts ship in the separate, optional poetry-charts
gem — not bundled with poetry-ui. If you
don't have it yet, add it and wire it into your app:
bundle add poetry-charts
bin/rails g poetry:install --charts
Default
<% data = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 }
]
config = { desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" } } %>
<div class="w-full max-w-xl">
<%= poetry_area_chart(data: data, config: config, id: "area-default",
margin: { left: 12, right: 12 }) do |chart| %>
<% chart.with_grid %>
<% chart.with_x_axis(data_key: :month, tick_formatter: ->(v) { v[0, 3] }) %>
<% chart.with_area(data_key: :mobile, stack: :a) %>
<% chart.with_area(data_key: :desktop, stack: :a) %>
<% chart.with_tooltip(indicator: :dot) %>
<% end %>
</div>
Legend toggle
<%# live: true carries the {spec, frame} payload; clicking a legend item
re-renders CLIENT-SIDE through the vendored kernel - the hidden series
leaves the domain (recharts' rescale-on-hide). %>
<%# Live data carries PRE-FORMATTED category strings - lambdas can't ride
the JSON payload (the gem raises a teaching error). %>
<% data = [
{ month: "Jan", desktop: 186, mobile: 80 },
{ month: "Feb", desktop: 305, mobile: 200 },
{ month: "Mar", desktop: 237, mobile: 120 },
{ month: "Apr", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "Jun", desktop: 214, mobile: 140 }
]
config = { desktop: { label: "Desktop", color: "var(--chart-1)" },
mobile: { label: "Mobile", color: "var(--chart-2)" } } %>
<div class="w-full max-w-xl">
<%= poetry_area_chart(data: data, config: config, id: "area-legend", live: true,
margin: { left: 12, right: 12 }) do |chart| %>
<% chart.with_grid %>
<% chart.with_x_axis(data_key: :month) %>
<% chart.with_area(data_key: :mobile) %>
<% chart.with_area(data_key: :desktop) %>
<% chart.with_tooltip(indicator: :dot) %>
<% chart.with_legend(toggle: true) %>
<% end %>
</div>
API
Poetry::Charts::AreaChart::Component — options are
constructor keywords (the poetry_* helper forwards them);
slots are composed inside the block. Generated from the gem's source documentation.
| Option | Type | Details | Description |
|---|---|---|---|
| config: | Object | required | The series config - key => { label:, color: } - naming and coloring every series. |
| data: | Object | required | The rows to plot: an array of hashes, one per x category. |
| height: | Integer | defaults to 360 |
ViewBox height in pixels. |
| id: | String | Explicit DOM id token, stable across renders; otherwise the chart gets a unique per-render id. | |
| label: | String | Accessible name for the chart SVG; defaults to one built from the configured series. | |
| margin: | Object | Plot margin overrides ({ top:, right:, bottom:, left: }), merged over the defaults. | |
| offset: | Symbol | one of: none, expand; defaults to :none |
Stack baseline mode - :expand normalizes each stack to percentages. |
| width: | Integer | defaults to 640 |
ViewBox width in pixels; the rendered chart scales to its container. |
Slots
| Writer | Description |
|---|---|
| with_area | An area series bound to data_key:. Areas sharing a stack: id pile up; gradient: true fades the fill; curve: picks the interpolation. |
Styling
Every part carries a stable data-slot attribute — target
[data-slot=…] from your own CSS to restyle it. State rides
data attributes on the parts below. This contract is verified against rendered DOM in CI.
| Part | Description |
|---|---|
| [data-slot=chart-brush] | The brush strip group (with_brush): track + window + two handles below the x axis |
| [data-slot=chart-brush-track] | The full-width brush rail |
| [data-slot=chart-brush-window] | The selected-range rect the drag moves |
| [data-slot=chart-brush-handle] | One draggable window edge |
| [data-slot=chart-zoom-selection] | The zoom drag-selection overlay (zoom: true), hidden until a drag starts |
| [data-slot=chart-live-payload] | The embedded {spec, frame} JSON the live renderer recomputes geometry from |
| [data-slot=chart-svg] | The chart canvas (<svg>) - server-computed geometry in a fixed viewBox; role=img, or the focusable role=application accessibilityLayer when the tooltip attaches |
| [data-slot=chart-motion-reveal] | The entrance clipPath rect (the ported area reveal) - the motion stylesheet scales it 0 -> 1; only when animate |
| [data-slot=chart-grid] | The gridline group (with_grid) - horizontal and/or vertical rules across the plot |
| [data-slot=chart-cursor] | The hover cursor, hidden until the tooltip controller positions and reveals it at the active index - a vertical rule or a translucent band rect (bar charts) |
| [data-slot=chart-areas] | The area-mark group - a fill path plus top-curve stroke per series, clipped by the reveal rect while animating |
| [data-slot=chart-area] | One series' fill path (var(--color-<key>) or its gradient) |
| [data-slot=chart-area-stroke] | One series' top-curve stroke path (the source strokes the curve, never the area outline) |
| [data-slot=chart-active-dots] | The hover-marker group (with_tooltip) - pre-rendered hidden circles for every series x index |
| [data-slot=chart-active-dot] | One hover marker - display=none until the tooltip controller reveals the active index's dot |
| [data-slot=chart-x-axis] | The x-axis tick-label group (with_x_axis) |
| [data-slot=chart-y-axis] | The y-axis tick-label group (with_y_axis) |
| [data-slot=chart-coordinates] | The embedded per-index geometry payload (<script type=application/json>) the tooltip controller reads - zero chart math in the browser |
State attributes
| Part | Attribute | Condition | Values |
|---|---|---|---|
| chart-brush-handle | data-edge | always - which edge | start · end |
| chart-svg | data-animate | present when animate (the default) - the motion stylesheet and controller key the entrance off it | — |
| chart-svg | data-motion | runtime - the motion rig stamps the animation lifecycle (entrance/morph, then settled) | entrance · morph · settled |
| chart-area | data-key | the series key | — |
| chart-area-stroke | data-key | the series key | — |
| chart-active-dot | data-key | the series key | — |
| chart-active-dot | data-index | the datum index | — |
| chart-active-dot | data-active | runtime - rides the marker while its index is the active one | — |
CSS variables
| Part | Variable | Description |
|---|---|---|
| chart-svg | --poetry-motion-duration | the entrance/morph duration (animation_duration, ms) |
| chart-svg | --poetry-motion-easing | the animation easing keyword (animation_easing) |
| chart-svg | --poetry-motion-delay | the pre-animation hold (animation_begin, ms) |
Wiring
The Stimulus surface each element carries — declared in the component, verified against rendered DOM in CI. Bare actions fire on the element's default event.
| Element | Controller | Wiring |
|---|---|---|
| frame | poetry--charts--tooltip (if tooltip?) | registers · value sync (if) |
| frame | poetry--charts--motion (if animate?) | registers |
| frame | poetry--charts--live (if live?) | registers · receive on poetry-chart:update |
| frame | poetry--charts--tooltip (if) | refresh on poetry--charts--live:updated |
| frame | poetry--charts--window (if window_features?) | registers · value zoom · value plot · value brush (if) |
| svg | poetry--charts--tooltip (if tooltip?) | move on pointermove · leave on pointerleave · focus on focus · blur on blur · keydown on keydown · target svg |
| svg | poetry--charts--window (if zoom?) | startZoom on pointerdown · reset on dblclick |
| coordinates | poetry--charts--tooltip (if tooltip?) | target data |
| tooltip_layer | poetry--charts--tooltip (if tooltip?) | target tooltip |
| live_payload | poetry--charts--live | target payload |
| brush | poetry--charts--window (if) | startBrush on pointerdown |