Skip to content

[Bug]: Muted TUI text uses SGR 2 (Ink dimColor), bypassing the theme and capped at half the terminal's minimum contrast ratio #169

Description

@johnsoez4

What happened?

Auggie renders muted TUI text using Ink's dimColor prop, which compiles to the SGR 2 ("faint") attribute. SGR 2 is a terminal attribute, not a color: the emulator blends the foreground toward the background. This has two consequences that make muted text unreadable on a dark background and impossible for a user to fix:

  1. It is not themeable. No palette entry addresses it, so neither the ansi theme plus terminal palette overrides, nor any default-* theme, can brighten it.
  2. In xterm.js-based terminals it is capped at half the configured minimum contrast ratio. xterm.js deliberately enforces half the ratio for dim text (xtermjs/xterm.js#4672, fixing #4262; IColorSet.halfContrastCache"Maps original colors to colors that respect half of the minimum contrast ratio"). VS Code inherits this (microsoft/vscode#190371).

Measured against the v0.36.0 bundle (augment.mjs):

Pattern Count Effect
dimColor total 181
{dimColor: true} with no color prop 117 Default foreground + SGR 2; bypasses the theme entirely
color: theme.text.muted, dimColor: true 27 Theme resolves text.muted correctly, then SGR 2 re-darkens it

The second row is the more surprising one: those sites take an already-correct, deliberately-chosen theme color and push it back toward the background in the same render call. Under the ansi theme, text.muted resolves via cc.gray"gray" → ANSI bright black, which a user can override — and dimColor then undoes that override.

A concrete example is the ask-user question prompt's context paragraph:

context && createElement(Text, {dimColor: true}, t.context)

No color prop at all, so it is pure default-foreground + SGR 2.

What did you expect to happen?

Muted text should resolve to the active theme's text.muted slot and nothing else, so that it is (a) controlled by the theme, and (b) subject to the terminal's full minimum contrast ratio rather than half of it.

The theme system already has the right slot — text.muted is defined in every theme. The issue is that dimColor is being used as the styling primitive for "muted" either instead of, or in addition to, that slot.

Steps to reproduce

  1. Use a dark terminal background (e.g. VS Code default #1e1e1e).
  2. Set "theme": "ansi" in ~/.augment/settings.json — the theme intended to honor the terminal palette.
  3. Override the terminal palette to brighten muted text, e.g. in VS Code settings.json:
    "workbench.colorCustomizations": { "terminal.ansiBrightBlack": "#b6c2ce" }
  4. Start auggie and trigger any prompt that renders context text (e.g. a tool asking a clarifying question), or observe help/hint text.
  5. Observe: text rendered via text.muted alone brightens as expected. Text rendered with dimColor does not.

Isolating the mechanism directly in the same terminal:

printf '%b\n' "normal \033[2mDIM (SGR 2)\033[0m | \033[90mANSI 90\033[0m"

ANSI 90 responds to the palette override; DIM does not.

Auggie version

0.36.0 (commit 7c61e5bb)

Request ID

N/A — this is a client-side rendering/theming defect, not a request-scoped failure. No request is involved and /request-id does not apply. (Flagging this because the field is marked required by the template; happy to supply one if there is something specific you would like captured.)

Environment details

Environment
  • OS: Android 17 (Google Pixel 10 Pro) — client; GitHub Codespaces (Linux, Ubuntu devcontainer) — host
  • Shell: bash
  • Tool/CLI version: auggie 0.36.0 (commit 7c61e5bb), Node v22.22.2
  • Terminal: VS Code for the Web (xterm.js), reached via DuckDuckGo for Android (Chromium-based) → github.com/codespaces
  • Theme: ansi, terminal background #1e1e1e

Anything else we need to know?

Why the available workaround is not sufficient.

Raising terminal.integrated.minimumContrastRatio is the only lever a user has, and the half-ratio rule caps what it can achieve:

Setting Resulting dim floor Effect on normal text (bg #1e1e1e, max achievable 16.67:1)
7 (raised from default 4.5) 3.5:1 No help — below where dim text already sat
14 7.0:1 Works, but washes all other colors toward white
18+ 9.0:1+ Clamp target exceeds what the background allows; every foreground collapses to pure #ffffff and color is lost entirely

So the only setting that makes dim text readable does so by destroying the color fidelity of everything else. There is no value that fixes muted text and preserves the theme.

This is worse outside xterm.js. On a phone the practical alternative to the browser is gh codespace ssh from a terminal app, which has no minimum-contrast-ratio feature at all. On that path there is no workaround whatsoever — SGR 2 renders however the app blends it.

Accessibility framing. 7:1 is the ceiling this approach can reach for dim text, and that is only attainable by sacrificing color. Muted text that cannot reach WCAG AA (4.5:1) independently of a global contrast override is a genuine accessibility gap, and it is most acute on a small mobile screen in variable lighting.

Suggested fix, in rough order of value:

  1. Replace the 117 bare {dimColor: true} sites with color={theme.text.muted}. Directly themeable, and gets the full contrast ratio rather than half.
  2. Drop dimColor from the 27 color: text.muted, dimColor: true sites — the theme slot is already doing the work, and dimColor is actively fighting it. This subset alone is a small, low-risk change.
  3. Optionally darken text.muted slightly per theme to preserve the intended visual hierarchy, now that it is a real color rather than a blend.
  4. Optionally expose a setting (e.g. "dimText": false) for users whose terminals render SGR 2 harshly.

Happy to test a prerelease on this setup if useful.


Investigated and drafted by an Augment agent session, reviewed and submitted on my behalf. The bundle measurements above were taken from the installed augment.mjs for v0.36.0; the xterm.js behavior was confirmed against upstream source, not inferred.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions