Terminal CSS
The terminal-specific surface: what svelterm adds to CSS, and how familiar properties map onto a character grid. Everything here is invalid or inert in browsers by design — the same stylesheet runs in both modes.
The cell unit
One terminal cell is the atomic unit of layout:
.sidebar { width: 24cell; padding: 1cell 2cell; } chis an exact alias (1ch=1cell). Preferchin dual-target stylesheets — browsers understand it too, and on a monospace grid it means the same thing.- All lengths round to whole cells;
4.6cellis 5. - Unitless
0is valid, as in CSS.%is relative to the containing block’s cells.frworks in grid templates. - Pixel-derived units (
px,em,rem,vw,vh…) parse and are dropped — see compatibility.
Targeting one mode
svelterm evaluates @media (display-mode: terminal) as true; a display-mode: browser block is ignored by the terminal and applies in
real browsers as plain CSS:
.card {
@media (display-mode: browser) { box-shadow: 0 2px 8px #0004; }
@media (display-mode: terminal) { border: single; }
} This is the documented pattern for everything svelterm deliberately ignores (shadows, radii, fonts, transforms).
Borders
border-style takes terminal-native values drawn with box-drawing and block glyphs.
Browser values (solid, dashed…) are ignored.
| Value | Look |
|---|---|
single | ┌─┐ light box drawing |
double | ╔═╗ double lines |
rounded | ╭─╮ rounded corners |
heavy | ┏━┓ heavy lines |
ascii | +-+ plain ASCII |
eighth-cell-inner / -outer | thin eighth-block edges inside/outside the cell |
half-cell-inner / -outer | half-block edges |
full-cell | full-block frame |
- Borders are exactly one cell thick and consume layout space.
- Individual sides:
border-top: true/false(setting one side first disables the others). border-corner: h | v | nonebiases which line wins at corners.border-coloris standard, includingcurrentColor.- Tables support
border-collapse: collapsewith shared grid lines. border-collapse: collapseextends beyond tables: on any container (it inherits, so:rootopts the whole app in), adjacent bordered siblings — stacked blocks, flex items, grid items — share a single border line, merging into junction glyphs (├┬┼) in the border’s family. Without it, sibling frames stay separate as in browsers.border-collapse: separateon a child opts it back out.
.panel { border: rounded; border-color: cyan; }
.rule { border-top: true; border-style: single; } /* horizontal rule */
.list { border-collapse: collapse; } /* children share dividers */ Colour
Resolved colours are ANSI palette names or 24-bit truecolor:
- The 8 basic CSS keywords (
red,cyan, …) map to ANSI palette names — the user’s terminal theme can restyle them. - Hex,
rgb(),hsl(),hwb(),lab(),lch(),oklab(),oklch()and the 148 named colours resolve to exact truecolor. transparentmeans “no colour set”: the parent’s background shows through. There is no alpha blending.currentColorworks wherever a colour is valid.
Dark/light schemes
The terminal’s own scheme is detected by polling OSC 11 (override with run({ colorScheme }) or setColorScheme()). It drives both:
:root { color-scheme: light dark; }
.hint { color: light-dark(#64748b, #94a3b8); }
@media (prefers-color-scheme: dark) { .logo { color: cyan; } } light-dark() is usually the shortest way to theme a component for both schemes.
Named terminal colours
The sixteen ANSI names — black, red, green, yellow, blue, magenta, cyan, white and their bright* variants
(brightblack … brightwhite) — emit palette escape codes rather
than truecolor, so they follow the user’s terminal theme at any colour
depth. All other CSS named colours resolve to their hex values.
Alpha and opacity
Terminals have no alpha layer, so svelterm composites at paint time: rgba(), #rrggbbaa and slash-alpha colours blend over whatever the
cell already holds, and numeric opacity folds into the element’s
colours as a blend factor. Blending over ANSI colour names uses their
nominal xterm values; blending over the terminal’s default background
assumes black — set an explicit background on an ancestor when
compositing in light themes.
.overlay { background: rgb(0 0 0 / 0.4); } /* darkens what's beneath */
.muted { opacity: 0.6; } /* blends toward the bg */ The non-standard opacity: dim applies the terminal’s dim attribute
instead of blending. Animating opacity switches discretely — animate a
colour toward the background for a smooth fade.
Text attributes
font-weight: bold, font-style: italic, text-decoration: underline and line-through map to terminal attributes. text-transform, text-align, white-space (normal/nowrap/pre), word-break (normal/break-all), and text-overflow: ellipsis (plus non-standard ellipsis-middle, which keeps both ends of long paths) behave as
standard. Font choice and size are the terminal emulator’s business — font-family, font-size, line-height are ignored.
Text is measured in grapheme clusters and cell widths: CJK and
fullwidth characters occupy two cells, emoji (including ZWJ sequences)
two, combining marks zero — so 1ch-based layouts, wrapping, truncation
and borders stay aligned with non-Latin content. Caveat: emoji width
ultimately depends on the terminal’s font; svelterm follows the modern
two-cell convention.
<svt-ansi>
Raw ANSI passthrough for pre-styled output — git diff, ls --color,
build logs. Its text content renders with its own SGR styling (16/256/
truecolor, bold, underline, …); non-SGR escape sequences are stripped.
Content is treated as pre: newlines split lines, tabs expand to
8-column stops, nothing wraps.
<svt-ansi>{diffOutput}</svt-ansi> <svt-region>
A paint primitive whose cells come from your code — used to embed whole
terminal emulators (the playground’s embedded-terminal demo). It fills its
box like a replaced element, receives a cell-source callback, and fires resize with { cols, rows } when its allocated size changes.