Styling

A terminal cell holds a character, a foreground color, a background color, and a few attributes. Every CSS property maps to one of those or does nothing. The compatibility matrix lists which properties are supported.

Units #

By default, 1px and 1ch are one cell: width: 10px is ten columns, height: 10px ten rows. em, rem, and ex scale from font-size, which defaults to 1. Percentages, vw, vh, and calc() work. Lengths round to whole cells.

A terminal has one font at one size, so font-size and font-family don't change how text looks. font-size still sets em, ex, and how many rows a line-height takes.

Colors #

color and background-color accept named colors, hex with 3, 4, 6, or 8 digits, and rgb() and hsl() in either syntax.

A translucent background-color is blended with what the cell already shows, or with the terminal's background. Alpha 0 paints nothing. Gradient stops blend over the color beneath them.

System colors:

ColorMeaning
CanvasThe terminal's background. A dialog or popover uses it to cover what's behind it.
CanvasTextThe terminal's foreground.
Highlight, HighlightTextInverse video, used by ::selection.
SelectedItem, SelectedItemTextInverse video.
LinkTextBlue, the color of <a href>.
GrayTextGray.

linear-gradient() paints each cell with the color at its center. Stop positions take any length; ch counts along the gradient, so 3ch down a column is three rows. radial-gradient(), url(), opacity, filter, box-shadow, and mix-blend-mode do nothing.

Color depth #

The colorDepth option sets how many colors TermDOM uses. Its values are the bit counts screen.colorDepth reports:

colorDepthColorsA CSS color is drawn
"auto" (default)Whatever the terminal reportsAs one of the below
24AnyExactly
8256As the nearest of the 256
416As the nearest basic color

"rgb", "256", and "ansi" are the same three depths.

With "auto", TermDOM asks the terminal on attach(). It uses 24 if the terminal reports 24-bit color or runs tmux, which converts it. Otherwise it uses 8. It doesn't guess higher, because some terminals show unsupported 24-bit color as hidden text.

Alacritty, Konsole, and VS Code's terminal support 24-bit color without reporting it, but set COLORTERM. TermDOM doesn't read the environment, but you can:

const truecolor =
	process.env.COLORTERM === "truecolor" || process.env.COLORTERM === "24bit";
const term = new TermDOM({colorDepth: truecolor ? 24 : "auto"});

Media queries match each depth: color-gamut for "rgb", (color-index: 256) for "256", and (color-index: 16) for "ansi".

.banner { background: navy; }

@media (color-gamut: srgb) {
	.banner { background: linear-gradient(to right, #1e3a8a, #9333ea); }
}

@media (color-index: 256) {
	.banner { background: #5f00af; }
}

Text attributes #

PropertyTerminal attribute
font-weight 600 or more, bold, bolderBold
font-weight 300 or less, lighterDim
font-style: italicItalic
text-decoration-line: underlineUnderline
text-decoration-line: overlineOverline, where the terminal draws one
text-decoration-line: line-throughStrikethrough
text-decoration-style: doubleDouble underline where supported, otherwise single
text-transformuppercase, lowercase, and capitalize

text-decoration-color, text-decoration-thickness, letter-spacing, and word-spacing do nothing.

A line takes line-height / font-size rows, rounded down, at least one: 1.5 is one row, 2 is two.

Terminals draw italic and dim inconsistently, and some don't draw them at all. Use color when it has to look the same everywhere.

Borders #

A border takes one cell per side. The style picks the characters:

StyleGlyphs
solid, groove, ridge, inset, outsetโ”€ โ”‚ โ”Œ โ” โ”” โ”˜
doubleโ• โ•‘ โ•” โ•— โ•š โ•
dashedโ•Œ โ”†
dottedโ”„ โ”Š
hidden, noneNothing

Any border-radius above zero gives โ•ญ โ•ฎ โ•ฐ โ•ฏ corners. Widths above 1px still take one cell.

Borders that meet in a cell join:

Table borders and a fieldset's legend join this way by default.

outline recolors a box's border. On a box with no border, it draws as an overline on the top row and an underline on the bottom row, and takes no space. outline-offset does nothing.

visibility: hidden keeps the space and paints nothing.

Generated content #

::before and ::after take string content. counter() isn't supported.

::marker styles list bullets and the <summary> arrow. list-style-type draws โ€ข for disc, โ—ฆ for circle, and โ–ช for square. Counting styles such as decimal, lower-roman, and upper-alpha draw a number and a dot. <ol start> and <li value> set the count.

Pseudo-classes and pseudo-elements #

:hover matches the element under the mouse. While a rule uses it, TermDOM turns on mouse motion reporting. :focus and :focus-visible match the focused element. Also supported:

::selection styles selected text, ::highlight() styles ranges in CSS.highlights, ::placeholder styles placeholders, and ::backdrop styles what a modal dialog or popover covers.

The built-in controls take these pseudo-elements:

A meter matches :optimal-value, :low-value, or :high-value by its level.

These names come from a CSS draft and may change. The API guide lists the stable WebKit and Firefox names. ::part() doesn't reach inside the built-in controls.

CSS.highlights.set("hit", new Highlight(...ranges)) colors ranges through ::highlight(hit), which can set color, background-color, and text-decoration-line. Highlights don't change layout. Where they overlap, the higher priority wins, and ::selection paints over all of them. See examples/highlights.ts.

Default styles #

The user-agent stylesheet sets these, and any rule can override them:

ElementDefault
b, strong, th, legendBold
i, em, cite, dfn, varItalic
uUnderline
sStrikethrough
kbdBold and underlined
smallDim
codeA faint tinted background
a[href]LinkText, underlined; inverse when focused
hrA one-row horizontal rule
blockquoteA solid left border with 1ch padding
prewhite-space: pre
fieldset, textarea, dialog, [popover]A solid border with 0 1ch padding
td, thA solid border with 0 1ch padding, collapsed into the table's
table[border="0"] td, thNo border, for layout tables. Nested tables follow their own border
buttonThe label between [ and ]
summaryA โ–ธ marker when closed, โ–พ when open; list-style: none hides it
progress, meterA 10ch bar over a gray track. progress is blue; meter is green, yellow, or red by level
input, selectInline, white-space: pre
input[type=checkbox], input[type=radio]3ch wide, drawn as [x] or (x)
input[type=range]A 20ch slider: a blue fill and a โ— thumb over a gray track
dialog:modal, [popover]Centered, position: fixed, on a Canvas background
mathInline; display="block" centers it

Headings have no default style, since a terminal has one font size:

h1 { font-weight: bold; text-decoration: underline; }
h2 { font-weight: bold; }

A focused element shows a solid outline, except a text field or editing host, which shows the cursor at its caret.

Where terminals differ #

TermDOM asks the terminal what it supports rather than checking its name:

Edit this page on GitHub