Colors
OpenTUI uses the RGBA class for color representation. The class stores 8-bit RGBA channel values plus packed metadata internally, but provides methods for working with different color formats.
Every color also carries color intent inside the packed value: an RGB snapshot, an indexed ANSI slot, or the terminal default. The renderer uses this intent to keep palette-relative colors stable when the terminal palette changes.
Creating colors
From integers (0-255)
import { RGBA } from "@opentui/core"
const red = RGBA.fromInts(255, 0, 0, 255)
const semiTransparentBlue = RGBA.fromInts(0, 0, 255, 128)
From float values (0.0-1.0)
Float values are quantized to 8-bit channels for terminal output.
const green = RGBA.fromValues(0.0, 1.0, 0.0, 1.0)
const transparent = RGBA.fromValues(1.0, 1.0, 1.0, 0.5)
From hex strings
const purple = RGBA.fromHex("#800080")
const withAlpha = RGBA.fromHex("#FF000080") // Semi-transparent red
String colors
Most component properties accept both RGBA objects and color strings:
import { Text, Box } from "@opentui/core"
// Using hex strings
Text({ content: "Hello", fg: "#00FF00" })
// Using CSS color names
Box({ backgroundColor: "red", borderColor: "white" })
// Using RGBA objects
const customColor = RGBA.fromInts(100, 150, 200, 255)
Text({ content: "Custom", fg: customColor })
// Transparent
Box({ backgroundColor: "transparent" })
The parseColor utility
The parseColor() function converts various color formats to RGBA:
import { parseColor } from "@opentui/core"
const color1 = parseColor("#FF0000") // Hex
const color2 = parseColor("blue") // CSS color name
const color3 = parseColor("transparent") // Transparent
const color4 = parseColor(RGBA.fromInts(255, 0, 0, 255)) // Pass-through
Color intent
Every RGBA value stores intent in packed metadata next to the RGBA snapshot. The renderer reads this intent to decide which ANSI sequence to emit:
rgb: a literal RGB color. The renderer emits38;2;r;g;b/48;2;r;g;b.default: the terminal’s default foreground or background. The renderer emits39/49(SGR default) so the terminal picks the right side of its palette.indexed: an indexed ANSI color. The renderer emits38;5;n/48;5;nso the user’s theme overrides for that palette slot keep working when the palette changes.
Each color still carries its RGBA values so alpha blending and non-ANSI renderers work. The intent flows end-to-end through TypeScript, the FFI, the buffer, and the native renderer.
Default foreground and background
import { RGBA } from "@opentui/core"
// Will emit SGR 39 / 49 when rendered
const fg = RGBA.defaultForeground()
const bg = RGBA.defaultBackground()
You can pass an optional snapshot color to override the RGBA fallback. The renderer uses the snapshot for early frames, before it detects the terminal palette:
const fg = RGBA.defaultForeground("#E6EDF3")
Indexed ANSI colors (0-255)
import { RGBA, ansi256IndexToRgb } from "@opentui/core"
// Indexed colors keep their palette slot even if the user changes their theme
const ansiRed = RGBA.fromIndex(1)
const ansiBrightBlue = RGBA.fromIndex(12)
// With a custom RGBA snapshot (for early frames before palette detection)
const themed = RGBA.fromIndex(4, "#1F6FEB")
// Convert an ANSI 256 index to an RGB triplet
const [r, g, b] = ansi256IndexToRgb(196)
Inspecting color intent
import { RGBA } from "@opentui/core"
const fg = RGBA.defaultForeground()
fg.intent // => "default"
const indexed = RGBA.fromIndex(10)
indexed.intent // => "indexed"
indexed.slot // => 10
const literal = RGBA.fromHex("#00FF00")
literal.intent // => "rgb"
Exported color constants and types
| Export | Description |
|---|---|
DEFAULT_FOREGROUND_RGB | [255, 255, 255] fallback RGB triplet for default fg |
DEFAULT_BACKGROUND_RGB | [0, 0, 0] fallback RGB triplet for default bg |
ColorIntent | "rgb" | "indexed" | "default" |
RGBTriplet | readonly [number, number, number] |
ansi256IndexToRgb(index) | Converts an ANSI 256 palette index to an RGB triplet |
parseColor(value) | Parses string | RGBA into an RGBA value |
Packed transport
RGBA.buffer is a Uint16Array(4). The low byte of each component stores r, g, b, and a; the high bytes together store 32 bits of metadata.
const color = RGBA.fromIndex(6)
color.buffer // Uint16Array(4)
color.intent // "indexed"
color.slot // 6
This lets native buffers compare current and next frame colors with exact integer equality. It also keeps palette intent attached to the color as it crosses the TypeScript/native boundary.
Terminal palette detection
When the terminal supports it, the renderer queries the active palette (OSC 4 for palette slots, OSC 10/11 for default fg/bg, plus other special colors where supported). Indexed colors stay mapped to the terminal’s user-configured slots, default colors stay linked to the terminal defaults, and non-truecolor renderers can re-resolve RGB fallbacks against the detected palette.
const colors = await renderer.getPalette({ size: 256 })
console.log(colors.palette[1]) // '#cc0000' or whatever red your theme uses
console.log(colors.defaultForeground, colors.defaultBackground)
Useful members:
renderer.getPalette(options?)— returns aTerminalColorssnapshot. Cached by requested size (16or256).renderer.paletteDetectionStatus—"idle" | "detecting" | "cached".renderer.clearPaletteCache()— drop cached palettes (for example, after the user changes their theme).renderer.on("palette", handler)— subscribe to refreshed palette snapshots when the detected palette changes.
getPalette throws if the renderer is suspended. Startup palette detection runs automatically only when native ANSI-256 fallback rendering needs palette data (ansi256 without truecolor rgb). Truecolor terminals query the palette only when you call getPalette() or when a palette listener exists and the terminal reports a theme change.
Alpha blending
You can use transparent cells and alpha blending for layered effects:
import { FrameBufferRenderable, RGBA } from "@opentui/core"
const canvas = new FrameBufferRenderable(renderer, {
id: "canvas",
width: 50,
height: 20,
})
// Draw with alpha blending
const semiTransparent = RGBA.fromValues(1.0, 0.0, 0.0, 0.5)
const transparent = RGBA.fromInts(0, 0, 0, 0)
canvas.frameBuffer.setCellWithAlphaBlending(10, 5, " ", transparent, semiTransparent)
Blended colors become literal RGB colors internally. This is intentional: once colors are blended, the result no longer represents a specific ANSI slot or terminal default.
Text attributes with colors
Combine colors with text attributes:
import { TextRenderable, TextAttributes, RGBA } from "@opentui/core"
const styledText = new TextRenderable(renderer, {
id: "styled",
content: "Important",
fg: RGBA.fromHex("#FFFF00"),
bg: RGBA.fromHex("#333333"),
attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE,
})
Color constants
Common colors:
// Some examples of commonly used colors
const white = RGBA.fromInts(255, 255, 255, 255)
const black = RGBA.fromInts(0, 0, 0, 255)
const red = RGBA.fromInts(255, 0, 0, 255)
const green = RGBA.fromInts(0, 255, 0, 255)
const blue = RGBA.fromInts(0, 0, 255, 255)
const transparent = RGBA.fromInts(0, 0, 0, 0)