client/note tree: adjust the custom color of tree items to maintain readability

This commit is contained in:
Adorian Doran
2025-10-18 21:05:34 +03:00
parent 0d94ae9f61
commit 2b460be63a
10 changed files with 151 additions and 13 deletions

View File

@@ -1,5 +1,20 @@
import {readCssVar} from "../utils/css-var";
import Color, { ColorInstance } from "color";
const registeredClasses = new Set<string>();
// Read the color lightness limits defined in the theme as CSS variables
const lightThemeColorMaxLightness = readCssVar(
document.documentElement,
"tree-item-light-theme-max-color-lightness"
).asNumber(70);
const darkThemeColorMinLightness = readCssVar(
document.documentElement,
"tree-item-dark-theme-min-color-lightness"
).asNumber(50);
function createClassForColor(color: string | null) {
if (!color?.trim()) {
return "";
@@ -13,9 +28,16 @@ function createClassForColor(color: string | null) {
const className = `color-${normalizedColorName}`;
const adjustedColor = adjustColorLightness(color, lightThemeColorMaxLightness!, darkThemeColorMinLightness!);
if (!adjustedColor) return "";
if (!registeredClasses.has(className)) {
// make the active fancytree selector more specific than the normal color setting
$("head").append(`<style>.${className}, span.fancytree-active.${className} { color: ${color} !important; }</style>`);
$("head").append(`<style>
.${className}, span.fancytree-active.${className} {
--light-theme-custom-color: ${adjustedColor.lightThemeColor};
--dark-theme-custom-color: ${adjustedColor.darkThemeColor}
}
</style>`);
registeredClasses.add(className);
}
@@ -23,6 +45,31 @@ function createClassForColor(color: string | null) {
return className;
}
/**
* Returns a pair of colors — one optimized for light themes and the other for dark themes, derived
* from the specified color to maintain sufficient contrast with each theme.
* The adjustment is performed by limiting the colors lightness in the CIELAB color space,
* according to the lightThemeMaxLightness and darkThemeMinLightness parameters.
*/
function adjustColorLightness(color: string, lightThemeMaxLightness: number, darkThemeMinLightness: number) {
let labColor: ColorInstance | undefined = undefined;
try {
// Parse the given color in the CIELAB color space
labColor = Color(color).lab();
} catch (ex) {
console.error(`Failed to parse color: "${color}"`, ex);
return;
}
// For the light theme, limit the maximum lightness
const lightThemeColor = labColor.l(Math.min(labColor.l(), lightThemeMaxLightness)).hex();
// For the light theme, limit the minimum lightness
const darkThemeColor = labColor.l(Math.max(labColor.l(), darkThemeMinLightness)).hex();
return {lightThemeColor, darkThemeColor};
}
export default {
createClassForColor
};
};