feat: add color blind colors to reviews graph

Signed-off-by: David Brenn <davidbrenn@t-online.de>
This commit is contained in:
David Brenn 2025-08-12 15:30:29 +02:00
parent 8d6d8dc6c8
commit c530a024e2
3 changed files with 63 additions and 14 deletions

View file

@ -22,7 +22,7 @@ import {
sum,
} from "d3";
import type { GraphBounds } from "./graph-helpers";
import { colorBlindColors, type GraphBounds } from "./graph-helpers";
type Count = [string, number, boolean, string];
export interface GraphData {
@ -36,13 +36,13 @@ let barColours;
if((window as any).colorBlindMode)
{
barColours = [
"#88CCEE", /* new */
"#44AA99", /* learn */
"#117733", /* relearn */
"#CC6677", /* young */
"#882255", /* mature */
"#DDCC77", /* suspended */
"#332288", /* buried */
colorBlindColors.new, /* new */
colorBlindColors.learn, /* learn */
colorBlindColors.relearn, /* relearn */
colorBlindColors.young, /* young */
colorBlindColors.mature, /* mature */
colorBlindColors.suspended, /* suspended */
colorBlindColors.buried, /* buried */
];
}
else

View file

@ -105,3 +105,17 @@ export function numericMap<T>(obj: { [k: string]: T }): Map<number, T> {
export function getNumericMapBinValue(d: Bin<Map<number, number>, number>): number {
return sum(d, (d) => d[1]);
}
/**
* Colorblind-friendly colors from https://davidmathlogic.com/colorblind/
*/
export const colorBlindColors = {
new: "#88CCEE",
learn: "#44AA99",
relearn: "#117733",
young: "#CC6677",
mature: "#882255",
suspended: "#DDCC77",
buried: "#332288",
filtered: "#AA4499"
};

View file

@ -22,16 +22,20 @@ import {
interpolateGreens,
interpolatePurples,
interpolateReds,
interpolateRgb,
max,
min,
pointer,
scaleLinear,
scaleOrdinal,
scaleSequential,
select,
sum,
color,
hsl
} from "d3";
import type { GraphBounds, TableDatum } from "./graph-helpers";
import { colorBlindColors, type GraphBounds, type TableDatum } from "./graph-helpers";
import { GraphRange, numericMap, setDataAvailable } from "./graph-helpers";
import { hideTooltip, showTooltip } from "./tooltip-utils.svelte";
@ -188,21 +192,52 @@ export function renderReviews(
x.domain() as any,
);
const colorBlindMode = (window as any).colorBlindMode;
function makeColorBlindGradient(baseHex: string, satAdjust = 0.02, lightAdjust = 0.02) {
const base = color(baseHex);
if (!base) throw new Error(`Invalid color: ${baseHex}`);
const lighter = hsl(base);
lighter.s = Math.min(1, lighter.s + satAdjust);
lighter.l = Math.min(1, lighter.l + lightAdjust);
const darker = hsl(base);
darker.s = Math.max(0, darker.s - satAdjust);
darker.l = Math.max(0, darker.l - lightAdjust);
return scaleSequential(interpolateRgb(darker.toString(), lighter.toString()));
}
const colorBlindScales = {
mature: makeColorBlindGradient(colorBlindColors.mature),
learn: makeColorBlindGradient(colorBlindColors.learn),
relearn: makeColorBlindGradient(colorBlindColors.relearn),
young: makeColorBlindGradient(colorBlindColors.young),
suspended: makeColorBlindGradient(colorBlindColors.suspended),
buried: makeColorBlindGradient(colorBlindColors.buried),
filtered: makeColorBlindGradient(colorBlindColors.filtered)
};
Object.values(colorBlindScales).forEach(scale => scale.domain(x.domain() as any));
function binColor(idx: BinIndex): ScaleSequential<string> {
switch (idx) {
case BinIndex.Mature:
return darkerGreens;
return colorBlindMode ? colorBlindScales.mature : darkerGreens;
case BinIndex.Young:
return lighterGreens;
return colorBlindMode ? colorBlindScales.young : lighterGreens;
case BinIndex.Learn:
return blues;
return colorBlindMode ? colorBlindScales.learn : blues;
case BinIndex.Relearn:
return reds;
return colorBlindMode ? colorBlindScales.relearn : reds;
case BinIndex.Filtered:
return purples;
return colorBlindMode ? colorBlindScales.filtered : purples;
}
}
function valueLabel(n: number): string {
if (showTime) {
return timeSpan(n / 1000, false, true, TimespanUnit.Hours);