nextcloud / deck

🗂 Kanban-style project & personal management tool for Nextcloud, similar to Trello
https://apps.nextcloud.com/apps/deck
GNU Affero General Public License v3.0
1.22k stars 276 forks source link

Foreground color based on background switches too late from black to white #3084

Closed verymilan closed 1 year ago

verymilan commented 3 years ago

Hi there, i couldn't find a matching issue about this, and since it is affecting multiple areas/apps of Nextcloud, i went with this repo.

In apps that use tags or other elements with selectable colors, i always find myself annoyed by the bad contrast due to late switching of the foreground color. For example when moving from Google Calendar to Nextcloud, the default blue and red colors would have white text, but in Nextcloud it is still black, which does not look nice. Also i just noticed it with default tag backgrounds in Nextcloud Deck:

What it looks like What it should look like
Bildschirmfoto vom 2021-05-24 12-51-42 Bildschirmfoto vom 2021-05-24 12-52-38

I would highly appreciate it if this would get improved, and i would enjoy using Nextcloud way more, because i wouldn't have to fight with colors that much in order to make myself comfi in the apps. :)

kesselb commented 3 years ago

cc @nextcloud/designers

stefan-niedermann commented 3 years ago

I think this is handled a bit better in the Deck Android app. The detection of the foreground color is based on the brightness and luminescence of the color.

The algorithm can be found here (kotlin, but should be portable) and is based on contrast-ratio.com (where it also can be tested directly online).

juliusknorr commented 3 years ago

Colored tags are not really standardized yet, so I'd move this over to deck to check again for our logic there maybe we'll need to adapt to the android mechanism then, thanks for the pointer @stefan-niedermann

doodhout commented 2 years ago

I found the following code in this repo:

CardItem.vue sets the label background colour and text colour using the labelStyle() method that is defined in labelStyle.js.

labelStyle() returns the style including background colour and text colour by accepting background colour as-is and calculating text colour by calling textColor() in color.js.

The method textColor() in color.js calculates the text colour like this:

        textColor(hex) {

            const rgb = this.hexToRgb(hex)
            if (rgb === null) {
                return '#000000'
            }
            const { l } = this.rgb2hls(rgb)

            if (l < 0.5) {
                return '#ffffff'
            } else {
                return '#000000'
            }
        },

In short it returns white if luminance (in the HSL colour space) is less than 0.5, or returns black if the luminance is equal to or greater than 0.5.

This is a fine approach, however HSL is a poor model to calculate human-perceived lightness of a colour, as explained here: Stop using HSL for color systems!

The gist of the article is that, given a certain fixed value for lightness in the HSL colour space, humans perceive dark blue, dark green and red colors as significantly darker than yellows, cyans and lighter greens, despite all of those colours having the same HSL-lightness value. Hence it looks wrong to have bright red paired with black text colour, because white would be perceptibly nicer.

PROPOSAL FOR FIX

Instead of using HSL's L-value to decide on text colour, instead use the "CIELAB" (also called "L*a*b*", Lab or Lch) colour space to calculate the text colour for labels. This can be easily done using the chroma.js library that is licensed under Apache 2.0 (i.e. very permissive).

Since I've worked out the analysis-part of this effort, I might implement this myself soon-ish if no-one picks this up, because I'm a hardcore user of Deck and this issue sometimes bothers me enough to trigger me to try and find out what's wrong and how to improve it.

juliusknorr commented 1 year ago

Thanks for the insight, this was very helpful. I pushed a fix for this in 2d47e0f