The TRMNL Framework is a CSS/SCSS system for building layouts on ePaper. It provides layout primitives, utilities, and components for grayscale and color-capable devices.
The framework supports:
- Grayscale palettes: 1-bit (black/white), 2-bit (4 shades), and higher bit-depth grayscale devices.
- Color palettes: limited palettes (3-7 colors with pattern-based rendering) and full-spectrum color palettes.
Grayscale palettes (published in db/data/framework_devices.yml):
| ID | Grays | Bit Depth | Description |
|---|---|---|---|
bw |
2 | 1-bit | Black & White |
gray-4 |
4 | 2-bit | 4 Grays |
gray-16 |
16 | 4-bit | 16 Grays |
gray-256 |
256 | 8-bit delivery, renders as 4-bit | Smooth Grayscale (device-quantized) |
Color palettes (published in db/data/framework_devices.yml):
| ID | Colors | Type |
|---|---|---|
color-3bwr |
Black, Red, White | Limited |
color-3bwy |
Black, Yellow, White | Limited |
color-4bwry |
Black, Red, White, Yellow | Limited |
color-6a |
Red, Green, Blue, Yellow, Black, White | Limited |
color-7a |
Black, White, Red, Green, Blue, Yellow, Orange | Limited |
color-12bit |
Full spectrum | Full-spectrum |
color-24bit |
Full spectrum | Full-spectrum |
Implementation notes:
- Grayscale palette classes map to
screen--1bit,screen--2bit, orscreen--4bit(gray-256is capped at framework 4-bit classing). - Limited color palettes map to
screen--color-*classes. - Full-spectrum palettes (
color-12bit,color-24bit) usescreen--color-full. - Limited color palettes (
color-3bwr,color-3bwy,color-4bwry,color-6a,color-7a) use 1-bit grayscale for gray areas: only black and white are available for gray, so gray tokens use the same curated 1-bit dither asgrayscale/. Seelimited_palette_grayscale_1bit_idsinframework_colors.ymland thegrayscale_bit_depthfield on limited color palettes indb/data/framework_devices.yml.
Single source of truth: db/data/framework_colors.yml
Consumed by:
lib/tasks/framework.rake- SCSS token and CSS variable generationlib/framework/color_manifest.rb- resolved manifest builder, including the color-to-gray fallback mapping
Key YAML keys:
color_hues- canonical base hue ordercolor_shade_steps- canonical shade-step list used across token generationcolor_palette- hue and hue-step token hex valueslimited_palettes- palette ID to hex arrays used for limited-palette renderinghue_to_accents- hue to accent color(s) by limited palettecolor_palette_limited_ids- IDs that use limited/patterned renderinglimited_palette_grayscale_1bit_ids- limited palette IDs that use 1-bit grayscale for gray tokens (only black and white available; all of 3bwr, 3bwy, 4bwry, 6a, 7a)preview_limited_palette_white_hex- effective white used for preview mode on limited 1-bit grayscale palettes
Framework v3 themes must preserve device-capability rendering paths.
- Allowed in theme files:
- Semantic channel refs via mixins (
theme-slots.semantic-bg/text/stroke/border) - Slot token refs via mixins (
theme-slots.bg-slot/text-slot/border-level-slot/border-token-slot) - Utility token remaps via mixins (
theme-slots.utility-bg/text/stroke/border-tokenand the bulkutility-remap-*helpers)
- Semantic channel refs via mixins (
- Disallowed in theme files:
- Root palette variable overrides (
--white,--black,--gray-*,--color-*) - Protected (framework-owned) paint vars:
--bg-*--text-*--border-*
- Root palette variable overrides (
Protected vars are generated in base/_screen-mode-vars.scss and drive mode-accurate rendering (1-bit/2-bit dither, limited-palette image families, preview variants, and 4-bit+ solid paths). Overriding them in a theme bypasses device behavior.
Semantic and slot refs compile to framework semantic/slot vars (for example --framework-semantic-surface-bg-*, --framework-slot-item-meta-bg-*) that components consume. This keeps preview/raw and capability rendering logic in the core engine.
Theme IDs live in lib/framework/themes.rb (Framework::Themes), the registry the release pipeline and docs picker read. A theme file themes/<id>-theme.scss without a registry entry fails the lint.
Starter template for new themes:
app/assets/stylesheets/framework/themes/_theme-boilerplate.scss- Slot helper mixins:
app/assets/stylesheets/framework/mixins/_theme-slots.scss
Validate themes with:
rake framework:themes:lintRake tasks
| Task | Purpose |
|---|---|
rake framework:resolved_manifest |
Generate tmp/framework_colors.resolved.json (includes the color-to-gray fallback map) |
rake framework:color_tokens |
Generate _tokens_colors_generated.scss from framework_colors.yml (bakes in the color-to-gray fallback map) |
rake framework:css_variables |
Generate _variables_colors.scss (CSS custom properties) |
rake framework:colors |
Regenerate all color artifacts (color_tokens + css_variables) |
rake framework:themes:lint |
Lint theme files for protected paint/root palette overrides and registry sync |
rake framework:docs_chrome |
Refresh app/assets/static/docs_chrome.css, the Tailwind snapshot hosts without a build serve |
Framework::ColorData.load- loadframework_colors.ymlFramework::ColorData.color_hues,color_shade_steps,color_palette,limited_palettes,hue_to_accents,color_palette_limited_ids,limited_palette_grayscale_1bit_ids,preview_limited_palette_white_hex- typed accessors for color dataFramework::ColorUtils.lab_l_star,l_star_to_gray- fallback mapping utilitiesFramework::ColorManifest.compute_color_to_gray_fallback- build the color-to-gray fallback map baked into the resolved manifestFramework::Devices.palettes- palette hashes read from the device manifest that core exports todb/data/framework_devices.ymlframework_palette_optionshelper - palette hashes for UI consumers (for example the screen picker), built fromFramework::Devices.palettes
When updating db/data/framework_colors.yml:
- Run
rake framework:colorsto regenerate SCSS tokens and CSS variables. - Validate generated artifacts before committing.
| Path | Description |
|---|---|
app/assets/stylesheets/framework/config/_tokens_colors_generated.scss |
SCSS token maps and fallback maps |
app/assets/stylesheets/framework/config/_variables_colors.scss |
CSS custom properties |
Pattern art for released bundles is committed under public/images/ (grayscale, border,
and color-* directories). Released bundles reference it by absolute path; the current
bundle paints the same art from generated gradients and data URIs instead.
- Canonical docs routes live under
/framework/docs/:page. - Canonical examples routes live under
/framework/examples/:id. - Legacy
/framework/:pageroutes redirect to/framework/docs/:page. /framework/layout_examplesis still available as a legacy examples route.
app/services/screen/palette.rb(in core) - palette class definitions; reads this repo's publishedframework_colors.resolved.json- lib/framework/color_data.rb - color data loader/accessors
- lib/framework/color_utils.rb - LAB and grayscale mapping helpers
- db/data/framework_colors.yml - canonical palette data
- lib/tasks/framework.rake - color token and CSS generation
- 4.0 Breaking Changes - deferred removals shipped as deprecated 3.x aliases (numbered borders, divider variants, native-vs-shadow text stroke, bare content selectors)
Framework documentation tooling renders the live framework docs into prebuilt Markdown and generates framework/llms.txt and framework/llms-full.txt.
| Task | Purpose |
|---|---|
rake framework:generate_markdown |
Generate prebuilt docs/examples Markdown and llms*.txt outputs |
rake framework:add_heading_ids |
Add framework_heading(...) IDs to docs heading markup (maintenance) |
generate_markdownrenders docs and examples for all versions using internal requests with_raw=1, then converts HTML to Markdown.- Output goes to this repo's
public/, the directoryFramework::Staticserves, so every page's.mdtwin and bothllms*.txtfiles resolve in any host that mounts the engine. - The generated files are committed.
rake framework:release:*runs the task, so each release ships its own Markdown. APP_URLcontrols the base URL used inpublic/framework/llms.txtlinks (defaults tohttps://trmnl.com).- The host owns
/llms.txt. trmnl.com folds the framework index into it with its API and help-center links. llms.txtindexes the current version's docs pages by category and appends an Instructions section.llms-full.txtconcatenates the current version's docs Markdown.
| Path | Description |
|---|---|
public/framework/docs/{version}/*.md |
Prebuilt Markdown for docs pages |
public/framework/examples/*.md |
Prebuilt Markdown for examples pages |
public/framework/llms.txt |
Index of the current version's docs pages |
public/framework/llms-full.txt |
Full concatenated docs content for the current version |
- lib/tasks/framework_docs.rake - markdown and LLM output generation
- app/controllers/framework_controller.rb - docs/examples page registry and routing actions
- app/helpers/framework_helper.rb - page titles/descriptions used by documentation outputs