Current section
Files
Jump to
Current section
Files
README.md
# 🐹 gerbil
[](https://hex.pm/packages/gerbil)
[](https://gerbil.hexdocs.pm/)
This packages makes it easy to build pretty svg charts you can include in a
[Lustre](https://lustre.build) app, or render to serve as static documents!
Here's an example of a chart built with `gerbil`:
<div>
<style>
.doc-chart {
padding-block-start: var(--gap);
padding-block-end: var(--small-gap);
padding-inline-end: var(--small-gap);
padding-inline-start: var(--gap);
}
body {
--plot-colour: #4078f2;
}
body.theme-dark {
--plot-colour: var(--pink);
--x-tick-colour: var(--bg-tint-1);
--y-tick-colour: var(--bg-tint-1);
}
</style>
<pre class="doc-chart">
<svg xmlns="http://www.w3.org/2000/svg" class="gerbil" viewBox="0.0 0.0 500.0 250.0"><g stroke="var(--y-tick-colour, #eaeaea)" stroke-linecap="round"><line class="tick y" x1="21.0" x2="482.0" y1="223.2" y2="223.2"></line><line class="tick y" x1="21.0" x2="482.0" y1="169.39999999999998" y2="169.39999999999998"></line><line class="tick y" x1="21.0" x2="482.0" y1="115.6" y2="115.6"></line><line class="tick y" x1="21.0" x2="482.0" y1="61.79999999999998" y2="61.79999999999998"></line><line class="tick y" x1="21.0" x2="482.0" y1="8.0" y2="8.0"></line></g><g stroke="var(--x-tick-colour, #dddddd)" stroke-dasharray="2 3" stroke-linecap="round"><line class="tick x" x1="29.0" x2="29.0" y1="0.0" y2="231.2"></line><line class="tick x" x1="156.9552715654952" x2="156.9552715654952" y1="0.0" y2="231.2"></line><line class="tick x" x1="286.33226837060704" x2="286.33226837060704" y1="0.0" y2="231.2"></line><line class="tick x" x1="417.1309904153355" x2="417.1309904153355" y1="0.0" y2="231.2"></line></g><g fill="var(--label-colour, #b5b5b5)" font-size="13.0" style="font-variant-numeric:tabular-nums;" text-anchor="middle"><text class="label x" dominant-baseline="hanging" x="29.0" y="237.2">Jan 2026</text><text class="label x" dominant-baseline="hanging" x="156.9552715654952" y="237.2">Apr 2026</text><text class="label x" dominant-baseline="hanging" x="286.33226837060704" y="237.2">Jul 2026</text><text class="label x" dominant-baseline="hanging" x="417.1309904153355" y="237.2">Oct 2026</text></g><g fill="var(--label-colour, #b5b5b5)" font-size="13.0" style="font-variant-numeric:tabular-nums;" text-anchor="end"><text class="label y" dominant-baseline="middle" x="15.0" y="223.2">0</text><text class="label y" dominant-baseline="middle" x="15.0" y="169.39999999999998">5</text><text class="label y" dominant-baseline="middle" x="15.0" y="115.6">10</text><text class="label y" dominant-baseline="middle" x="15.0" y="61.79999999999998">15</text><text class="label y" dominant-baseline="middle" x="15.0" y="8.0">20</text></g><!-- lustre:fragment --><path class="line" d="M 41.79552715654952 169.39999999999998 L 128.52076677316293 61.79999999999998 L 300.5495207667732 8.0 L 429.926517571885 115.6 L 474.0 190.92" fill="none" stroke="var(--plot-colour, #fe2865)" stroke-linecap="round" stroke-width="2px"></path><g class="points"><circle class="point" cx="41.79552715654952" cy="169.39999999999998" fill="white" r="3.0" stroke="var(--plot-colour, #fe2865)" stroke-width="2px"></circle><circle class="point" cx="128.52076677316293" cy="61.79999999999998" fill="white" r="3.0" stroke="var(--plot-colour, #fe2865)" stroke-width="2px"></circle><circle class="point" cx="300.5495207667732" cy="8.0" fill="white" r="3.0" stroke="var(--plot-colour, #fe2865)" stroke-width="2px"></circle><circle class="point" cx="429.926517571885" cy="115.6" fill="white" r="3.0" stroke="var(--plot-colour, #fe2865)" stroke-width="2px"></circle><circle class="point" cx="474.0" cy="190.92" fill="white" r="3.0" stroke="var(--plot-colour, #fe2865)" stroke-width="2px"></circle></g><g class="band"><rect fill="var(--plot-colour, #fe2865)" height="223.2" style="opacity:0.15;" width="135.9552715654952" x="21.0" y="0.0"></rect><line fill="none" stroke="var(--plot-colour, #fe2865)" stroke-dasharray="4" stroke-linecap="round" stroke-width="1.5px" x1="156.9552715654952" x2="156.9552715654952" y1="0.0" y2="223.2"></line></g><!-- /lustre:fragment --></svg>
</pre>
</div>
This is the code you need to build it:
```gleam
import gleam/option.{None, Some}
import gleam/time/calendar.{April, Date, January, July, March, November, October}
import lustre/element
import gerbil/axis
import gerbil/plot
pub fn chart() -> String {
let sightings = [
chart.point(Date(2026, January, 10), 5, []),
chart.point(Date(2026, March, 12), 15, []),
chart.point(Date(2026, July, 11), 20, []),
chart.point(Date(2026, October, 10), 10, []),
chart.point(Date(2026, November, 10), 3, []),
]
let date =
axis.date()
|> axis.min(Date(2026, January, 1))
let gerbil_sightings = axis.int()
chart.new(x: date, y: gerbil_sightings)
|> chart.add(chart.line([], sightings))
|> chart.add(chart.points([], sightings))
|> chart.add(chart.vertical_band(None, Some(Date(2026, April, 1)), []))
|> chart.to_svg
|> element.to_string
}
```