Skip to content

Fix SGR 2 (dim/faint) rendering on light backgrounds#478

Open
evertjr wants to merge 1 commit intomigueldeicaza:mainfrom
evertjr:fix/sgr2-dim-light-themes
Open

Fix SGR 2 (dim/faint) rendering on light backgrounds#478
evertjr wants to merge 1 commit intomigueldeicaza:mainfrom
evertjr:fix/sgr2-dim-light-themes

Conversation

@evertjr
Copy link
Contributor

@evertjr evertjr commented Feb 26, 2026

Problem

dimmedColor() reduces each RGB component by 50 %, effectively blending toward black. On dark backgrounds this looks correct, but on light backgrounds it makes dim text darker.

A secondary issue: an alpha-based workaround (withAlphaComponent(0.5)) produces the right apparent color but leaves visible seams between adjacent box-drawing characters (e.g. ─────) because each cell composites independently.
image

This problem is visible on Claude Code in light theme:

SwiftTerm:
image

macOS Terminal:
image

Fix

Replace the no-arg dimmedColor() with dimmedColor(towards:) that takes the cell's background color and produces a fully-opaque 50 % blend between foreground and background. This:

  • Reduces contrast correctly regardless of whether the theme is light or dark
  • Eliminates tiling artifacts on box-drawing character runs
  • Applies to both macOS (NSColor) and iOS (UIColor)

The call site in AppleTerminalView already computes the background color for each cell, so passing it to the new method is straightforward.

Here is how it looks after the fix:
image

@evertjr
Copy link
Contributor Author

evertjr commented Feb 26, 2026

I'll be testing for a while to see if this introduces any problems during use, when I'm confident I'll mark as ready for review.

@migueldeicaza
Copy link
Owner

Perhaps we should just use alpha transparency in that case, rather than going to black. That should give us the same effect, this is what Ghostty does.

@evertjr evertjr force-pushed the fix/sgr2-dim-light-themes branch from 819d08e to 86bdaba Compare February 27, 2026 05:07
@evertjr
Copy link
Contributor Author

evertjr commented Feb 27, 2026

Perhaps we should just use alpha transparency in that case, rather than going to black. That should give us the same effect, this is what Ghostty does.

I updated the approach based on your feedback and dimmedColor() now uses alpha transparency, same as Ghostty.

One thing I ran into is that visible seams between adjacent box-drawing characters (e.g. ─────) like I mentioned in the PR. To handle that, I added opaqueComposite(over:) which pre-composites the color against nativeBackgroundColor before drawing in drawBoxDrawings and drawBlockElements. Regular text still uses natural alpha blending.

image

Tested on both light and dark themes, looks correct on both. Ready for review.

@evertjr evertjr marked this pull request as ready for review February 27, 2026 05:15
@evertjr evertjr force-pushed the fix/sgr2-dim-light-themes branch from 86bdaba to 5e6a2c9 Compare February 27, 2026 05:19
@migueldeicaza
Copy link
Owner

Thank you, this makes sense. U had initially missed the bit about the character drawing challenge, in that case, perhaps your color mixing is a better approach. What do you think?

dimmedColor() blended the foreground toward black (RGB * 0.5), which
made dim text darker on light backgrounds — the opposite of "faint".

Replace with dimmedColor(towards:) that takes the cell background color
and produces a fully-opaque 50 % blend between foreground and background.
This reduces contrast correctly regardless of whether the theme is light
or dark, and avoids tiling artifacts on box-drawing character runs that
semi-transparent fills would cause.
@evertjr
Copy link
Contributor Author

evertjr commented Feb 27, 2026

Thank you, this makes sense. U had initially missed the bit about the character drawing challenge, in that case, perhaps your color mixing is a better approach. What do you think?

I think you're right, if we need to pre-composite for tiled characters anyway, doing it at the source for all dim text is simpler. I'll revert to the background-blend approach in dimmedColor() and drop the separate opaqueComposite method

@evertjr evertjr force-pushed the fix/sgr2-dim-light-themes branch from 5e6a2c9 to 058be6f Compare February 27, 2026 17:50
@evertjr
Copy link
Contributor Author

evertjr commented Mar 8, 2026

@migueldeicaza did you had time to take look on this approach?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants