Pie Chart
A pie chart for showing parts of a whole.
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
<%# Each row carries its slice color in a fill key; the config maps
names to labels and colors. %>
<% data = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, data: data, config: config, id: "pie-default") do |chart| %>
<% chart.with_pie(data_key: :visitors, name_key: :browser) %>
<% chart.with_tooltip %>
<% end %>
</div>
Donut
<%# inner_radius: 60 opens the donut hole. %>
<% data = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, data: data, config: config, id: "pie-donut") do |chart| %>
<% chart.with_pie(data_key: :visitors, name_key: :browser, inner_radius: 60) %>
<% chart.with_tooltip %>
<% end %>
</div>
Donut active
<%# active_index pops one slice out by 10px (the donut-active look). %>
<% data = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, data: data, config: config, id: "pie-donut-active") do |chart| %>
<% chart.with_pie(data_key: :visitors, name_key: :browser, inner_radius: 60, active_index: 0) %>
<% end %>
</div>
Donut text
<%# with_center_label fills the donut hole with plain SVG text. %>
<% data = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, data: data, config: config, id: "pie-donut-text") do |chart| %>
<% chart.with_pie(data_key: :visitors, name_key: :browser, inner_radius: 60) %>
<% chart.with_center_label(title: "925", subtitle: "Visitors") %>
<% end %>
</div>
Label list
<%# labels: :list writes a label inside each slice; label_key picks
the row field to print (here the browser name). %>
<% data = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, data: data, config: config, id: "pie-label-list") do |chart| %>
<% chart.with_pie(data_key: :visitors, name_key: :browser, labels: :list, label_key: :browser) %>
<% end %>
</div>
Legend
<%# with_legend lists the slices below the chart. %>
<% data = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, data: data, config: config, id: "pie-legend") do |chart| %>
<% chart.with_pie(data_key: :visitors, name_key: :browser) %>
<% chart.with_legend %>
<% end %>
</div>
Stacked
<%# Stacked pies: two with_pie slots, each with its own data: and
non-overlapping radii. The first pie drives the tooltip. %>
<% browsers = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" }
]
months = [
{ month: "january", desktop: 186, fill: "var(--color-january)" },
{ month: "february", desktop: 305, fill: "var(--color-february)" },
{ month: "march", desktop: 237, fill: "var(--color-march)" }
]
config = { visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" },
desktop: { label: "Desktop" },
january: { label: "January", color: "var(--chart-1)" },
february: { label: "February", color: "var(--chart-2)" },
march: { label: "March", color: "var(--chart-3)" } } %>
<div class="w-full max-w-xl">
<%= poetry_chart(:pie, config: config, id: "pie-stacked") do |chart| %>
<% chart.with_pie(data: browsers, data_key: :visitors, name_key: :browser, outer_radius: 60) %>
<% chart.with_pie(data: months, data_key: :desktop, name_key: :month,
inner_radius: 70, outer_radius: 90) %>
<% end %>
</div>
API
Poetry::Charts::PieChart::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 - name => { label:, color: } - naming and coloring the slices. |
| data: | Object | Default rows for pies that don't bring their own data: - one hash per slice. | |
| height: | Integer | defaults to 250 |
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 | Margin overrides ({ top:, right:, bottom:, left: }), merged over the slim polar default. | |
| width: | Integer | defaults to 250 |
ViewBox width in pixels; the rendered chart scales to its container. |
Slots
| Writer | Description |
|---|---|
| with_center_label | The donut-hole text: a title line plus an optional subtitle. |
| with_legend | The legend row: align:, items:, and hide_icon:. |
| with_py | One ring of slices reading data_key: values and name_key: slice names. inner_radius: makes the donut; padding_angle: spaces the slices; active_index: pops one out by active_grow: pixels. |
| with_tooltip | The hover tooltip; the slice name carries the label, so hide_label defaults on. |
Methods
| Method | Description |
|---|---|
| # | One ring of slices reading data_key: values and name_key: slice names. inner_radius: makes the donut; padding_angle: spaces the slices; active_index: pops one out by active_grow: pixels. Slot writer for the pies slot (repeatable).
ActiveSupport singularizes "pies" to "py" - give the grammar its
real name. |
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-svg] | The chart canvas (<svg>) - the aria-label surface, the tooltip's focus/keyboard surface (role=application when it attaches), and the motion rig's mount |
| [data-slot=chart-pie] | One pie's slice group (<g>) - a ring per with_pie slot |
| [data-slot=chart-pie-sector] | One slice (<path>) - fill from its row's color, popped out when active |
| [data-slot=chart-labels] | A series' value labels (<g> of <text>, aria-hidden), rendered when the series opts into labels |
| [data-slot=chart-center-label] | The center text (<text>) - title tspan plus optional subtitle filling the chart's middle |
| [data-slot=chart-coordinates] | The embedded JSON payload (<script>) the tooltip controller reads - per-category anchors and pre-formatted values, zero chart math in the browser |
State attributes
| Part | Attribute | Condition | Values |
|---|---|---|---|
| chart-svg | data-animate | when animate (the default) - the entrance tier's flag the motion stylesheet and controller key off | — |
| chart-svg | data-motion | runtime, when animate - the motion engine's lifecycle stamp | entrance · morph · settled |
| chart-pie | data-key | always - the series key | — |
| chart-pie-sector | data-key | always - the series key | — |
| chart-pie-sector | data-index | on the first pie's slices - the datum index the tooltip walks | — |
| chart-pie-sector | data-active | the active slice - server-rendered via active_index:, and reflected onto the hovered/arrow-keyed index by the tooltip controller at runtime | — |
| chart-pie-sector | data-motion-group | when animate - the motion rig's sweep group (one per ring) | — |
| chart-pie-sector | data-motion-sector | when animate - the motion rig's server-computed sector params for the fan-out sweep | — |
| chart-labels | data-key | always - the series key | — |
CSS variables
| Part | Variable | Description |
|---|---|---|
| chart-svg | --poetry-motion-delay | the motion rig's entrance delay (animation_begin) |
| chart-svg | --poetry-motion-duration | the motion rig's entrance duration (animation_duration) |
| chart-svg | --poetry-motion-easing | the motion rig's easing keyword (animation_easing) |
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 |
| 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--tooltip (if tooltip?) | enter on pointerover |
| coordinates | poetry--charts--tooltip (if tooltip?) | target data |
| tooltip_layer | poetry--charts--tooltip (if tooltip?) | target tooltip |