Chart Gallery
Every chart type and option of rui_chart, live, with the code that renders it. Each code block is generated from the same options as the chart above it, so what you copy is what you see.
The reference for each option is the Chart page . Figures here are illustrative; applications are named by file number, never by address.
Bars
Columns and rows, one series or many.
Grouped, with a reference line and an annotation
- 2024: New bike lane
| Category | Pedestrian | Cyclist |
|---|---|---|
| 2021 | 6 collisions | 1 collisions |
| 2022 | 8 collisions | 2 collisions |
| 2023 | 7 collisions | 1 collisions |
| 2024 | 9 collisions | 3 collisions |
| 2025 | 8 collisions | 2 collisions |
Show code
<%= rui_chart(
type: :bar,
title: "Collisions by year",
subtitle: "Illustrative, 2021 to 2025",
categories: ["2021", "2022", "2023", "2024", "2025"],
series: [
{ name: "Pedestrian", values: [6, 8, 7, 9, 8] },
{ name: "Cyclist", values: [1, 2, 1, 3, 2] }
],
unit: "collisions",
references: [{ value: 10, label: "City target" }],
annotations: [{ at: "2024", text: "New bike lane" }]) %>
Stacked, with shares in the tooltip
| Category | Pedestrian | Cyclist |
|---|---|---|
| 2021 | 6 collisions | 1 collisions |
| 2022 | 8 collisions | 2 collisions |
| 2023 | 7 collisions | 1 collisions |
| 2024 | 9 collisions | 3 collisions |
| 2025 | 8 collisions | 2 collisions |
Show code
<%= rui_chart(
type: :bar,
layout: :stacked,
title: "Collisions, stacked",
categories: ["2021", "2022", "2023", "2024", "2025"],
series: [
{ name: "Pedestrian", values: [6, 8, 7, 9, 8] },
{ name: "Cyclist", values: [1, 2, 1, 3, 2] }
],
unit: "collisions",
show_share: true) %>
Horizontal, stacked
-
Received
-
Review
-
Approved
| Category | Ask | Other |
|---|---|---|
| Received | 5 | 2 |
| Review | 9 | 4 |
| Approved | 3 | 1 |
Show code
<%= rui_chart(
type: :hbar,
layout: :stacked,
title: "Applications by status",
categories: ["Received", "Review", "Approved"],
series: [
{ name: "Ask", values: [5, 9, 3] },
{ name: "Other", values: [2, 4, 1] }
],
show_share: true) %>
Per point tooltip, highlight and note
-
Pothole
1.2k
-
Graffiti
340
-
Bulky items
80
-
Zero
0
| Category | Value |
|---|---|
| Pothole | 1.2k |
| Graffiti | 340 |
| Bulky items | 80 |
| Zero | 0 |
Show code
<%= rui_chart(
type: :hbar,
title: "311 top request types",
value_format: :compact,
data: [
{ label: "Pothole", value: 1200, tooltip: "210 requests · median 9 days" },
{ label: "Graffiti", value: 340, highlight: true },
{ label: "Bulky items", value: 80, note: "Includes couches" },
{ label: "Zero", value: 0 }
]) %>
Lollipop: ranked list, rest hidden
0.79.0+. Sorted high to low; max_rows: keeps six, rest_label: says how many are hidden. Below 480 px each label sits above its rule.
-
Pothole
-
Graffiti
-
Bulky items
-
Streetlight out on a residential side street
-
Noise
-
Litter
3 more types
| Item | Value | Share |
|---|---|---|
| Pothole | 1,210 | 1,210 of 3,800 · 32% |
| Graffiti | 860 | 860 of 3,800 · 23% |
| Bulky items | 640 | 640 of 3,800 · 17% |
| Streetlight out on a residential side street | 410 | 410 of 3,800 · 11% |
| Noise | 380 | 380 of 3,800 · 10% |
| Litter | 300 | 300 of 3,800 · 8% |
Show code
<%= rui_chart(
type: :lollipop,
title: "Requests by type",
subtitle: "Last 30 days",
sort: :desc,
max_rows: 6,
rest_label: "%{count} more types",
data: [
{ label: "Pothole", value: 1210 },
{ label: "Graffiti", value: 860, highlight: true },
{ label: "Bulky items", value: 640 },
{ label: "Streetlight out on a residential side street", value: 410 },
{ label: "Noise", value: 380 },
{ label: "Litter", value: 300 },
{ label: "Signals", value: 220 },
{ label: "Sidewalk", value: 180 },
{ label: "Trees", value: 150 }
]) %>
Lollipop: rest grouped into Other
rest: :other sums the hidden rows into one row, like :hbar. Use it when the rest is one more category, not a list of named places.
-
Online
-
Phone
-
In person
-
Other 2
| Item | Value | Share |
|---|---|---|
| Online | 520 | 520 of 1,015 · 51% |
| Phone | 310 | 310 of 1,015 · 31% |
| In person | 140 | 140 of 1,015 · 14% |
| Other 2 | 45 | 45 of 1,015 · 4% |
Show code
<%= rui_chart(
type: :lollipop,
title: "Channel, top 3",
sort: :desc,
max_rows: 3,
rest: :other,
data: [
{ label: "Online", value: 520 },
{ label: "Phone", value: 310 },
{ label: "In person", value: 140 },
{ label: "Mail", value: 40 },
{ label: "Fax", value: 5 }
]) %>
Funnel: conversion cohort
0.80.0+. Each step prints its count and its share of the first; the drop between steps shows between rows.
-
Visited
1,000
-
Signed up
400 · 40% of Visited
-
Paid
100 · 10% of Visited
| Step | Count | Share of first |
|---|---|---|
| Visited | 1,000 | 100% |
| Signed up | 400 | 40% |
| Paid | 100 | 10% |
Show code
<%= rui_chart(
type: :funnel,
title: "Sign-up funnel",
subtitle: "Illustrative",
data: [
{ label: "Visited", value: 1000 },
{ label: "Signed up", value: 400 },
{ label: "Paid", value: 100 }
]) %>
Funnel: stages, step colours
mode: :stages prints counts only and assumes no order: a later stage may be larger than an earlier one, and a 0 stage draws as a hairline.
-
Reported
240
-
Reference number
180
-
Escalated
0
-
Closed
150
-
Re-reported
190
| Stage | Count |
|---|---|
| Reported | 240 |
| Reference number | 180 |
| Escalated | 0 |
| Closed | 150 |
| Re-reported | 190 |
Show code
<%= rui_chart(
type: :funnel,
mode: :stages,
colors: :steps,
title: "Reports by stage",
data: [
{ label: "Reported", value: 240 },
{ label: "Reference number", value: 180 },
{ label: "Escalated", value: 0 },
{ label: "Closed", value: 150 },
{ label: "Re-reported", value: 190 }
]) %>
Waffle: percent
0.82.0+. 100 squares, shares rounded by largest remainder so the grid always holds 100. Hover a legend row to dim the other squares.
| Item | Count | Share |
|---|---|---|
| Pothole | 412 | 45.4% |
| Graffiti | 166 | 18.3% |
| Streetlight | 118 | 13% |
| Noise | 97 | 10.7% |
| Litter | 71 | 7.8% |
| Sidewalk | 44 | 4.8% |
Show code
<%= rui_chart(
type: :waffle,
title: "Issue mix",
subtitle: "Share of 311 requests",
data: [
{ label: "Pothole", value: 412 },
{ label: "Graffiti", value: 166 },
{ label: "Streetlight", value: 118 },
{ label: "Noise", value: 97 },
{ label: "Litter", value: 71 },
{ label: "Sidewalk", value: 44 }
]) %>
Waffle: one square per report
squares: :count for a small total: 71 reports, the first 8 types and the rest in Other.
One square for each report.
| Item | Count |
|---|---|
| Pothole | 18 reports |
| Graffiti | 11 reports |
| Streetlight | 9 reports |
| Noise | 8 reports |
| Litter | 7 reports |
| Sidewalk | 6 reports |
| Signals | 4 reports |
| Trees | 3 reports |
| Other | 5 reports |
Show code
<%= rui_chart(
type: :waffle,
squares: :count,
unit: "reports",
title: "Reports this month",
max_items: 8,
data: [
{ label: "Pothole", value: 18 },
{ label: "Graffiti", value: 11 },
{ label: "Streetlight", value: 9 },
{ label: "Noise", value: 8 },
{ label: "Litter", value: 7 },
{ label: "Sidewalk", value: 6 },
{ label: "Signals", value: 4 },
{ label: "Trees", value: 3 },
{ label: "Bulky items", value: 2 },
{ label: "Water", value: 2 },
{ label: "Snow", value: 1 }
]) %>
Slope: two columns, row by row
0.83.0+. Each row on its own scale; tone: :neutral colours by column, never by better or worse. change: :points for percentage rows, :none to print no change. Long labels get the spare width on wide cards (0.83.3+).
-
Open share
34% 21% -13 pts
-
Reports per km2
12.5 per km2 18 per km2
-
Median days to close
9 days 9 days no change
-
Requests per 1,000 people
41 33 -20%
| Measure | This area | Toronto | Change |
|---|---|---|---|
| Open share | 34% | 21% | 13 points lower |
| Reports per km2 | 12.5 per km2 | 18 per km2 | |
| Median days to close | 9 days | 9 days | no change |
| Requests per 1,000 people | 41 | 33 | 20% lower |
Show code
<%= rui_chart(
type: :slope,
title: "This area vs the city",
columns: ["This area", "Toronto"],
rows: [
{ label: "Open share", values: [34, 21], value_format: :percent, change: :points },
{ label: "Reports per km2", values: [12.5, 18], unit: "per km2", change: :none },
{ label: "Median days to close", values: [9, 9], unit: "days" },
{ label: "Requests per 1,000 people", values: [41, 33] }
]) %>
Slope: one shared axis, coloured by direction
shared_axis: true puts every row on one scale (same unit only); tone: :direction colours each line up or down.
-
Pothole
1,210 980 -19%
-
Graffiti
640 860 +34%
-
Noise
380 410 +8%
-
Litter
300 300 no change
| Measure | 2024 | 2025 | Change |
|---|---|---|---|
| Pothole | 1,210 | 980 | 19% lower |
| Graffiti | 640 | 860 | 34% higher |
| Noise | 380 | 410 | 8% higher |
| Litter | 300 | 300 | no change |
Show code
<%= rui_chart(
type: :slope,
title: "Requests by type, 2024 to 2025",
columns: ["2024", "2025"],
shared_axis: true,
tone: :direction,
rows: [
{ label: "Pothole", values: [1210, 980] },
{ label: "Graffiti", values: [640, 860] },
{ label: "Noise", values: [380, 410] },
{ label: "Litter", values: [300, 300] }
]) %>
Links on marks
Plain click follows the link. Cmd/Ctrl click and middle click open a new tab (0.77.1+).
Show code
<%= rui_chart(
type: :hbar,
title: "Jump to a section",
data: [
{ label: "Lines and areas", value: 12, href: "#gallery-lines" },
{ label: "Heatmap", value: 7, href: "#gallery-grids" }
]) %>
Compact axis
| Category | Value |
|---|---|
| 2011 | 81.6k |
| 2016 | 86k |
| 2021 | 89k |
Show code
<%= rui_chart(
type: :bar,
title: "Population, compact axis",
value_format: :compact,
data: [
{ label: "2011", value: 81_614 },
{ label: "2016", value: 86_038 },
{ label: "2021", value: 89_040 }
]) %>
Negative values
| Category | Value |
|---|---|
| A | 5 |
| B | 8 |
| C | 2 |
Show code
<%= rui_chart(
type: :bar,
title: "Net change",
data: [
{ label: "A", value: -5 },
{ label: "B", value: 8 },
{ label: "C", value: -2 }
]) %>
Long category labels
| Category | Value |
|---|---|
| Neighbourhood population growth by census tract | 4 |
| Short | 9 |
Show code
<%= rui_chart(
type: :bar,
title: "Long category labels",
data: [
{ label: "Neighbourhood population growth by census tract", value: 4 },
{ label: "Short", value: 9 }
]) %>
Eighteen years, thinned labels
| Category | Open | Approved | Closed |
|---|---|---|---|
| 2008 | 0 | 0 | 1 |
| 2009 | 0 | 0 | 2 |
| 2010 | 0 | 0 | 2 |
| 2011 | 0 | 0 | 0 |
| 2012 | 0 | 0 | 2 |
| 2013 | 0 | 0 | 9 |
| 2014 | 0 | 0 | 5 |
| 2015 | 0 | 1 | 4 |
| 2016 | 0 | 0 | 6 |
| 2017 | 0 | 0 | 4 |
| 2018 | 0 | 0 | 4 |
| 2019 | 0 | 1 | 2 |
| 2020 | 2 | 1 | 5 |
| 2021 | 2 | 1 | 3 |
| 2022 | 4 | 3 | 1 |
| 2023 | 0 | 0 | 1 |
| 2024 | 2 | 0 | 2 |
| 2025 | 1 | 2 | 0 |
Show code
<%= rui_chart(
type: :bar,
layout: :stacked,
title: "Applications filed per year",
categories: ["2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
series: [
{ name: "Open", values: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 0, 2, 1] },
{ name: "Approved", values: [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 3, 0, 0, 2] },
{ name: "Closed", values: [1, 2, 2, 0, 2, 9, 5, 4, 6, 4, 4, 2, 5, 3, 1, 1, 2, 0] }
],
show_share: true) %>
Bars on a phone
Below 480 px this renders as horizontal bars.
-
Category 1
-
Category 2
-
Category 3
-
Category 4
-
Category 5
-
Category 6
-
Category 7
-
Category 8
| Category | Value |
|---|---|
| Category 1 | 3 |
| Category 2 | 6 |
| Category 3 | 9 |
| Category 4 | 12 |
| Category 5 | 15 |
| Category 6 | 18 |
| Category 7 | 21 |
| Category 8 | 24 |
Show code
<%= rui_chart(
type: :bar,
responsive: :hbar,
title: "Eight categories",
data: [
{ label: "Category 1", value: 3 },
{ label: "Category 2", value: 6 },
{ label: "Category 3", value: 9 },
{ label: "Category 4", value: 12 },
{ label: "Category 5", value: 15 },
{ label: "Category 6", value: 18 },
{ label: "Category 7", value: 21 },
{ label: "Category 8", value: 24 }
]) %>
Lines and areas
Fixed height at any width; labels are HTML at 12 px.
Projection, band and annotation
- Census
- 2016: Boundary change
| Category | Census |
|---|---|
| 2011 | 81,614 |
| 2016 | 86,038 |
| 2021 | 89,040 |
| If built | 0 |
Show code
<%= rui_chart(
type: :line,
title: "Population, 4 neighbourhoods",
series: [
{ name: "Census", points: [["2011", 81_614], ["2016", 86_038], ["2021", 89_040]] }
],
projection: { from: "2021", to: "If built", value: 108_258 },
band: { at: "If built", low: 104_525, high: 112_761, label: "If every open or approved application is built" },
annotations: [{ at: "2016", text: "Boundary change" }],
value_format: :number) %>
Area, two series
- A
- B
| Category | A | B |
|---|---|---|
| 2021 | 4 | 2 |
| 2022 | 7 | 3 |
| 2023 | 5 | 6 |
Show code
<%= rui_chart(
type: :area,
title: "Area with two series",
series: [
{ name: "A", points: [["2021", 4], ["2022", 7], ["2023", 5]] },
{ name: "B", points: [["2021", 2], ["2022", 3], ["2023", 6]] }
]) %>
Crosshair
- Henry Farm
- Pleasant View
| Category | Henry Farm | Pleasant View |
|---|---|---|
| 2011 | 11,333 | 16,144 |
| 2016 | 15,723 | 15,818 |
| 2021 | 19,840 | 15,220 |
Show code
<%= rui_chart(
type: :area,
title: "Two neighbourhoods",
crosshair: true,
value_format: :number,
series: [
{ name: "Henry Farm", points: [["2011", 11_333], ["2016", 15_723], ["2021", 19_840]] },
{ name: "Pleasant View", points: [["2011", 16_144], ["2016", 15_818], ["2021", 15_220]] }
]) %>
End labels instead of a legend
| Category | Don Valley Village | Tam O'Shanter-Sullivan | Henry Farm |
|---|---|---|---|
| 2011 | 26,739 | 27,398 | 11,333 |
| 2016 | 27,051 | 27,446 | 15,723 |
| 2021 | 26,775 | 27,205 | 19,840 |
Show code
<%= rui_chart(
type: :line,
title: "Three neighbourhoods",
end_labels: true,
value_format: :number,
series: [
{ name: "Don Valley Village", points: [["2011", 26_739], ["2016", 27_051], ["2021", 26_775]] },
{ name: "Tam O'Shanter-Sullivan", points: [["2011", 27_398], ["2016", 27_446], ["2021", 27_205]] },
{ name: "Henry Farm", points: [["2011", 11_333], ["2016", 15_723], ["2021", 19_840]] }
]) %>
Parts of a whole
Donut, pie and the single 100% bar.
Donut with a centre total
Slices under 3% group into Other.
- Under review 31 (43.7%)
- Approved 28 (39.4%)
- Closed 11 (15.5%)
- Other 1 (1.4%)
| Category | Value |
|---|---|
| Under review | 31 (43.7%) |
| Approved | 28 (39.4%) |
| Closed | 11 (15.5%) |
| Other | 1 (1.4%) |
Show code
<%= rui_chart(
type: :donut,
title: "Status today",
center_label: "applications",
data: [
{ label: "Under review", value: 31 },
{ label: "Approved", value: 28 },
{ label: "Closed", value: 11 },
{ label: "Tiny", value: 1 }
]) %>
Pie
- A 5 (62.5%)
- B 3 (37.5%)
| Category | Value |
|---|---|
| A | 5 (62.5%) |
| B | 3 (37.5%) |
Show code
<%= rui_chart(
type: :pie,
title: "Pie",
data: [{ label: "A", value: 5 }, { label: "B", value: 3 }]) %>
100% bar
- Under Review 12
- Closed 38
- Approved 9
| Category | Value | Share |
|---|---|---|
| Under Review | 12 | 12 of 59 · 20% |
| Closed | 38 | 38 of 59 · 64% |
| Approved | 9 | 9 of 59 · 15% |
Show code
<%= rui_chart(
type: :stacked,
title: "Development applications by status",
data: [
{ label: "Under Review", value: 12 },
{ label: "Closed", value: 38 },
{ label: "Approved", value: 9 }
]) %>
100% bar, three shares
- Under review 31
- Approved 28
- Closed 11
| Category | Value | Share |
|---|---|---|
| Under review | 31 | 31 of 70 · 44% |
| Approved | 28 | 28 of 70 · 40% |
| Closed | 11 | 11 of 70 · 16% |
Show code
<%= rui_chart(
type: :stacked,
title: "Shares",
data: [
{ label: "Under review", value: 31 },
{ label: "Approved", value: 28 },
{ label: "Closed", value: 11 }
]) %>
Small and statistical
Sparklines for tiles and table cells; histograms for spread.
Sparkline
| Index | Value |
|---|---|
| 1 | 3 |
| 2 | 5 |
| 3 | 4 |
| 4 | 8 |
| 5 | 6 |
| 6 | 9 |
| 7 | 7 |
| 8 | 10 |
| 9 | 8 |
| 10 | 12 |
| 11 | 9 |
| 12 | 14 |
Show code
<%= rui_chart(
type: :sparkline,
title: "Reports per month, last 12",
values: [3, 5, 4, 8, 6, 9, 7, 10, 8, 12, 9, 14]) %>
Sparkline columns with a zero baseline
| Index | Value |
|---|---|
| 1 | 3 |
| 2 | 2 |
| 3 | 4 |
| 4 | 8 |
| 5 | 1 |
| 6 | 9 |
| 7 | 7 |
Show code
<%= rui_chart(
type: :sparkline,
title: "Net change per week",
variant: :column,
height: 32,
baseline: true,
values: [3, -2, 4, 8, -1, 9, 7]) %>
Histogram, explicit edges
| Category | days |
|---|---|
| 0 to 89 | 1 days |
| 90 to 364 | 2 days |
| 365 to 729 | 3 days |
| 730 to 1459 | 0 days |
| 1460 to 2500 | 8 days |
Show code
<%= rui_chart(
type: :histogram,
title: "Days in review",
unit: "days",
bin_edges: [0, 90, 365, 730, 1460, 2500],
values: [82, 227, 248, 438, 648, 649, 1488, 1625, 1625, 1635, 1758, 1854, 2137, 2216]) %>
Histogram, eight auto bins
| Category | Value |
|---|---|
| 2 to 14 | 9 |
| 15 to 27 | 7 |
| 28 to 40 | 9 |
| 41 to 53 | 7 |
| 54 to 66 | 8 |
| 67 to 79 | 7 |
| 80 to 92 | 9 |
| 93 to 106 | 4 |
Show code
<%= rui_chart(
type: :histogram,
title: "Eight auto bins",
bins: 8,
values: [37, 74, 11, 48, 85, 22, 59, 96, 33, 70, 7, 44, 81, 18, 55, 92, 29, 66, 3, 40, 77, 14, 51, 88, 25, 62, 99, 36, 73, 10, 47, 84, 21, 58, 95, 32, 69, 6, 43, 80, 17, 54, 91, 28, 65, 2, 39, 76, 13, 50, 87, 24, 61, 98, 35, 72, 9, 46, 83, 20]) %>
Heatmap and timeline strip
Grids of values and periods on a date axis.
Heatmap
| Row | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Pothole | 4 reports | 0 reports | 9 reports | 2 reports | 1 reports | 0 reports | 3 reports | 5 reports | 2 reports | 1 reports | 0 reports | 2 reports |
| Graffiti | no data | 2 reports | 1 reports | 0 reports | 0 reports | 1 reports | 2 reports | 0 reports | 1 reports | 3 reports | 1 reports | 0 reports |
| Water | 1 reports | 1 reports | 0 reports | 3 reports | 2 reports | 4 reports | 2 reports | 1 reports | 0 reports | 0 reports | 1 reports | 1 reports |
| Road damage | 0 reports | 0 reports | 2 reports | 1 reports | 6 reports | 2 reports | 1 reports | 1 reports | 0 reports | 2 reports | 0 reports | 1 reports |
| Other | 3 reports | 2 reports | 2 reports | 1 reports | 1 reports | 2 reports | 4 reports | 3 reports | 2 reports | 2 reports | 2 reports | 1 reports |
Show code
<%= rui_chart(
type: :heatmap,
title: "Reports by month and type",
unit: "reports",
rows: ["Pothole", "Graffiti", "Water", "Road damage", "Other"],
columns: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
values: [
[4, 0, 9, 2, 1, 0, 3, 5, 2, 1, 0, 2],
[nil, 2, 1, 0, 0, 1, 2, 0, 1, 3, 1, 0],
[1, 1, 0, 3, 2, 4, 2, 1, 0, 0, 1, 1],
[0, 0, 2, 1, 6, 2, 1, 1, 0, 2, 0, 1],
[3, 2, 2, 1, 1, 2, 4, 3, 2, 2, 2, 1]
]) %>
Timeline strip, three groups
Hatched end: still open. Pinned with as_of: so the day counts match this page.
-
File 19-114
-
File 17-052
-
File 20-231
-
File 21-008
-
File 22-301
-
File 15-207
-
File 09-411
-
File 12-038
-
File 24-090
- open
- approved
- closed
| Item | Status | From | To | Days |
|---|---|---|---|---|
| File 19-114 | open | Mar 4, 2019 | ongoing | 2,781 |
| File 17-052 | approved | Feb 1, 2017 | Nov 20, 2019 | 1,022 |
| File 17-052 | open | Nov 20, 2019 | ongoing | 2,520 |
| File 20-231 | open | Aug 17, 2020 | ongoing | 2,249 |
| File 21-008 | open | Jan 12, 2021 | ongoing | 2,101 |
| File 22-301 | open | Oct 3, 2022 | ongoing | 1,472 |
| File 15-207 | approved | May 20, 2015 | Jan 10, 2018 | 966 |
| File 15-207 | closed | Jan 10, 2018 | Feb 1, 2020 | 752 |
| File 09-411 | approved | Mar 11, 2009 | Jan 5, 2011 | 665 |
| File 09-411 | closed | Jan 5, 2011 | Aug 20, 2013 | 958 |
| File 12-038 | closed | Apr 2, 2012 | Jun 30, 2014 | 819 |
| File 24-090 | open | Sep 1, 2024 | ongoing | 773 |
Show code
<%= rui_chart(
type: :timeline_strip,
title: "Applications by file, time in review",
subtitle: "Longest first",
id: "gallery-strip",
rows: [
{ label: "File 19-114", segments: [{ from: "2019-03-04", group: "open", note: "Under review" }], markers: [{ at: "2021-06-02", label: "Second filing" }] },
{ label: "File 20-231", segments: [{ from: "2020-08-17", group: "open", note: "Zoning amendment" }] },
{ label: "File 17-052", segments: [
{ from: "2017-02-01", to: "2019-11-20", group: "approved" },
{ from: "2019-11-20", group: "open", note: "Site plan" }
] },
{ label: "File 15-207", segments: [
{ from: "2015-05-20", to: "2018-01-10", group: "approved" },
{ from: "2018-01-10", to: "2020-02-01", group: "closed" }
] },
{ label: "File 21-008", segments: [{ from: "2021-01-12", group: "open" }], markers: [
{ at: "2023-03-01", label: "Resubmitted" },
{ at: "2024-09-15", label: "Public meeting" }
] },
{ label: "File 12-038", segments: [
{ from: "2012-04-02", to: "2014-06-30", group: "closed", note: "Withdrawn" }
] },
{ label: "File 22-301", segments: [{ from: "2022-10-03", group: "open" }] },
{ label: "File 09-411", segments: [
{ from: "2009-03-11", to: "2011-01-05", group: "approved" },
{ from: "2011-01-05", to: "2013-08-20", group: "closed" }
] },
{ label: "File 24-090", segments: [{ from: "2024-09-01", group: "open", note: "First submission" }] }
],
domain: { from: "2008-01-01", to: "today" },
as_of: "2026-10-14",
groups: { "open" => 0, "approved" => 1, "closed" => 2 },
sort: :days,
max_rows: 12) %>
// Close two files
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"rows":[{"label":"File 19-114","segments":[{"group":"approved","to":"2026-06-30"}]},{"label":"File 20-231","segments":[{"group":"closed","to":"2025-12-01"}]}]} }))
// Reopen
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"rows":[{"label":"File 19-114","segments":[{"group":"open","to":null}]},{"label":"File 20-231","segments":[{"group":"open","to":null}]}]} }))
Calendar
0.81.0+. One column per week, coloured by count; 0 is the empty track colour. Below 480 px it shows the last two months (0.83.0+). Pinned with as_of:.
Oct 15, 2025 to Oct 14, 2026 Aug 15, 2026 to Oct 14, 2026
| Month | Total | Busiest day |
|---|---|---|
| Oct 2025 | 57 reports | Oct 15 (6 reports) |
| Nov 2025 | 135 reports | Nov 5 (8 reports) |
| Dec 2025 | 154 reports | Dec 3 (8 reports) |
| Jan 2026 | 75 reports | Jan 7 (5 reports) |
| Feb 2026 | 105 reports | Feb 4 (7 reports) |
| Mar 2026 | 150 reports | Mar 4 (8 reports) |
| Apr 2026 | 75 reports | Apr 1 (5 reports) |
| May 2026 | 68 reports | May 6 (5 reports) |
| Jun 2026 | 116 reports | Jun 10 (7 reports) |
| Jul 2026 | 123 reports | Jul 1 (7 reports) |
| Aug 2026 | 173 reports | Aug 5 (9 reports) |
| Sep 2026 | 95 reports | Sep 2 (6 reports) |
| Oct 2026 | 37 reports | Oct 7 (6 reports) |
Show code
<%= rui_chart(
type: :calendar,
title: "Reports per day",
unit: "reports",
days: reports.group(:reported_on).count,
as_of: "2026-10-14",
week_start: :sunday,
zero_label: "No reports") %>
Timeline strip, one group, every year labelled
-
A
-
B
-
C
| Item | Status | From | To | Days |
|---|---|---|---|---|
| A | Period | Feb 1, 2023 | Sep 1, 2023 | 212 |
| B | Period | May 15, 2023 | ongoing | 1,248 |
| C | Period | Jan 1, 2024 | Jan 2, 2024 | 1 |
Show code
<%= rui_chart(
type: :timeline_strip,
title: "One group, auto domain",
label_every: 1,
as_of: "2026-10-14",
rows: [
{ label: "A", segments: [{ from: "2023-02-01", to: "2023-09-01" }] },
{ label: "B", segments: [{ from: "2023-05-15" }] },
{ label: "C", segments: [{ from: "2024-01-01", to: "2024-01-02" }] }
]) %>
Play through time
A play button, a scrubber and a step chip under the chart.
Line with projection
- Census
| Category | Census |
|---|---|
| 2011 | 81,614 |
| 2016 | 86,038 |
| 2021 | 89,040 |
| If built | 0 |
Show code
<%= rui_chart(
type: :line,
title: "Population with projection",
id: "gallery-play-line",
timeline: { label: "Play 2011 to If built", step_ms: 600 },
series: [
{ name: "Census", points: [["2011", 81_614], ["2016", 86_038], ["2021", 89_040]] }
],
projection: { from: "2021", to: "If built", value: 108_258 },
band: { at: "If built", low: 104_525, high: 112_761, label: "If every open or approved application is built" },
value_format: :number) %>
Stacked columns, solid button
| Category | Open | Approved | Closed |
|---|---|---|---|
| 2008 | 0 | 0 | 1 |
| 2009 | 0 | 0 | 2 |
| 2010 | 0 | 0 | 2 |
| 2011 | 0 | 0 | 0 |
| 2012 | 0 | 0 | 2 |
| 2013 | 0 | 0 | 9 |
| 2014 | 0 | 0 | 5 |
| 2015 | 0 | 1 | 4 |
| 2016 | 0 | 0 | 6 |
| 2017 | 0 | 0 | 4 |
| 2018 | 0 | 0 | 4 |
| 2019 | 0 | 1 | 2 |
| 2020 | 2 | 1 | 5 |
| 2021 | 2 | 1 | 3 |
| 2022 | 4 | 3 | 1 |
| 2023 | 0 | 0 | 1 |
| 2024 | 2 | 0 | 2 |
| 2025 | 1 | 2 | 0 |
Show code
<%= rui_chart(
type: :bar,
layout: :stacked,
title: "Applications filed per year",
id: "gallery-play-stacked",
timeline: { label: "Play 2008 to 2025", button: :solid },
categories: ["2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
series: [
{ name: "Open", values: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 0, 2, 1] },
{ name: "Approved", values: [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 3, 0, 0, 2] },
{ name: "Closed", values: [1, 2, 2, 0, 2, 9, 5, 4, 6, 4, 4, 2, 5, 3, 1, 1, 2, 0] }
],
show_share: true) %>
Captions and a value readout
Everything named to date
| Category | Approved | Open or approved |
|---|---|---|
| 2022 | 865 | 3,000 |
| 2023 | 865 | 5,200 |
| 2024 | 1,450 | 8,100 |
| 2025 | 2,315 | 11,572 |
Show code
<%= rui_chart(
type: :bar,
title: "Homes named in applications",
id: "gallery-play-captions",
unit: "homes",
value_format: :number,
categories: ["2022", "2023", "2024", "2025"],
series: [
{ name: "Approved", values: [865, 865, 1450, 2315] },
{ name: "Open or approved", values: [3000, 5200, 8100, 11_572] }
],
timeline: { label: "Play 2022 to 2025", captions: ["2022: two approvals", "2023: first site plan filed", nil, "2025: the largest application filed"], final: "Everything named to date", readout: { series: "Open or approved", mode: :value, label: "homes named" } }) %>
Cumulative readout
| Category | Open | Approved | Closed |
|---|---|---|---|
| 2008 | 0 | 0 | 1 |
| 2009 | 0 | 0 | 2 |
| 2010 | 0 | 0 | 2 |
| 2011 | 0 | 0 | 0 |
| 2012 | 0 | 0 | 2 |
| 2013 | 0 | 0 | 9 |
| 2014 | 0 | 0 | 5 |
| 2015 | 0 | 1 | 4 |
| 2016 | 0 | 0 | 6 |
| 2017 | 0 | 0 | 4 |
| 2018 | 0 | 0 | 4 |
| 2019 | 0 | 1 | 2 |
| 2020 | 2 | 1 | 5 |
| 2021 | 2 | 1 | 3 |
| 2022 | 4 | 3 | 1 |
| 2023 | 0 | 0 | 1 |
| 2024 | 2 | 0 | 2 |
| 2025 | 1 | 2 | 0 |
Show code
<%= rui_chart(
type: :bar,
layout: :stacked,
title: "Applications filed, running total",
id: "gallery-play-cumulative",
categories: ["2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
series: [
{ name: "Open", values: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 4, 0, 2, 1] },
{ name: "Approved", values: [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 3, 0, 0, 2] },
{ name: "Closed", values: [1, 2, 2, 0, 2, 9, 5, 4, 6, 4, 4, 2, 5, 3, 1, 1, 2, 0] }
],
timeline: { label: "Play 2008 to 2025", readout: { mode: :cumulative, label: "applications filed" } }) %>
Update from the app
The buttons dispatch rui-chart:update. Marks tween; tooltips and the hidden table are rewritten.
Grouped columns
| Category | Pedestrian | Cyclist |
|---|---|---|
| 2021 | 6 collisions | 1 collisions |
| 2022 | 8 collisions | 2 collisions |
| 2023 | 7 collisions | 1 collisions |
| 2024 | 9 collisions | 3 collisions |
Show code
<%= rui_chart(
type: :bar,
title: "Collisions by year",
id: "gallery-update-grouped",
unit: "collisions",
categories: ["2021", "2022", "2023", "2024"],
series: [
{ name: "Pedestrian", values: [6, 8, 7, 9] },
{ name: "Cyclist", values: [1, 2, 1, 3] }
]) %>
// New values
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"categories":["2021","2022","2023","2024"],"series":[{"name":"Pedestrian","values":[2,9,3,12]},{"name":"Cyclist","values":[4,1,6,2]}]} }))
// Two years only
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"categories":["2021","2022"],"series":[{"name":"Pedestrian","values":[5,5]},{"name":"Cyclist","values":[2,2]}]} }))
// Reset
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"categories":["2021","2022","2023","2024"],"series":[{"name":"Pedestrian","values":[6,8,7,9]},{"name":"Cyclist","values":[1,2,1,3]}]} }))
Area morph
- A
| Category | A |
|---|---|
| Q1 | 4 |
| Q2 | 7 |
| Q3 | 5 |
| Q4 | 9 |
Show code
<%= rui_chart(
type: :area,
title: "Quarterly",
id: "gallery-update-area",
series: [{ name: "A", points: [["Q1", 4], ["Q2", 7], ["Q3", 5], ["Q4", 9]] }]) %>
// New values
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"series":[{"name":"A","values":[9,2,8,3]}]} }))
// Grow to 30
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"series":[{"name":"A","values":[12,25,18,30]}]} }))
// Down to -100
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"series":[{"name":"A","values":[-100,5,8,2]}]} }))
// Reset
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"series":[{"name":"A","values":[4,7,5,9]}]} }))
Donut re-sweep
- Open 14 (18.4%)
- Approved 9 (11.8%)
- Closed 53 (69.7%)
| Category | Value |
|---|---|
| Open | 14 (18.4%) |
| Approved | 9 (11.8%) |
| Closed | 53 (69.7%) |
Show code
<%= rui_chart(
type: :donut,
title: "Status",
id: "gallery-update-donut",
center_label: "applications",
data: [
{ label: "Open", value: 14 },
{ label: "Approved", value: 9 },
{ label: "Closed", value: 53 }
]) %>
// New values
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"data":[{"label":"Open","value":30},{"label":"Approved","value":30},{"label":"Closed","value":16}]} }))
// Reset
figure.dispatchEvent(new CustomEvent("rui-chart:update", { detail: {"data":[{"label":"Open","value":14},{"label":"Approved","value":9},{"label":"Closed","value":53}]} }))
Edge cases
What the chart does with empty, zero and bad input.
All zero
| Category | Value |
|---|---|
| a | 0 |
| b | 0 |
All values are zero.
Show code
<%= rui_chart(
type: :bar,
title: "All zero",
data: [{ label: "a", value: 0 }, { label: "b", value: 0 }]) %>
Tooltips off
Hover still dims the other marks.
| Category | Value |
|---|---|
| a | 3 |
| b | 5 |
Show code
<%= rui_chart(
type: :bar,
title: "tooltips: false",
tooltips: false,
data: [{ label: "a", value: 3 }, { label: "b", value: 5 }]) %>
Empty timeline strip
No applications yet
Show code
<%= rui_chart(
type: :timeline_strip,
title: "Empty",
rows: [],
empty_text: "No applications yet") %>
Timeline strip, rows it drops
A future start, an unreadable date and an end before its start are dropped; only File A draws.
-
File A
| Item | Status | From | To | Days |
|---|---|---|---|---|
| File A | Period | Jan 1, 2020 | Jun 1, 2022 | 882 |
Show code
<%= rui_chart(
type: :timeline_strip,
title: "Dropped rows",
as_of: "2026-10-14",
domain: { from: "2018-01-01", to: "today" },
rows: [
{ label: "File A", segments: [{ from: "2020-01-01", to: "2022-06-01" }] },
{ label: "Future filing", segments: [{ from: "2027-01-01" }] },
{ label: "Bad row", segments: [{ from: "not a date" }, { from: "2020-01-01", to: "2019-01-01" }] }
]) %>
Dashboard Patterns
Common dashboard cards built only from existing options. Each card is your own markup around one rui_chart.
KPI tiles
A number, a delta and a sparkline. The tile is your markup; the sparkline is type: :sparkline, line or variant: :column.
Reports this month
| Index | Value |
|---|---|
| 1 | 150 |
| 2 | 162 |
| 3 | 158 |
| 4 | 171 |
| 5 | 180 |
| 6 | 176 |
| 7 | 191 |
| 8 | 214 |
Median days to close
| Index | Value |
|---|---|
| 1 | 14 |
| 2 | 13 |
| 3 | 12 |
| 4 | 12 |
| 5 | 11 |
| 6 | 10 |
| 7 | 11 |
| 8 | 9 |
Open applications
| Index | Value |
|---|---|
| 1 | 24 |
| 2 | 25 |
| 3 | 27 |
| 4 | 26 |
| 5 | 28 |
| 6 | 28 |
| 7 | 29 |
| 8 | 31 |
Net change, homes
| Index | Value |
|---|---|
| 1 | 40 |
| 2 | 20 |
| 3 | 60 |
| 4 | 10 |
| 5 | 30 |
| 6 | 90 |
| 7 | 120 |
| 8 | 140 |
Show code
<div class="p-4 rounded-lg border">
<p class="text-sm">Reports this month</p>
<div class="flex items-baseline gap-2">
<span class="text-3xl font-semibold">214</span>
<%= rui_badge("+12%", color: :success, size: :sm) %>
</div>
<%= rui_chart(type: :sparkline, values: [150, 162, 158, 171, 180, 176, 191, 214]) %>
</div>
Leaderboard
hbar with a highlighted row, values on the bars, and max_rows: folding the tail into Other.
-
Pothole
1.2k
-
Graffiti
860
-
Bulky items
640
-
Streetlight
410
-
Noise
380
-
Litter
300
-
Other 3
550
| Category | Value |
|---|---|
| Pothole | 1.2k |
| Graffiti | 860 |
| Bulky items | 640 |
| Streetlight | 410 |
| Noise | 380 |
| Litter | 300 |
| Other 3 | 550 |
Show code
<%= rui_chart(
type: :hbar,
title: "Requests by type",
subtitle: "Last 30 days",
value_format: :compact,
show_values: true,
max_rows: 6,
data: [
{ label: "Pothole", value: 1210 },
{ label: "Graffiti", value: 860, highlight: true },
{ label: "Bulky items", value: 640 },
{ label: "Streetlight", value: 410 },
{ label: "Noise", value: 380 },
{ label: "Litter", value: 300 },
{ label: "Signals", value: 220 },
{ label: "Sidewalk", value: 180 },
{ label: "Trees", value: 150 }
]) %>
Target vs actual
A reference line for the target and an annotation for the month that changed.
- Apr: Extra crew
| Category | repairs |
|---|---|
| Jan | 38 repairs |
| Feb | 42 repairs |
| Mar | 47 repairs |
| Apr | 51 repairs |
| May | 44 repairs |
| Jun | 53 repairs |
Show code
<%= rui_chart(
type: :bar,
title: "Repairs per month",
unit: "repairs",
references: [{ value: 45, label: "Target" }],
annotations: [{ at: "Apr", text: "Extra crew" }],
data: [
{ label: "Jan", value: 38 },
{ label: "Feb", value: 42 },
{ label: "Mar", value: 47 },
{ label: "Apr", value: 51 },
{ label: "May", value: 44 },
{ label: "Jun", value: 53 }
]) %>
Forecast
projection: continues the last point; band: shows the range.
- Requests
| Category | Requests |
|---|---|
| 2019 | 410 |
| 2020 | 380 |
| 2021 | 455 |
| 2022 | 490 |
| 2023 | 530 |
| 2024 | 575 |
| 2025 | 610 |
| 2027 | 0 |
Show code
<%= rui_chart(
type: :line,
title: "Requests per year",
value_format: :number,
series: [
{ name: "Requests", points: [
["2019", 410],
["2020", 380],
["2021", 455],
["2022", 490],
["2023", 530],
["2024", 575],
["2025", 610]
] }
],
projection: { from: "2025", to: "2027", value: 700 },
band: { at: "2027", low: 660, high: 745, label: "Range if growth holds" }) %>
Compare series
crosshair: shows every series at the hovered category; end_labels: names each line at its end.
| Category | Online | Phone |
|---|---|---|
| Jan | 120 | 90 |
| Feb | 135 | 86 |
| Mar | 150 | 84 |
| Apr | 162 | 80 |
| May | 171 | 77 |
| Jun | 188 | 72 |
Show code
<%= rui_chart(
type: :area,
title: "Requests by channel",
crosshair: true,
end_labels: true,
value_format: :number,
series: [
{ name: "Online", points: [
["Jan", 120],
["Feb", 135],
["Mar", 150],
["Apr", 162],
["May", 171],
["Jun", 188]
] },
{ name: "Phone", points: [
["Jan", 90],
["Feb", 86],
["Mar", 84],
["Apr", 80],
["May", 77],
["Jun", 72]
] }
]) %>
Activity
A weekday by hour heatmap.
| Row | 6a | 8a | 10a | 12p | 2p | 4p | 6p | 8p | 10p |
|---|---|---|---|---|---|---|---|---|---|
| Mon | 2 requests | 10 requests | 8 requests | 5 requests | 8 requests | 10 requests | 11 requests | 7 requests | 4 requests |
| Tue | 2 requests | 10 requests | 8 requests | 5 requests | 8 requests | 10 requests | 11 requests | 7 requests | 4 requests |
| Wed | 2 requests | 10 requests | 8 requests | 5 requests | 8 requests | 10 requests | 11 requests | 7 requests | 4 requests |
| Thu | 2 requests | 10 requests | 8 requests | 5 requests | 8 requests | 10 requests | 11 requests | 7 requests | 4 requests |
| Fri | 2 requests | 10 requests | 8 requests | 5 requests | 8 requests | 10 requests | 11 requests | 7 requests | 4 requests |
| Sat | 0 requests | 3 requests | 7 requests | 7 requests | 9 requests | 9 requests | 5 requests | 4 requests | 3 requests |
| Sun | 0 requests | 3 requests | 7 requests | 7 requests | 9 requests | 9 requests | 5 requests | 4 requests | 3 requests |
Show code
<%= rui_chart(
type: :heatmap,
title: "Requests by weekday and hour",
unit: "requests",
rows: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
columns: ["6a", "8a", "10a", "12p", "2p", "4p", "6p", "8p", "10p"],
values: [
[2, 10, 8, 5, 8, 10, 11, 7, 4],
[2, 10, 8, 5, 8, 10, 11, 7, 4],
[2, 10, 8, 5, 8, 10, 11, 7, 4],
[2, 10, 8, 5, 8, 10, 11, 7, 4],
[2, 10, 8, 5, 8, 10, 11, 7, 4],
[0, 3, 7, 7, 9, 9, 5, 4, 3],
[0, 3, 7, 7, 9, 9, 5, 4, 3]
]) %>
Status history
A timeline strip pinned with as_of: so a saved report reads the same later.
-
Permit 22-14
-
Permit 23-07
-
Permit 24-31
-
Permit 25-02
- review
- issued
| Item | Status | From | To | Days |
|---|---|---|---|---|
| Permit 22-14 | review | Mar 1, 2022 | Jan 15, 2023 | 320 |
| Permit 22-14 | issued | Jan 15, 2023 | Feb 1, 2023 | 17 |
| Permit 23-07 | review | May 10, 2023 | ongoing | 1,253 |
| Permit 24-31 | review | Feb 20, 2024 | Nov 5, 2024 | 259 |
| Permit 24-31 | issued | Nov 5, 2024 | Nov 20, 2024 | 15 |
| Permit 25-02 | review | Jun 1, 2025 | ongoing | 500 |
Show code
<%= rui_chart(
type: :timeline_strip,
title: "Permits in review",
as_of: "2026-10-14",
domain: { from: "2022-01-01", to: "today" },
groups: { "review" => 0, "issued" => 1 },
sort: :start,
rows: [
{ label: "Permit 22-14", segments: [
{ from: "2022-03-01", to: "2023-01-15", group: "review" },
{ from: "2023-01-15", to: "2023-02-01", group: "issued" }
] },
{ label: "Permit 23-07", segments: [{ from: "2023-05-10", group: "review" }] },
{ label: "Permit 24-31", segments: [
{ from: "2024-02-20", to: "2024-11-05", group: "review" },
{ from: "2024-11-05", to: "2024-11-20", group: "issued" }
] },
{ label: "Permit 25-02", segments: [{ from: "2025-06-01", group: "review" }] }
]) %>
Distribution
A histogram of days to close.
| Category | requests |
|---|---|
| 0 to 2 | 5 requests |
| 3 to 6 | 7 requests |
| 7 to 13 | 6 requests |
| 14 to 29 | 5 requests |
| 30 to 60 | 3 requests |
Show code
<%= rui_chart(
type: :histogram,
title: "Days to close a request",
unit: "requests",
bin_edges: [0, 3, 7, 14, 30, 60],
values: [1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 6, 6, 8, 9, 9, 10, 12, 13, 15, 18, 21, 25, 29, 33, 41, 52]) %>
Story
Play through the years with your own captions and a count-up readout.
2025: the busiest year
| Category | Approved |
|---|---|
| 2021 | 210 |
| 2022 | 340 |
| 2023 | 290 |
| 2024 | 520 |
| 2025 | 610 |
Show code
<%= rui_chart(
type: :bar,
title: "Homes approved per year",
id: "pattern-story-chart",
unit: "homes",
value_format: :number,
categories: ["2021", "2022", "2023", "2024", "2025"],
series: [{ name: "Approved", values: [210, 340, 290, 520, 610] }],
timeline: { label: "Play 2021 to 2025", captions: ["2021: a slow start", nil, "2023: one large file withdrawn", nil, "2025: the busiest year"], readout: { mode: :cumulative, label: "homes approved" } }) %>
Phones
responsive: :hbar turns ten columns into rows below 480 px.
-
Ward 1
-
Ward 2
-
Ward 3
-
Ward 4
-
Ward 5
-
Ward 6
-
Ward 7
-
Ward 8
-
Ward 9
-
Ward 10
| Category | Value |
|---|---|
| Ward 1 | 57 |
| Ward 2 | 74 |
| Ward 3 | 91 |
| Ward 4 | 48 |
| Ward 5 | 65 |
| Ward 6 | 82 |
| Ward 7 | 99 |
| Ward 8 | 56 |
| Ward 9 | 73 |
| Ward 10 | 90 |
Show code
<%= rui_chart(
type: :bar,
responsive: :hbar,
title: "Requests by ward",
value_format: :number,
data: [
{ label: "Ward 1", value: 57 },
{ label: "Ward 2", value: 74 },
{ label: "Ward 3", value: 91 },
{ label: "Ward 4", value: 48 },
{ label: "Ward 5", value: 65 },
{ label: "Ward 6", value: 82 },
{ label: "Ward 7", value: 99 },
{ label: "Ward 8", value: 56 },
{ label: "Ward 9", value: 73 },
{ label: "Ward 10", value: 90 }
]) %>
Brand theming
Series colours come from --rui-chart-1 to --rui-chart-8; text, grid and tooltip from the chrome variables. Set them on :root or on one card. The dark card also sets color-scheme: dark, which flips every chrome fallback you leave unset.
| Category | Online | Phone |
|---|---|---|
| Q1 | 52 | 35 |
| Q2 | 58 | 31 |
| Q3 | 61 | 29 |
| Q4 | 66 | 25 |
| Category | Online | Phone |
|---|---|---|
| Q1 | 52 | 35 |
| Q2 | 58 | 31 |
| Q3 | 61 | 29 |
| Q4 | 66 | 25 |
Show code
:root {
--rui-chart-1: #0f766e;
--rui-chart-2: #f59e0b;
--rui-chart-bar-radius: 2px;
}
.card-dark {
color-scheme: dark;
--rui-chart-1: #2dd4bf;
--rui-chart-2: #fbbf24;
--rui-chart-text: #f4f4f5;
--rui-chart-muted: #a1a1aa;
--rui-chart-grid: #3f3f46;
}