终端应生成256色调色板。
Terminals should generate the 256-color palette

原始链接: https://gist.github.com/jake-stewart/0a8ea46159a7da2c808e5be2177e1783

当前终端通常在颜色深度方面存在问题。虽然16色base16主题简单,但限制较多。真彩色(1600万色)功能强大,但需要为每个程序单独配置,且缺乏广泛的终端支持。256色调色板提供了一个中间方案,但其默认实现存在可读性差的问题,并且由于不一致的颜色插值和饱和度,与现有的base16主题冲突。 解决方案是终端*自动生成*基于用户现有base16主题的256色调色板。这样既利用了base16主题的简洁性,又解锁了更广泛的颜色范围。生成的调色板使用LAB色彩空间的线性插值,以确保一致的亮度和可读性。 这种方法将鼓励开发者使用256色调色板,在表现力、易用性和兼容性之间取得平衡——避免了真色彩的配置开销和base16的限制。提供的代码演示了一个公有领域的实现,用于生成这种改进的调色板。

一个 Hacker News 的讨论强调了一项提案,即终端自动生成 256 色调色板,以改善为使用更丰富的调色板设计的程序的颜色显示。这个想法由“tosh”分享,旨在解决标准 16 色终端体验的局限性,尤其是在视觉上复杂的应用程序中。 评论者认为这是一个合乎逻辑的改进,有人建议将其扩展到 24 位颜色。然而,他们也强调了可选性的必要性。用户应该能够禁用此功能,以避免与现有配色方案(如 Solarized)发生冲突,这些方案可能会因两次颜色转换的应用而意外更改。 这场讨论强调了对终端中更好颜色支持的渴望,同时也尊重用户自定义设置。
相关文章

原文

Terminals should generate the 256-color palette from the user's base16 theme.

If you've spent much time in the terminal, you've probably set a custom base16 theme. They work well. You define a handful of colors in one place and all your programs use them.

The drawback is that 16 colors is limiting. Complex and color-heavy programs struggle with such a small palette.

The mainstream solution is to use truecolor and gain access to 16 million colors. But there are drawbacks:

  • Each truecolor program needs its own theme configuration.
  • Changing your color scheme means editing multiple config files.
  • Light/dark switching requires explicit support from program maintainers.
  • Truecolor escape codes are longer and slower to parse.
  • Fewer terminals support truecolor.

The 256-color palette sits in the middle with more range than base16 and less overhead than truecolor. But it has its own issues:

  • The default theme clashes with most base16 themes.
  • The default theme has poor readability and inconsistent contrast.
  • Nobody wants to manually define 240 additional colors.

The solution is to generate the extended palette from your existing base16 colors. You keep the simplicity of theming in one place while gaining access to many more colors.

If terminals did this automatically then terminal program maintainers would consider the 256-color palette a viable choice, allowing them to use a more expressive color range without requiring added complexity or configuration files.

Understanding the 256-Color Palette

The 256-color palette has a specific layout. If you are already familiar with it, you can skip to the next section.

The first 16 colors form the base16 palette. It contains black, white, and all primary and secondary colors, each with normal and bright variants.

  1. black
  2. red
  3. green
  4. yellow
  5. blue
  6. magenta
  7. cyan
  8. white
  9. bright black
  10. bright red
  11. bright green
  12. bright yellow
  13. bright blue
  14. bright magenta
  15. bright cyan
  16. bright white

The next 216 colors form a 6x6x6 color cube. It works like 24-bit RGB but with 6 shades per channel instead of 256.

You can calculate a specific index using this formula, where R, G, and B range from 0 to 5:

16 + (36 * R) + (6 * G) + B

The final 24 colors form a grayscale ramp between black and white. Pure black and white themselves are excluded since they can be found in the color cube at (0, 0, 0) and (5, 5, 5).

You can calculate specific index using this formula, where S is the shade ranging from 0 to 23:

232 + S

Problems with the 256-Color Palette

The most obvious problem with the 256-color palette is the inconsistency with the user's base16 theme:

inconsistent theme

Using a custom 256-color palette gives a more pleasing result:

consistent theme

The default 216-color cube interpolates between black and each color incorrectly. It is shifted towards lighter shades (37% intensity for the first non-black shade as opposed to the expected 20%), causing readability issues when attempting to use dark shades as background:

poor readability example

If the color cube is instead interpolated correctly, readability is preserved:

fixed readability example

The default 256-color palette uses fully saturated colors, leading to inconsistent brightness against the black background. Notice that blue always appears darker than green, despite having the same shade:

poor readability example

If a less saturated blue is used instead then the consistent brightness is preserved:

fixed readability example

These problems can be solved by generating the 256-color palette from the user's base16 colors.

The base16 palette has 8 normal colors which map to the 8 corners of the 216-color cube. The terminal foreground and background should be used instead of the base16 black and white.

These colors can be used to construct the 216-color cube via trilinear interpolation, and the grayscale ramp with a simple background to foreground interpolation.

The LAB colorspace should be used to achieve consistent apparent brightness across hues of the same shade.

Solarized with RGB interpolation:

without lab

Solarized with LAB interpolation:

with lab

Combined image of many generated themes:

example generated themes

Before and after using 256 palette generation with default colors:

example before and after

This code is public domain, intended to be modified and used anywhere without friction.

def lerp_lab(t, lab1, lab2):
    return (
        lab1[0] + t * (lab2[0] - lab1[0]),
        lab1[1] + t * (lab2[1] - lab1[1]),
        lab1[2] + t * (lab2[2] - lab1[2]),
    )

def generate_256_palette(base16, bg=None, fg=None):
    base8_lab = [rgb_to_lab(c) for c in base16[:8]]
    bg_lab = rgb_to_lab(bg) if bg else base8_lab[0]
    fg_lab = rgb_to_lab(fg) if fg else base8_lab[7]

    palette = [*base16]

    for r in range(6):
        c0 = lerp_lab(r / 5, bg_lab, base8_lab[1])
        c1 = lerp_lab(r / 5, base8_lab[2], base8_lab[3])
        c2 = lerp_lab(r / 5, base8_lab[4], base8_lab[5])
        c3 = lerp_lab(r / 5, base8_lab[6], fg_lab)
        for g in range(6):
            c4 = lerp_lab(g / 5, c0, c1)
            c5 = lerp_lab(g / 5, c2, c3)
            for b in range(6):
                c6 = lerp_lab(b / 5, c4, c5)
                palette.append(lab_to_rgb(c6))

    for i in range(24):
        t = (i + 1) / 25
        lab = lerp_lab(t, bg_lab, fg_lab)
        palette.append(lab_to_rgb(lab))

    return palette

The default 256-color palette has room for improvement. Considering its poor readability and its clash with the user's theme, program authors avoid it, opting for the less expressive base16 or more complex truecolor.

Terminals should generate the 256-color palette from the user's base16 theme. This would make the palette a viable option especially considering its advantages over truecolor:

  • Access to a wide color palette without needing config files.
  • Light/dark switching capability without developer effort.
  • Broader terminal support without compatibility issues.
联系我们 contact @ memedata.com