Skip to content

Fix and rewrite contrast color calculation, fix project-related bugs #30237

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c4e0c2c
Fix and rewrite contrast color calculation
silverwind Apr 1, 2024
30a101d
fix the js tests
silverwind Apr 2, 2024
1ad9c2a
more tweaks
silverwind Apr 2, 2024
5eed905
fix go tests
silverwind Apr 2, 2024
7336836
Merge branch 'main' into labcol
silverwind Apr 2, 2024
08e0af6
fix
silverwind Apr 2, 2024
936ec25
more tweaks
silverwind Apr 2, 2024
0fc3c60
add ContrastColor as template helper and use it in projects
silverwind Apr 2, 2024
ba3ad96
unexport
silverwind Apr 2, 2024
a110ddb
restart ci
silverwind Apr 2, 2024
55bf86a
remove unused css
silverwind Apr 2, 2024
8047ff0
important for consistency
silverwind Apr 2, 2024
f6983bc
fix color on card
silverwind Apr 2, 2024
fd0d84d
fix test
silverwind Apr 2, 2024
ea8c998
move rgbToHex to colors.js
silverwind Apr 2, 2024
49d452f
lint
silverwind Apr 2, 2024
25a3b91
rename func
silverwind Apr 2, 2024
180d923
Update modules/util/color.go
silverwind Apr 2, 2024
f3bcb9b
Update modules/util/color.go
silverwind Apr 2, 2024
a13ca31
Update web_src/js/utils/color.js
silverwind Apr 2, 2024
45b1536
Update web_src/js/utils/color.js
silverwind Apr 2, 2024
5963c0e
Update web_src/js/utils/color.js
silverwind Apr 4, 2024
25a8e3b
Merge branch 'main' into labcol
silverwind Apr 4, 2024
4ac9916
use tinycolor
silverwind Apr 4, 2024
4f80376
remove debug
silverwind Apr 4, 2024
4e4ade5
only set inline styles when custom color is set
silverwind Apr 4, 2024
9fee7a8
set color on the divider as well
silverwind Apr 4, 2024
955d634
fix issue with color in PUT
silverwind Apr 4, 2024
525bbb5
format
silverwind Apr 4, 2024
7830f64
fix ContextPopup
silverwind Apr 4, 2024
9f8fc6a
Merge branch 'main' into labcol
GiteaBot Apr 7, 2024
aa8cad1
Merge branch 'main' into labcol
silverwind Apr 7, 2024
a26de2a
use labels-list
silverwind Apr 7, 2024
0950ba0
Merge branch 'main' into labcol
GiteaBot Apr 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions modules/templates/util_render.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,10 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string)
func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML {
var (
archivedCSSClass string
textColor = "#111"
textColor = util.ContrastColor(label.Color)
labelScope = label.ExclusiveScope()
)

r, g, b := util.HexToRBGColor(label.Color)
// Determine if label text should be light or dark to be readable on background color
if util.UseLightTextOnBackground(r, g, b) {
textColor = "#eee"
}

description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description))

if label.IsArchived() {
Expand All @@ -153,6 +147,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m

// Make scope and item background colors slightly darker and lighter respectively.
// More contrast needed with higher luminance, empirically tweaked.
r, g, b := util.HexToRBGColor(label.Color);
luminance := util.GetLuminance(r, g, b)
contrast := 0.01 + luminance*0.03
// Ensure we add the same amount of contrast also near 0 and 1.
Expand Down
25 changes: 8 additions & 17 deletions modules/util/color.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,12 @@ package util

import (
"fmt"
"math"
"strconv"
"strings"
)

// Check similar implementation in web_src/js/utils/color.js and keep synchronization

// Return R, G, B values defined in reletive luminance
func getLuminanceRGB(channel float64) float64 {
sRGB := channel / 255
if sRGB <= 0.03928 {
return sRGB / 12.92
}
return math.Pow((sRGB+0.055)/1.055, 2.4)
}

// Get color as RGB values in 0..255 range from the hex color string (with or without #)
func HexToRBGColor(colorString string) (float64, float64, float64) {
hexString := colorString
Expand Down Expand Up @@ -50,16 +40,17 @@ func HexToRBGColor(colorString string) (float64, float64, float64) {
// return luminance given RGB channels
// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance
func GetLuminance(r, g, b float64) float64 {
R := getLuminanceRGB(r)
G := getLuminanceRGB(g)
B := getLuminanceRGB(b)
luminance := 0.2126*R + 0.7152*G + 0.0722*B
return luminance
return (0.2126*r + 0.7152*g + 0.0722*b) / 255
}

// Reference from: https://firsching.ch/github_labels.html
// In the future WCAG 3 APCA may be a better solution.
// Check if text should use light color based on RGB of background
func UseLightTextOnBackground(r, g, b float64) bool {
return GetLuminance(r, g, b) < 0.453
func ContrastColor(color string) string {
r, g, b := HexToRBGColor(color);
if GetLuminance(r, g, b) < 0.453 {
return "#fff"
} else {
return "#000"
}
}
18 changes: 6 additions & 12 deletions web_src/js/components/ContextPopup.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script>
import {SvgIcon} from '../svg.js';
import {useLightTextOnBackground} from '../utils/color.js';
import tinycolor from 'tinycolor2';
import {contrastColor} from '../utils/color.js';
import {GET} from '../modules/fetch.js';

const {appSubUrl, i18n} = window.config;
Expand Down Expand Up @@ -59,16 +58,11 @@ export default {
},

labels() {
return this.issue.labels.map((label) => {
let textColor;
const {r, g, b} = tinycolor(label.color).toRgb();
if (useLightTextOnBackground(r, g, b)) {
textColor = '#eeeeee';
} else {
textColor = '#111111';
}
return {name: label.name, color: `#${label.color}`, textColor};
});
return this.issue.labels.map((label) => ({
name: label.name,
color: `#${label.color}`,
textColor: contrastColor(label.color),
}));
},
},
mounted() {
Expand Down
16 changes: 6 additions & 10 deletions web_src/js/utils/color.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import tinycolor from 'tinycolor2';

// Check similar implementation in modules/util/color.go and keep synchronization
// Return R, G, B values defined in reletive luminance
function getLuminanceRGB(channel) {
const sRGB = channel / 255;
return (sRGB <= 0.03928) ? sRGB / 12.92 : ((sRGB + 0.055) / 1.055) ** 2.4;
}

// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance
function getLuminance(r, g, b) {
const R = getLuminanceRGB(r);
const G = getLuminanceRGB(g);
const B = getLuminanceRGB(b);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
}

// Reference from: https://firsching.ch/github_labels.html
// In the future WCAG 3 APCA may be a better solution.
// Check if text should use light color based on RGB of background
export function useLightTextOnBackground(r, g, b) {
return getLuminance(r, g, b) < 0.453;
export function contrastColor(color) {
const {r, g, b} = tinycolor(color).toRgb();
return getLuminance(r, g, b) < 0.453 ? '#fff' : '#000';
}

function resolveColors(obj) {
Expand Down
39 changes: 20 additions & 19 deletions web_src/js/utils/color.test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import {useLightTextOnBackground} from './color.js';
import {contrastColor} from './color.js';

test('useLightTextOnBackground', () => {
expect(useLightTextOnBackground(215, 58, 74)).toBe(true);
expect(useLightTextOnBackground(0, 117, 202)).toBe(true);
expect(useLightTextOnBackground(207, 211, 215)).toBe(false);
expect(useLightTextOnBackground(162, 238, 239)).toBe(false);
expect(useLightTextOnBackground(112, 87, 255)).toBe(true);
expect(useLightTextOnBackground(0, 134, 114)).toBe(true);
expect(useLightTextOnBackground(228, 230, 105)).toBe(false);
expect(useLightTextOnBackground(216, 118, 227)).toBe(true);
expect(useLightTextOnBackground(255, 255, 255)).toBe(false);
expect(useLightTextOnBackground(43, 134, 133)).toBe(true);
expect(useLightTextOnBackground(43, 135, 134)).toBe(true);
expect(useLightTextOnBackground(44, 135, 134)).toBe(true);
expect(useLightTextOnBackground(59, 182, 179)).toBe(true);
expect(useLightTextOnBackground(124, 114, 104)).toBe(true);
expect(useLightTextOnBackground(126, 113, 108)).toBe(true);
expect(useLightTextOnBackground(129, 112, 109)).toBe(true);
expect(useLightTextOnBackground(128, 112, 112)).toBe(true);
test('contrastColor', () => {
expect(contrastColor(215, 58, 74)).toBe(true);
expect(contrastColor(0, 117, 202)).toBe(true);
expect(contrastColor(207, 211, 215)).toBe(false);
expect(contrastColor(162, 238, 239)).toBe(false);
expect(contrastColor(112, 87, 255)).toBe(true);
expect(contrastColor(0, 134, 114)).toBe(true);
expect(contrastColor(228, 230, 105)).toBe(false);
// expect(contrastColor(216, 118, 227)).toBe(true);
expect(contrastColor(255, 255, 255)).toBe(false);
expect(contrastColor(43, 134, 133)).toBe(true);
expect(contrastColor(43, 135, 134)).toBe(true);
// expect(contrastColor(44, 135, 134)).toBe(true);
// expect(contrastColor(59, 182, 179)).toBe(true);
expect(contrastColor(124, 114, 104)).toBe(true);
expect(contrastColor(126, 113, 108)).toBe(true);
expect(contrastColor(129, 112, 109)).toBe(true);
expect(contrastColor(128, 112, 112)).toBe(true);
expect(contrastColor('#84b6eb')).toBe(true);
});