Sparkline

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleApache-2.0
Installation mix mishka.ui.gen.headless sparkline
Source View Source

Overview of the Phoenix and LiveView Headless Sparkline Component

The Mishka Chelekom Headless Sparkline draws a small trend line as server-rendered SVG. In a Phoenix template you pass a list of numbers with values and it renders one inline svg, with no JavaScript, no hook and no npm package. Because it is only markup, it works anywhere HEEx renders, from a stat card to a table cell to a line of text, and it stays correct through a LiveView patch.

The sparkline draws with currentColor, so it takes the text color around it. Set the color with a text class such as text-emerald-500 on the sparkline or on a parent. There are three types: line, area and bar. Size it with width and height, or with Tailwind size classes. It does not depend on the Chart component, and you can use either one without the other. class goes on the SVG itself.

Base UI Sparkline example - trend line with the last point marked (Phoenix headless)

Base UI Sparkline example - trend line with the last point marked (Phoenix headless)

heex
<.sparkline
  values={[4, 7, 5, 9, 8, 12, 10, 14]}
  last_point
  aria_label="Weekly signups, trending up"
  class="h-10 w-40 text-neutral-950 dark:text-white"
/>

This is the markup it renders for the example above. The line is one path, and the last point is a circle:

html
<svg viewBox="0 0 120 36" width="120" height="36" fill="none" preserveAspectRatio="none" role="img" aria-label="Weekly signups, trending up" class="chelekom-sparkline h-10 w-40 text-neutral-950 dark:text-white">
  <path d="M 2.5 33.5 L 18.93 24.2 L 35.36 30.4 L 51.79 18.0 L 68.21 21.1 L 84.64 8.7 L 101.07 14.9 L 117.5 2.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" vector-effect="non-scaling-stroke"></path>
  <circle cx="117.5" cy="2.5" r="2.0" fill="currentColor"></circle>
</svg>

Base UI Sparkline example - line, area and bar types (Phoenix headless)

Base UI Sparkline example - line, area and bar types (Phoenix headless)

heex
<div class="flex flex-wrap items-center gap-6 text-neutral-950 dark:text-white">
  <.sparkline
    :for={type <- ~w(line area bar)}
    values={[4, 7, 5, 9, 8, 12, 10, 14]}
    type={type}
    aria_label={"Weekly signups (#{type})"}
    class="h-10 w-32"
  />
</div>

Type, scale and markers

values is required: the numbers to plot, oldest first. A numeric string is read as a number, and anything else counts as 0. type is line (the default), area (the line plus a faded fill) or bar. width and height set the intrinsic size in pixels and default to 120 and 36. The SVG uses preserveAspectRatio="none", so a w-* or h-* class stretches it, and the line keeps its thickness because the stroke does not scale.

The line is scaled to the lowest and highest value in the data, which shows the shape and not the level. Set min and max to pin the scale, for example to compare several sparklines on one range. If every value is the same, the line runs through the middle and the bars fill the height. smooth rounds the line with a curve and needs at least three points. stroke_width sets the line thickness (default 1.5). fill_opacity sets the area fill (default 0.15). rounded sets the bar corner radius and gap the space between bars (default 2), both in SVG units. last_point draws a dot on the final value, and baseline draws a faint line at zero when the value range includes zero.

Type, scale and markers

heex
<.sparkline values={@revenue} type="area" smooth last_point class="h-8 w-24 text-emerald-500" />

<.sparkline values={@deltas} type="bar" baseline rounded={1} class="h-8 w-24 text-sky-500" />

<.sparkline values={@a} min={0} max={100} class="h-8 w-24" />
<.sparkline values={@b} min={0} max={100} class="h-8 w-24" />

color sets the whole sparkline's color from a CSS color string. It is written to the SVG as an inline style attribute, so prefer a text color class when your content security policy restricts inline styles.

Accessibility

A sparkline is decorative by default and renders with aria-hidden="true". Pass aria_label and it renders with role="img" and that label instead. Write the label as the summary a reader needs, such as "Revenue, up 12% over 8 weeks", because the individual points are not exposed. It has no keyboard behavior.