Theme Slots
Every surface a theme can re-point, with the mixin that sets it: semantic channels, component slots, utility remaps, border lines, and the chart ramp. Slots take token references, so each one still resolves through the device mode at render time.
Semantic channels
The semantic channels are the preferred API: a few lines re-theme the whole screen, and every component that reads a channel follows. The channels are a fixed set:
- Backgrounds:
canvas,surface,backdrop,fill-strong,fill-muted,fill-soft - Text:
text-primary,text-secondary,text-inverse - Stroke:
stroke-contrast - Borders:
border-strong,border-muted - Icon: one global channel for
image--adaptivepaint, defaulting to text-primary. See Image Image Optimize images using dithering techniques for 1-bit rendering Place images on a screen and control their size, object fit, and inversion. On 1-bit displays, dithering arranges black and white pixels so an image still reads as shades of gray. .
-
semantic-bg($semantic, $token): points a background channel at a token. -
semantic-text($semantic, $token): points a text channel at a token, carrying the full text paint (ink, tile, clip). -
semantic-stroke($semantic, $token): points the stroke channel at a token. -
semantic-border($semantic, $token): points a border channel at a token's fill paint. -
semantic-icon($token): optional paint for adaptive icons; omit it and icons follow text-primary.
Every semantic mixin takes $raw: true, which
points the channel at a root palette variable and skips the mode pipeline. The framework uses it for its
own unthemed dark defaults, where the dark remap would invert the value a second time. Theme files have
no use for it; see
Authoring Themes
.
@include theme-slots.semantic-bg("canvas", "yellow");
@include theme-slots.semantic-bg("surface", "yellow");
@include theme-slots.semantic-text("text-primary", "black");
@include theme-slots.semantic-text("text-secondary", "yellow-30");
@include theme-slots.semantic-stroke("stroke-contrast", "yellow");
@include theme-slots.semantic-border("border-muted", "yellow-30");
Component slots
Component slots tune one surface without moving a whole channel. A slot name is the variable fragment a component reads, and each slot takes one kind of paint. These are the slots components consume today:
- Background slots:
screen-backdrop,title-bar,item-meta,item-meta-emphasis-2,item-meta-emphasis-3,progress-track,progress-fill,progress-fill-emphasis-2,progress-fill-emphasis-3,progress-dot,progress-dot-current,table-meta,table-meta-device,chip - Text slots:
label-gray,chip - Border slots:
table-head-row,table-body-row,label-underline
chip is a pair, one background slot and one
text slot for the same surface: a solid marker box with readable text on it, for plugin surfaces the
framework has no component for. It is the only slot whose default names semantic channels instead of a
token, taking its field from fill-strong and
its text from text-inverse. State both halves
in a theme when the marker box needs its own pairing, the way White and Red gives it a white field with
red text.
Pointing a slot at a channel it does not take is silent. A background slot given
text-slot writes four variables no component
reads, and nothing on the screen changes.
-
bg-slot($slot, $token): a slot's background paint. -
text-slot($slot, $token): a slot's text paint, including the tile and clip. -
border-level-slot($slot, $level, $dir: h): points a slot's border at one of the framework border levels. -
border-token-slot($slot, $token, $dir: h): points a slot's border at a token's fill paint instead of a level.
Slot values cascade from the screen, so you can set them again on a component or state wrapper.
On unthemed screens the inverse class supplies local defaults for the opposite of the screen's scheme.
Inside a theme the subtree keeps the theme's slots, and the theme's inverse rule restates the ones that flip with the ground.
@include theme-slots.bg-slot("title-bar", "yellow-40");
@include theme-slots.bg-slot("progress-fill", "yellow-55");
@include theme-slots.text-slot("label-gray", "yellow-30");
@include theme-slots.border-token-slot("label-underline", "yellow-30");
.screen--theme-example .inverse {
@include theme-slots.bg-slot("progress-fill", "yellow-20");
}
Utility remaps
Utility remaps re-point the raw palette utilities
(bg--*,
text--*,
text-stroke--*), so plugin markup that names
gray tokens follows the theme without edits.
-
utility-remap-grayscale($to-hue, $side): the bulk remap of the whole grayscale ladder to a hue.$side: 'dark'(default) compresses grays into the hue's dark steps 10 to 40, keeps black as ink, and sends white to the hue;'bright'compresses into steps 45 to 75, sends black to the hue, and keeps white;'linear'maps gray steps 1:1 to hue steps. Strokes are the exception: the token sent to the hue takes the nearest hue step for its stroke instead (45 under bright, 40 under dark, 75 under linear), so outlines never dissolve into the canvas. -
utility-remap-border-grayscale($to-hue, $side): the same idea for the two border line inks, recoloring the staggered patterns without touching their geometry. -
utility-bg($token, $remap-to)/utility-text($token, $remap-to)/utility-stroke($token, $remap-to): remap one utility token, for exceptions on top of a bulk remap. -
utility-border-token($level, $dir, $token): remap one border level and direction to a token's paint. -
utility-border-level($level, $dir, $from-level): point one border level at another level's art, fields and render program included. This is the handle the 2-bit rail needs, where the four tones are literal in the line data and no token remap can reach them. Restating a level onto itself is not a no-op: it also pins that level to the family's own line polarity.
The three shipped themes show every side: Black and Yellow is a dark-side remap; White and Red is a
bright-side remap plus one exception, utility-bg("white", "red"),
so white surfaces adopt the canvas hue while white text stays ink. Dark mirrors the grayscale axis and
leans on utility-border-level for the 2-bit
rail and for the one level that maps to itself.
// White and Red: bright-side remap with one exception.
@include theme-slots.utility-remap-grayscale("red", $side: "bright");
@include theme-slots.utility-remap-border-grayscale("red", $side: "bright");
@include theme-slots.utility-bg("white", "red");
Border levels and lines
Border levels are the seven-step intensity scale the border pipeline renders: 1 is black, 2 through 6
step through the grays (gray-15, gray-30, gray-40, gray-50, gray-65), 7 is white, each with an
h and a
v direction.
border-level-slot points a component slot at a
level; utility-border-token re-points a level
itself, and the bulk line remap recolors the two dither line inks. See
Border
Border
Apply border patterns that create the illusion of different border intensities
Draw a horizontal or vertical rule on any element with the border--h and border--v utilities, named on the same 10 to 75 shade scale as backgrounds. On 1-bit displays a step renders as a dither pattern of black and white pixels, so a rule can read as gray. 4-bit and full-color screens draw all 14 steps; the other rails pair them onto seven levels.
and
Divider
Divider
Create horizontal or vertical dividers between elements
The Divider element provides a simple, standalone shorthand for horizontal and vertical separators. It uses the same border-level rendering pipeline as the Border utility and defaults to level 6.
for how levels
render.
Every border slot and remap moves two outputs: the CSS background and the SVG render program that
TRMNLPaint.border() and
TRMNLPaint.divider() export. A slot bound to a
level carries that level's pattern program. A slot or remap bound to a token exports a flat line in the
token's stroke color, because a token line has no pattern program of its own.
The chart ramp
chart-series-ramp($tokens, $span: 6) publishes
the theme's chart-series ramp. $tokens is an
ordered list running from the strongest contrast against the canvas (series 0) toward the canvas tone;
each slot aliases the token's own paint, so series inherit per-mode dithers and solids for free.
$span is the legible front of the ramp that
JavaScript spreads series across.
The ramp always publishes 16 slots, so a short list is safe: the slots past your last token are cleared
rather than left holding the framework grayscale, and
$span never reaches past that token.
Charts read the ramp through TRMNLPaint.series();
see
Painting Charts
Painting Charts
Chart series colors from the framework ramp, with Highcharts adapters
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.
and
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.
.
@include theme-slots.chart-series-ramp((
black, yellow-10, yellow-20, yellow-30, yellow-40, yellow-55, yellow-75, yellow
));