PhoenixKitWeb.Components.Core.Chart (phoenix_kit v2.0.0)

Copy Markdown View Source

Server-rendered SVG chart primitives — no JavaScript, no external libraries. Charts re-render whenever LiveView assigns change, so they are live by construction. (They snap rather than tween: SVG geometry attributes are not CSS-transitionable.)

All charts draw in currentColor: set a text colour on the component (class="text-primary") and stroke, fill and gradient all follow it in light and dark themes.

Sizing

Each chart fills its container. The SVG uses preserveAspectRatio="none", so it stretches to whatever box you give it while strokes stay crisp via vector-effect="non-scaling-stroke". Give the wrapper a height — otherwise you inherit the viewBox's intrinsic ratio, which is rarely what you want:

<div class="h-48 w-full">
  <.line_chart id="p" data={@points} class="text-primary" />
</div>

Heavy stretching still distorts anything measured in user space: dash patterns and the bars' rounded corners. Strokes are protected; those are not.

Data

Values are plain numbers — callers map their domain (time, money, watts) to x/y first, which is what keeps these primitives generic: a price curve, a CPU graph and a signup funnel are all just {x, y} pairs.

Points may be {x, y} tuples or %{x: x, y: y} maps, and Decimal values are converted automatically (money and metrics arrive that way from Ecto). Points that aren't numeric are dropped rather than crashing the page; if nothing usable is left, the :empty slot renders.

Series are sorted by x, so out-of-order data plots correctly instead of zigzagging.

Origin: extracted from NordSwitch's live price charts; also intended as the drawing layer for phoenix_kit_dashboards widgets.

Summary

Functions

A vertical bar chart, measured from a zero baseline.

A line chart for a numeric series, optionally filled.

A minimal inline sparkline for a list of values.

Functions

bar_chart(assigns)

A vertical bar chart, measured from a zero baseline.

Examples

<.bar_chart
  id="daily-kwh"
  data={Enum.map(@days, &%{label: &1.date, value: &1.kwh})}
  aria_label="Daily consumption"
  class="text-secondary"
/>

Bars carry a native <title>, so hovering one shows its label and value with no JS. Negative values hang below the baseline instead of disappearing. A datum may set class: to colour one bar differently (%{label: "Fri", value: 22, class: "text-error"}).

Attributes

  • id (:string) (required) - Unique id (applied to the SVG element).
  • data (:list) (required) - List of %{label: term, value: number, class: optional_string}.
  • width (:integer) - Defaults to 960.
  • height (:integer) - Defaults to 200.
  • show_labels (:boolean) - Render labels beneath the bars. Defaults to false.
  • baseline (:boolean) - Draw the zero line when any value is negative (its position is not otherwise implied). Defaults to true.
  • class (:any) - Classes for the wrapper (set text-* for colour). Defaults to nil.
  • aria_label (:string) - Accessible name for the chart. Defaults to nil.
  • value_format (:any) - 1-arity fun formatting a value for its tooltip. No generic chart can infer units or locale, so pass your own. It receives the NUMBER, not your original struct — non-numeric values are dropped before this runs — so write &"$#{&1}", not &Money.to_string/1. Defaults to a compact numeric rendering. Defaults to nil.
  • Global attributes are accepted. Extra attributes for the wrapper.

Slots

  • empty - Rendered instead of the SVG when there is no usable data.

line_chart(assigns)

A line chart for a numeric series, optionally filled.

Examples

<%!-- a day of prices: x = minute of day, y = EUR/MWh --%>
<.line_chart
  id="price-day"
  data={Enum.map(@slots, &{minute_of_day(&1.starts_at), &1.eur_mwh})}
  x_domain={{0, 1440}}
  step
  marker_x={minute_of_day(@now)}
  aria_label="Electricity price today"
  class="text-primary"
/>

<%!-- a bare line, auto domains --%>
<.line_chart id="signups" data={@signups} area={false} />

With step each point holds its y until the next x (right-open steps — the correct reading for slot/interval data like prices). marker_x draws a dashed vertical "now" line. Domains default to the data's min/max, y padded by 10%; a reversed domain is normalised.

Attributes

  • id (:string) (required) - Unique id (namespaces the SVG gradient defs).
  • data (:list) (required) - Points as {x, y} tuples or %{x: , y: } maps. Non-numeric points are dropped.
  • step (:boolean) - Step interpolation (interval data). Defaults to false.
  • area (:boolean) - Fill a gradient area under the line. Defaults to true.
  • x_domain (:any) - {min, max} for x, or nil to fit data. Defaults to nil.
  • y_domain (:any) - {min, max} for y, or nil to fit data (10% padded). Defaults to nil.
  • marker_x (:any) - x position for a dashed vertical marker, or nil. Defaults to nil.
  • width (:integer) - viewBox width (the SVG scales to its container). Defaults to 960.
  • height (:integer) - viewBox height. Defaults to 240.
  • gridlines (:integer) - Number of horizontal gridlines (0 disables). Defaults to 3.
  • class (:any) - Classes for the wrapper (set text-* for colour). Defaults to nil.
  • aria_label (:string) - Accessible name. role="img" without one is an unlabelled graphic to a screen reader; pass what the chart shows. Unlabelled charts are marked decorative. Defaults to nil.
  • Global attributes are accepted. Extra attributes for the wrapper.

Slots

  • empty - Rendered instead of the SVG when there is no usable data.

sparkline(assigns)

A minimal inline sparkline for a list of values.

Examples

<.sparkline values={Enum.map(@upcoming, & &1.eur_mwh)} class="w-full h-12 text-info" />

A single value draws a flat line rather than nothing — one sample is a real state, and an empty box reads as a rendering bug.

Attributes

  • values (:list) (required) - Numbers, in order. Non-numeric values are dropped.
  • width (:integer) - viewBox width. Defaults to 200.
  • height (:integer) - viewBox height. Defaults to 48.
  • class (:any) - Classes for the wrapper (set text-* for colour). Defaults to nil.
  • aria_label (:string) - Accessible name. Left unset the sparkline is marked decorative. Defaults to nil.
  • Global attributes are accepted. Extra attributes for the wrapper.

Slots

  • empty - Rendered instead of the SVG when there is no usable data.