Skip to content

Customize

CSS variables

Load tty.css first, then override its effective custom properties. The tables below reflect the variables actually consumed by the current stylesheet.

Scope and themes

Palette parameters live on :root and a root override applies across modes. For direct semantic color overrides, match each active selector you support: :root for light, :root[data-theme="dark"] for explicit dark, and :root[data-theme="auto"] inside prefers-color-scheme: dark for automatic dark. tty.css does not currently implement local, subtree themes.

:root {
  --tty-accent: #ffd166;
  --tty-accent-hover: #ffc233;
  --tty-accent-fg: #2b1800;
}

:root[data-theme="dark"] {
  --tty-accent: #ffe08a;
  --tty-accent-hover: #ffeaad;
  --tty-accent-fg: #241400;
}

@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
    --tty-accent: #ffe08a;
    --tty-accent-hover: #ffeaad;
    --tty-accent-fg: #241400;
  }
}

Recheck text, focus, control, selection, and status contrast in every theme after changing colors. See theme selection and accessibility responsibilities.

Create a color palette

Use the palette playground for generated colors, or Monokai for full overrides. The complete token tables are below.

Color tokens

tty.css requires OKLCH. The 19 generated semantic roles use the parameter-derived formulas below, with no support guard or hex fallback. Direct semantic overrides may use any CSS color format supported by your target browsers. Status tokens remain independent mode-specific hex colors:

TokenLight defaultDark and auto-dark defaultUsed for
--tty-success#15803d#4ade80Valid states and optimum meters
--tty-warning#b45309#fbbf24Suboptimum meters
--tty-danger#c83232#fb7185Invalid states, deletion, and poor meters

Public palette parameters

These defaults apply across all modes. Override them on :root after tty.css; no support guard is needed. Direct semantic token overrides remain supported and can be mixed with parameters.

ParameterDefault (all modes)Role mapping
--tty-accent-hue65Hue of accents, links, general focus, and selection
--tty-accent-chroma0.125Chroma of those accent roles, with role-specific factors
--tty-neutral-hue260Hue of canvas, surfaces, text, borders, code, control focus, and shadows
--tty-neutral-chroma0.025Chroma of those neutral roles, with role/mode-specific factors

Status colors and SVG icon images are independent of these parameters. Arbitrary hue/chroma choices are not guaranteed accessible; test each mode and state. See the Colors guide for parameter-only, direct, and mixed palettes.

Effective OKLCH role formulas

Every row evaluates to oklch(L C H). Here A means var(--tty-accent-chroma), N means var(--tty-neutral-chroma), and hue is the corresponding public hue parameter. Multiplication denotes calc(). These describe the implementation, not additional public knobs.

Light (including an omitted theme attribute) uses the light column. Explicit dark uses the dark column. Auto uses light unless prefers-color-scheme: dark matches, when it uses dark. Modes select internal --_tty-* lightness values and neutral chroma factors; those internal values are not public API.

Semantic tokenL light / darkC light / darkH parameter
--tty-bg98.5% / 18%N × 0.2 / N × 1.2neutral hue
--tty-surface100% / 23%N × 0 / N × 1.2neutral hue
--tty-surface-muted96.5% / 27%N × 0.4 / N × 1.6neutral hue
--tty-fg21% / 98%N × 1.6 / N × 0.2neutral hue
--tty-fg-muted48% / 78%N / Nneutral hue
--tty-border85% / 43%N / Nneutral hue
--tty-border-strong21% / 98%N × 1.6 / N × 0.2neutral hue
--tty-accent83% / 83%A / Aaccent hue
--tty-accent-hover78% / 88%A / Aaccent hue
--tty-accent-fg27% / 23%A × 0.32 / A × 0.32accent hue
--tty-link47% / 85%A / Aaccent hue
--tty-link-hover40% / 80%A / Aaccent hue
--tty-selection90% / 40%A × 0.6 / A × 0.6accent hue
--tty-selection-fg35% / 97%A × 0.32 / A × 0.32accent hue
--tty-code-bg21% / 13%N × 1.6 / N × 1.6neutral hue
--tty-code-fg98% / 98%N × 0.2 / N × 0.2neutral hue
--tty-focus56% / 85%A / Aaccent hue
--tty-control-focus37% / 85%N / Nneutral hue
--tty-control-shadow55% / 43%N / Nneutral hue

--tty-success, --tty-warning, and --tty-danger have no derived formula: use their mode-specific hex defaults in the color table above.

Typography and font policy

TokenDefaultUsed for
--tty-font"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospaceBody, controls, code, and component headings
--tty-font-displayvar(--tty-font)Page headings and reading-layout headings
--tty-reading-width72chMaximum width of paragraphs and other reading text

tty.css names JetBrains Mono first but does not load fonts. It uses JetBrains Mono only when already installed and otherwise follows the system monospace fallback stack.

Override both font tokens when body and display text should differ. Supply complete fallback stacks, verify all required weights, and test control sizing, code, wrapping, and layout in fallback fonts. A ch-based reading width changes with the selected font.

:root {
  --tty-font: "IBM Plex Mono", ui-monospace, monospace;
  --tty-font-display: "IBM Plex Sans", system-ui, sans-serif;
  --tty-reading-width: 68ch;
}

Layout and shape tokens

TokenDefaultUsed for
--tty-container72remBody landmark and .container maximum width
--tty-grid-min17remMinimum auto-fitting .grid column width, capped by available width
--tty-radius0.2remGeneral controls, code, menus, and disclosures; joined search controls retain pill-shaped ends
--tty-frame-radius0Article, fieldset, dialog, and exposed tab-frame corners, independent of controls. Joined tab edges stay square.
--tty-border-width1pxQuiet borders, dividers, and tooltip borders
--tty-frame-width2pxStrong component frames and custom controls
--tty-shadow-x0.5remHorizontal decorative frame shadow
--tty-shadow-y0.5remVertical decorative frame shadow
--tty-shadow-colorvar(--tty-border-strong)Frame-shadow color, independent of mask shapes
--tty-shadow-opacity0.6; 1 for .strong-shadowFrame-shadow opacity
--tty-shadow-patternEmbedded 4×4 SVG: Standard; inverse Dense for .strong-shadowRepeating SVG mask with two staggered rectangular marks
--tty-shadow-pattern-size4px 4pxMask tile size, not offset; use 3px 3px for Fine
--tty-progress-patternEmbedded 4×4 Dense SVG, matching Dense frame shadowsOptional repeating fill mask for progress.textured[value]
--tty-progress-pattern-size4px 4pxTextured progress mask tile size
--tty-control-shadow-size0.125remButton and form-control shadow offset
--tty-control-height2.5remMinimum height of buttons and single-line controls

Changing a dimension can affect many components at once. Keep border widths non-negative, preserve usable target sizes, and check narrow layouts before shipping.

Set --tty-grid-min on an individual grid for a local override; see the live grid example.

<div class="grid" style="--tty-grid-min: 14rem">
  <article><h3>Primary</h3><p>Healthy</p></article>
  <article><h3>Replica</h3><p>Healthy</p></article>
</div>
:root {
  --tty-container: 80rem;
  --tty-radius: 0;
  --tty-frame-width: 2px;
  --tty-shadow-x: 0.35rem;
  --tty-shadow-y: 0.35rem;
  --tty-control-shadow-size: 0.1rem;
  --tty-control-height: 2.75rem;
}

For a component-scoped adjustment, override --tty-frame-width, --tty-shadow-x, and --tty-shadow-y on an application class instead of :root. These also work with the optional frame treatments; double borders use 3× the frame width, while strong shadows keep the configured offsets.

The four mask-shadow tokens are optional overrides, not required :root declarations. They are shared by articles, fieldsets, tabs, table wrappers, and dialogs. Their defaults resolve on each shadow pseudo-element so local border colors are respected, and explicit overrides also win on .strong-shadow. SVG masks supply shapes only; CSS supplies color. For a custom --tty-shadow-pattern: url(...), the SVG alpha defines transparency. See texture examples.

The two progress pattern tokens are separate optional overrides, not aliases of the shadow tokens. Their defaults resolve where consumed, with no :root aliases. Set them on progress.textured to customize its mask; SVG alpha supplies the shape while CSS color supplies the currentColor fill. The default Dense mask matches frame shadows but is configured independently. See the native progress example.

On .overflow-auto table wrappers, frame treatments style the table frame and its shadow within the wrapper padding. Dropdown and tooltip overlays are shadowless and do not support frame treatments; dropdown lists need no wrapper. Small solid control shadows, radio states, and validation treatments remain intentional.

--tty-space is not defined or consumed by the current stylesheet. It is not part of the effective customization API, and overriding it has no effect; use ordinary application spacing rules instead.

Icon tokens

TokenDefaultUsed for
--tty-icon-searchEmbedded 16×16 SVG data URLSearch inputs
--tty-icon-dateEmbedded 16×16 SVG data URLDate, month, and week inputs
--tty-icon-timeEmbedded 16×16 SVG data URLTime inputs
--tty-icon-datetimeEmbedded 16×16 SVG data URLLocal date-time inputs

These icons are decorative CSS backgrounds; control labels provide meaning. Their SVG strokes remain fixed at #526174 in light and #aab7c8 in dark/auto-dark; changing palette parameters or --tty-fg-muted does not recolor the images. Override them with a CSS url(), including a percent-encoded data URL if embedding SVG. Match the 16×16 view box and test light, dark, and disabled states. Firefox and forced-colors mode intentionally use native picker presentation for date and time controls.

:root {
  --tty-icon-search: url("/assets/search.svg");
  --tty-icon-date: url("/assets/calendar.svg");
  --tty-icon-time: url("/assets/clock.svg");
  --tty-icon-datetime: url("/assets/date-time.svg");
}

Low-specificity overrides

Most framework selectors use :where(), so an application class normally wins without !important. Load overrides after tty.css and target the smallest stable application hook. Some structural selectors and composition classes have normal specificity, so inspect the actual rule when an override does not win.

.billing-card {
  border-style: dashed;
}

button.danger {
  background: var(--tty-danger);
  color: var(--tty-surface);
}

.settings-form input {
  border-radius: 0;
}

Avoid overriding generated internals such as browser-specific range and meter pseudo-elements unless your product owns the resulting cross-browser test matrix.