Framework Runtime
Different devices have different, fixed amounts of screen space. The Framework Runtime fills that space when a plugin layout renders, doing the heavy, repetitive measuring and fitting for you. Expand the "Framework Runtime" panel under any example on this site to see the stats for that render.
What it does
At a high level, it measures the space you have and then plans columns, clamps text, formats and fits values, and adjusts gaps and index widths, so everything fits neatly without manual tweaking.
- Normalizes screen context (size, orientation, bit depth, scale)
- Maps legacy responsive classes to data attributes for consistency
- Formats values and fits numbers into their containers
- Adjusts gaps so columns/grids land on integer pixel widths
- Plans multi-column layouts, re-clamps text per column, and can add a trailing "and N more" label when enabled
- Applies standalone clamping where needed outside columns
- Limits overly tall content and schedules pixel-perfect text processing
Runtime steps and stats
When the runtime executes, it performs these steps in the order below. Column Gaps runs twice: once before Overflow for the columns Overflow does not own, and once at the end.
Images
Waits for every image to settle, then recolors the adaptive ones for the current mode.
- Holds the pass until images have loaded, so later steps measure real heights
- Repaints
image--adaptivesources for the screen's bit depth, dark mode, and theme
Index Widths
Ensures item index badges render at even widths to avoid artifacts.
- Runs as one pass per terminalize, for indices outside
.columns - Runs again inside each
.columnscontainer after its layout commits - Skipped on 2-bit and higher, where any width it pinned is cleared
Value Formatting
Formats numbers to fit available space and abbreviates as needed (k, M, B).
- Accepts
data-value-format="true"ordata-value-type="number" - Respects
data-value-locale - Works with
data-fit-valuefor auto-sizing
Fit Value
Adjusts font size, line-height, and weight to fit numbers within their containers.
- Minimum font size safeguard (default 8px)
- Accepts
data-fit-valueordata-value-fit
Grid Gaps
Tweaks CSS gaps so grid column widths resolve to integer pixels.
- Disable with
data-adjust-grid-gaps="false" - Falls back to measuring child positions when
gapis not explicitly set
Column Gaps
Normalizes gaps between .column elements so column widths are integers.
- Disable with
data-adjust-column-gaps="false" - Runs as a pre-pass for non-overflow columns and a final pass after Overflow
Overflow
Plans 1..N columns with off-screen staging and commits the best fit, then re-clamps per real column width.
- Duplicates group headers across columns when needed
- Optional trailing "and N more" label for hidden items (enable with
data-overflow-counter="true") - Enforces final fit by hiding trailing items if necessary
Clamp
Clamps text to N lines.
- Word-based ellipsis
- Preserves original text
- Re-clamps when widths change
- Supports responsive data attributes (size/orientation)
- Maps legacy class utilities to
data-clamp - Applies outside and inside columns (per-column re-clamp handled by Overflow)
Table Overflow
Trims table rows that do not fit the space the table has.
- Opt in with
data-table-limit="true"on the table - Runs after Clamp, so it measures rows at their final line count
Content Limiter
Caps content at an explicit height budget, or at the space it measures as available, and flags small content.
- Set the budget with
data-content-max-height, otherwise the limiter measures the view's layout - Adds
content--small, then setsdata-clampanddata-clamp-max-height-pxon the block it has to trim
Pixel-Perfect Fonts
Wraps lines in spans and enforces even/odd widths for crisp rendering; scheduled in idle time.
- Skipped on higher bit-depth modes
- Respects centered alignment
Driving the runtime from JavaScript
The runtime starts itself. It runs one pass after the page load event, then runs again whenever a screen--* class changes on a .screen element. Content injected after that needs an explicit re-run.
Functions
-
terminalize(): runs the full pipeline and returns a Promise that resolves once the screen has settled, including the deferred pixel-perfect pass. Call it after you inject or replace content. -
executeTerminalize(): queues a run two animation frames out instead of starting one immediately. Repeated calls before it fires collapse into a single pass. -
markFrameworkReady(): setswindow.frameworkReadyand dispatchestrmnl:framework:readyonwindow. A host page calls it once its own setup is done.
Ready signals
-
window.TRMNL_PLUGINS_READY:falsewhile a pass runs andtrueonce it settles. A screenshot pipeline waits fortruebefore it captures. -
window.frameworkReady:falseuntilmarkFrameworkReady()runs. -
window.__TRMNL_BUILD__: the build stamp of the loadedplugins.js. A released bundle reports its own version (plugins.js v3.2.0) and a working checkout reportsplugins.js source. Read it when an edit does not show up and you suspect a pinned or cached file.
The stats event
Every pass dispatches trmnl:terminalize:stats on window. Its detail carries three fields.
-
steps: every step the pass ran, each with aname, adurationMs, and its own counters. -
enginesandengineCount: the subset of steps that changed something. -
errors: present only when an engine threw, as{ engine, message }entries. The pass runs the remaining engines and readiness still flips totrue.
// Re-run after injecting content into a screen.
terminalize().then(function () {
console.log("screen settled");
});
// Inspect what each pass did.
window.addEventListener("trmnl:terminalize:stats", function (event) {
console.log(event.detail.engineCount, "engines changed something");
});
Debug logging
Set window.__TRMNL_DEBUG__ = true before a pass to log what each engine decided. Renders are quiet by default. Engine failures always reach the console, debug flag or not.
plugins.js also puts TRMNLPaint and TRMNLCharts on window. Those are documented on
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.
.
Why this exists
Plugins need to fit source data into a static layout space that is device-defined and varies by model, orientation, and density. While this resembles responsive web design, the runtime provides purpose-built tools (overflow planning, per-column clamping, integer pixel alignment, and value fitting) tailored specifically for TRMNL devices.
Related Tokens
These tokens are automatically mapped to this page by token prefix.
| Token | 1-bit | 2-bit | Density 2x | 4-bit and up |
|---|---|---|---|---|
--content-scale
|
1 | - | - | - |
--device-ui-scale
|
1 | - | - | - |
--full-h
|
calc(var(--screen-h) - var(--gap) * 2) | - | - | - |
--full-w
|
calc(var(--screen-w) - var(--gap) * 2) | - | - | - |
--gap-scale
|
1 | - | - | - |
--half_horizontal-h
|
calc((var(--screen-h) - var(--gap) * 2) / 2 - var(--gap) / 2) | - | - | - |
--half_horizontal-w
|
calc((var(--screen-w) - var(--gap) * 2)) | - | - | - |
--half_vertical-h
|
calc((var(--screen-h) - var(--gap) * 2)) | - | - | - |
--half_vertical-w
|
calc((var(--screen-w) - var(--gap) * 2) / 2 - var(--gap) / 2) | - | - | - |
--modifier-scale
|
1 | - | - | - |
--modifier-text-scale
|
1 | - | - | - |
--quadrant-h
|
calc((var(--screen-h) - var(--gap) * 2) / 2 - var(--gap) / 2) | - | - | - |
--quadrant-w
|
calc((var(--screen-w) - var(--gap) * 2) / 2 - var(--gap) / 2) | - | - | - |
--screen-h
|
480px | - | - | - |
--screen-h-original
|
480px | - | - | - |
--screen-w
|
800px | - | - | - |
--screen-w-original
|
800px | - | - | - |
--text-ui-scale
|
1 | - | - | - |
--ui-scale
|
1 | - | - | - |
Related APIs
The paint half of the runtime
The same plugins.js that runs these engines also
ships TRMNLPaint, the framework's public paint API. It reads the live cascade and returns canonical Fill,
BorderFill, and TypeSpec objects, so a plugin can resolve framework colors from JavaScript while the engines
handle layout. 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.
.