Painting Charts
The chart resolvers pick evenly spaced series colors from the framework chart ramp, resolved through the live cascade. Adapters convert the resulting Fills into Highcharts color options.
Chart series
Every resolver takes an optional { el }: the id
or element whose nearest .screen ancestor
supplies the paint. Each returns a canonical
Fill; see
Paint API
Paint API
TRMNLPaint: read the live CSS cascade from JavaScript to resolve framework colors and tile patterns
TRMNLPaint is the framework's public JavaScript paint API. It reads the live CSS cascade (bit depth, dark mode, theme, limited palette, and tiles all resolved) and hands back a canonical Fill, so token mappings are never duplicated in JavaScript. Charts are just one consumer; any plugin can resolve framework colors from JS for any purpose.
for the shape.
-
series(i, count, { el }): the fill for series i of count from the screen's chart-series ramp, which opens on the screen's ink. Series are spread across the legible front of the ramp; the span is the public--framework-chart-series-spanvar, so a theme narrows it from CSS. -
ramp({ el }): every chart-series slot as an array of Fills. CSS publishes how many there are in--framework-chart-series-count, so a longer ramp reaches JavaScript without a framework release. Slots past the end of a shorter ramp come back empty.
Full-color screens draw their series from a color ramp: the ink, then seven hues in a fixed order, with steps picked for the light and dark ground separately. Grayscale and limited-palette screens keep the shade ladder, and a theme still overrides both with its own ramp.
// A single ramp step for series 1 of 4.
var s = TRMNLPaint.series(1, 4, { el: "my-chart" });
// Every ramp slot at once.
var fills = TRMNLPaint.ramp({ el: "my-chart" });
Highcharts adapters
Adapters shape a resolved Fill,
BorderFill or
TypeSpec for one specific renderer. They copy
resolved values into the renderer's native form; no adapter adds contrast heuristics, thresholds or
substitute design rules.
-
toHighcharts(fill): a solid Fill returns a flat color string; a tile Fill returns a{ pattern: { image, width, height, backgroundColor } }object for the Highcharts pattern-fill module. The field color is composited into the pattern image beneath the ink paths, matching CSS layer order, so the tile is self-contained. An empty Fill returnsnull.
The axis and label adapters live with their resolvers:
toHighchartsAxis() on
Painting Borders
Painting Borders
Read border rails as BorderFill objects for custom rails and Highcharts axes
The border resolvers read the framework border rails as BorderFill objects. Apply them to custom rails, or convert them for Highcharts axes and grid lines.
,
toHighchartsText() on
Painting Typography
Painting Typography
Read text roles as TypeSpec objects for custom text and chart labels
The typography resolver reads a text role or utility class as a TypeSpec: font, size, weight, paint, and optional stroke. Apply it to custom text, or convert it for Highcharts labels.
.
TRMNLCharts (see the next section) is the
Highcharts composition layer over TRMNLPaint: its
grid(),
axisLine() and
textStyle() methods select the documented chart
roles and delegate resolution and conversion here. Adapters for other libraries (a Chart.js
CanvasPattern, a D3
<pattern>) are a natural sibling shape.
Until one ships, resolve a Fill and translate it yourself; the Fill already carries everything a library
needs.
// Highcharts: flat color on solid panels, a self-contained pattern tile
// on 1- and 2-bit screens.
var hc = TRMNLPaint.toHighcharts(TRMNLPaint.series(0, 4, { el: "my-chart" }));
// Any other library: resolve a Fill and translate it however you like.
var fill = TRMNLPaint.bg("gray-30", { el: "my-chart" });
var css = fill.image
? fill.image + " repeat" // dither tile
: fill.color; // solid
The TRMNLCharts API
TRMNLCharts ships in the same
plugins.js runtime as
TRMNLPaint and composes Highcharts options out
of it. It resolves no paint of its own: every value it returns comes from a TRMNLPaint resolver and
adapter.
Chart
Chart
Visualize data optimized for 1-bit rendering
With careful, minimal styling choices, TRMNL can display a variety of numerical or time centric content as charts and graphs.
puts it to work in line, bar, and gauge charts.
Every method takes the same optional { el } as
the resolvers: the chart container id or element whose nearest
.screen supplies the paint. Omit it on a
single-screen plugin.
Resolving chart paint
-
paint(token, { el }): one palette token as a Highcharts fill, flat color in solid modes and a pattern object in dither modes. -
series(i, count, { el }): the Highcharts fill for series i of count from the screen's chart-series ramp. -
applySwatches({ el }): paints every element under the screen carryingdata-chart-series="i"from that same ramp, so legend markers and series stay in lockstep. An optionaldata-chart-series-countsets the series total; it defaults to the number of tagged elements. Call it every time the chart builds.
Building the options
-
options({ el }): the recommended Highcharts options for the TRMNL aesthetic, with a transparent background, no animation, no chrome, and framework-resolved axes and type. -
merge(base, overrides): a deep merge of two plain objects, where arrays and scalars replace. Layer your chart's own config overoptions()with it. -
grid({ el, dir }): the grid-line options (gridLineColor,gridLineWidth,gridLineDashStyle) from the muted themed hairline, border step 65.dir: 'h'(the default) is the horizontal yAxis grid,'v'the vertical xAxis grid. -
axisLine({ el }): thelineColorandtickColoroptions from the black border rail, which axis and ticks share. -
textStyle(role, { el }): a Highcharts text style for one framework typography role. It carries the resolved font properties and an opaque ink color, and setstextOutline: 'none'to kill the default white halo on data labels. Pass{ stroke: '<token>' }for an intentional outline.
Keeping the chart current
-
watch(el, buildFn): builds the chart now and again whenever the device, scale, mode, dark-mode or theme classes change on the screen or on a wrapper above it.buildFncreates and returns the chart instance; the previous one is destroyed before each rebuild. Returns a stop function.
Highcharts numbers do not read CSS, so resolve heights, spacing, and offsets with
TRMNLPaint.px() inside the build function. See
Paint API
Paint API
TRMNLPaint: read the live CSS cascade from JavaScript to resolve framework colors and tile patterns
TRMNLPaint is the framework's public JavaScript paint API. It reads the live CSS cascade (bit depth, dark mode, theme, limited palette, and tiles all resolved) and hands back a canonical Fill, so token mappings are never duplicated in JavaScript. Charts are just one consumer; any plugin can resolve framework colors from JS for any purpose.
.
var el = "my-chart";
// watch() rebuilds on every device, mode, dark-mode and theme change.
TRMNLCharts.watch(el, function () {
var chart = Highcharts.chart(el, TRMNLCharts.merge(TRMNLCharts.options({ el: el }), {
chart: { type: "column", height: TRMNLPaint.px(180, { el: el }) },
xAxis: { categories: ["Mon", "Tue", "Wed"] },
series: [
{ name: "Sent", data: [3, 5, 8], color: TRMNLCharts.series(0, 2, { el: el }) },
{ name: "Read", data: [2, 4, 6], color: TRMNLCharts.series(1, 2, { el: el }) }
]
}));
// Legend markers marked data-chart-series="0" / "1" pick up the same ramp.
TRMNLCharts.applySwatches({ el: el });
return chart;
});
Live example
The bar rows below are painted with
TRMNLPaint.series() and
TRMNLPaint.apply(), wrapped in
TRMNLPaint.watch(), with no charting library
involved. Change the device mode or Style in the screen picker and the bars repaint from the live
cascade: flat colors on solid panels, dither tiles on 1- and 2-bit screens.
<!-- an empty, ID'd container to paint into -->
<div id="paint-strip" class="flex flex--col gap--small w--full"></div>
<script type="text/javascript">
// plugins.js bundles TRMNLPaint; wait for it before painting.
function whenReady(cb) {
if (window.TRMNLPaint) return cb();
window.addEventListener("load", function () {
if (window.TRMNLPaint) cb();
}, { once: true });
}
whenReady(function () {
var el = "paint-strip";
var DATA = [["Scranton", 12.4], ["Stamford", 9.8], ["Nashua", 8.6],
["Utica", 7.4], ["Albany", 6.2], ["Buffalo", 4.2]];
// watch() repaints whenever the screen device/scale/mode/dark/theme classes change,
// re-reading paint from the live cascade each time.
TRMNLPaint.watch(el, function () {
var box = document.getElementById(el);
if (!box) return;
box.innerHTML = "";
DATA.forEach(function (d, i) {
var row = document.createElement("div");
row.className = "flex flex--row flex--center-y gap--small";
var cap = document.createElement("span");
cap.className = "label label--small";
cap.style.cssText = "width:88px;flex:none;";
cap.textContent = d[0];
var rail = document.createElement("div");
rail.style.cssText = "flex:1;height:16px;";
var bar = document.createElement("div");
bar.style.cssText = "height:100%;border-radius:4px;width:" + (d[1] / DATA[0][1] * 100) + "%;";
// series(i, n) resolves the fill for series i from the screen's chart
// ramp; apply() composites field + tile like the CSS pipeline.
TRMNLPaint.apply(bar, TRMNLPaint.series(i, DATA.length, { el: el }));
rail.appendChild(bar);
row.appendChild(cap);
row.appendChild(rail);
box.appendChild(row);
});
});
});
</script>
Where This Applies
These pages document the surfaces this API programs.