Skip to content

Commit 8f3be78

Browse files
Make chart legend labels gray
Make chart legend labels gray and consistent with page background contrast to be easier to read in both light and dark modes
1 parent 5d34658 commit 8f3be78

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

‎lib/report-view.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,11 +396,20 @@ export function initBubbleChart(canvas, title, chartData) {
396396
padding: 15,
397397
boxWidth: 20,
398398
boxHeight: 20,
399+
// Chart.js renders each legend item's text from
400+
// item.fontColor (plugin.legend.js); labels.color only
401+
// feeds the DEFAULT generateLabels, which this chart
402+
// overrides below. The canvas is transparent, so the
403+
// text sits on the page background (#ffffff light /
404+
// #10161d dark): one gray for both, the balanced
405+
// midpoint (~4.3:1 on each; no single gray can hit
406+
// 4.5:1 on both).
399407
generateLabels: () =>
400408
['High Priority (1)', 'Medium Priority', 'Low Priority (max)'].map((label, i) => ({
401409
text: label,
402410
fillStyle: ['rgb(235, 64, 52)', 'rgb(137, 119, 74)', 'rgb(39, 174, 96)'][i],
403411
strokeStyle: ['rgb(235, 64, 52)', 'rgb(137, 119, 74)', 'rgb(39, 174, 96)'][i],
412+
fontColor: '#7a7a7a',
404413
lineWidth: 1,
405414
hidden: false,
406415
index: i

‎tests/unit/report-view.test.js‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,40 @@ describe('initBubbleChart', () => {
6262
expect(config.options.scales.x.title.text).toBe('Effort to refactor');
6363
expect(config.options.scales.y.title.text).toBe('Relative churn (impact)');
6464
});
65+
66+
it('renders legend labels in a gray readable in light and dark mode', () => {
67+
const created = [];
68+
window.Chart = function (ctx, config) { created.push(config); };
69+
const canvas = document.getElementById('chart_GOD');
70+
initBubbleChart(canvas, 'God Classes', { bubbles: [] });
71+
const items = created[0].options.plugins.legend.labels.generateLabels();
72+
expect(items.length).toBeGreaterThan(0);
73+
74+
// One gray for both palettes: the chart canvas is transparent, so legend
75+
// text sits on --bg-color (#ffffff light / #10161d dark). No single gray
76+
// can pass 4.5:1 on both, so the guard pins the balanced midpoint.
77+
// Chart.js draws each legend item's text from item.fontColor — with a
78+
// custom generateLabels, options.plugins.legend.labels.color is ignored.
79+
const legendColor = items[0].fontColor;
80+
items.forEach(item => expect(item.fontColor).toBe(legendColor));
81+
const channels = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(legendColor || '');
82+
expect(channels).not.toBeNull();
83+
const [r, g, b] = [1, 2, 3].map(i => parseInt(channels[i], 16));
84+
expect(r).toBe(g);
85+
expect(g).toBe(b); // neutral gray, no palette bias
86+
87+
const luminance = (rs, gs, bs) => {
88+
const lin = c => {
89+
const s = c / 255;
90+
return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
91+
};
92+
return 0.2126 * lin(rs) + 0.7152 * lin(gs) + 0.0722 * lin(bs);
93+
};
94+
const gray = luminance(r, g, b);
95+
const contrast = lum => (Math.max(lum, gray) + 0.05) / (Math.min(lum, gray) + 0.05);
96+
expect(contrast(luminance(0xff, 0xff, 0xff))).toBeGreaterThanOrEqual(4);
97+
expect(contrast(luminance(0x10, 0x16, 0x1d))).toBeGreaterThanOrEqual(4);
98+
});
6599
});
66100

67101
describe('initDisharmonyCharts', () => {

0 commit comments

Comments
 (0)