Why some GitHub labels illegible

Be aware
This represents the state of GitHub labels on 2023-03-11, which could have been fastened within the
meantime.
The issue
On GitHub, it’s potential to pick the colour of a label when assigning it to a pull request or a difficulty. When utilizing GitHub with the Gentle default theme, the colour of the textual content on these labels is dependent upon the brightness of the colour used: The darker the colour of the label the brighter the colour of the textual content of the label and vice versa. Right here’s how this appears like:
In actual fact for most colours, the textual content on the label will probably be both black or white. Digging into the
CSS that’s used for the Gentle theme, we discover the logic to determine on the colour of the textual content. Assuming the colour of the
label is in
--label-r
, --label-g
and --label-r
, we now have
--perceived-lightness: calc( ((var(--label-r) * 0.2126) + (var(--label-g) * 0.7152)) / 255 )
--lightness-threshold: 0.453
--lightness-switch: max(0, min(calc((var(--perceived-lightness) - var(--lightness-threshold)) * -1000), 1))
after which the colour for the font is set by
colour:hsl(0deg, 0%, calc(var(--lightness-switch) * 100%))
So if the sRGB worth of the colour is (r,g,b) within the vary [0, 255]^3, a perceived-lightness
is calculated by the linear equation
perceived_lightness = lambda r, g, b: (0.2126*r + 0.7152*g + 0.0722*b)/255
and primarily the textual content of the label will probably be coloured white if perceived-lightness<0.453
and black in any other case. Nonetheless, when the perceived-lightness
may be very near the brink, we don’t set off the min
or max
and truly get some form of gray colour for the label.
Taking for instance, taking the colour #3bb6b3 = (59, 182, 179)
, we get a perceived-lightness
of (0.2126*59 + 0.7152*182 + 0.0722*179)/255 = 0.6103...
, which is approach above the brink, therefore the textual content for this colour will probably be rendered in black.
For various shades of the identical colour we calculate as follows:
label colour | perceived-lightness |
lightness-switch |
textual content colour |
---|---|---|---|
2b8685 |
0.4493.. |
1 |
ffffff |
2b8786 |
0.4524.. |
0.5741.. |
929292 |
2c8786 |
0.4532.. |
0 |
000000 |
3bb6b3 |
0.6103.. |
0 |
000000 |
For #2b8786
, we multiply 0.5741...
by 255
to get 146
or 0x92
. This gray #929292
is already fairly onerous to learn, see beneath for worse circumstances of that habits.
Let’s have a look at the above formulation geometrically.
We begin with an sRGB colour dice, with could be visualized like that:
The worth the place perceived-lightness equals the lightness-threshold is a aircraft slicing by means of the dice:
The intersection between the dice and the aircraft is a parallelogram, and we are able to make an orthonormal projection to 2 dimensions to get a transparent have a look at it:
Since these are the colours is the brink the place GitHub’s algorithm switches from white to black for the textual content, each must be equally readable in opposition to that background. Nonetheless, at the very least to my eyes, the white textual content is extra readable:
Additionally word how the brightness isn’t uniform for these colours.
So what’s is happening? Nicely, CSS colours are understood to be within the sRGB colour area, however the method used is meant for linear RGB see sRGB – Wikipedia.
Due to this fact the right calculation in must be (in pseudo-code):
def linear_from_srgb_channel(c):
return c/12.92 if c <= 0.04044823627710819170430880 else ((c + 0.055)/1.055)**2.4
def linear_from_srgb(r, g, b):
return tuple(map(linear_from_srgb_channel, (r, g, b)))
improved_perceived_lightness = lambda r, g, b: perceived_lightness(*linear_from_srgb(r, g, b))
That exhibits that not doing the conversion to normally skews in the direction of utilizing black textual content an excessive amount of.
That is one drawback with the colour of the textual content of the labels. One other drawback the textual content for labels with a colour very near the brink will really be some shade of gray. I don’t know what the thought behind that’s, however it results in fairly unreadable labels, for example:
You possibly can check out these labels in a test repo. Within the Gentle default theme they’re essentially the most illegible label I may discover. In Darkish default theme they render wonderful, which could result in individuals really selecting these colour for his or her labels, even when there’s a preview within the person interface.
The answer…
… for the issue with gray textual content:
By no means make the textual content colour of labels gray, all the time determine for both white or black relying on that threshold, so change the code to one thing like
--lightness-switch: calc(var(--perceived-lightness) < var(--lightness-threshold) ? 1 : 0);
In all probability additionally verify first if --lightness-switch
is used elsewhere within the code.
… for the issue with utilizing the fallacious colour area:
A fast repair can be to vary the --lightness-threshold
to one thing just a little bigger.
Alternatively, when sticking with the --lightness-threshold
one must do the right calculations taking sRGB to linear RGB as described above.
I submitted a bug report back to GitHub; hopefully it will get fastened quickly!
Animations made with SwissGL with the assistance of Alexander Mordvintsev.