diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-10 15:23:50 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-10 15:23:50 -0500 |
| commit | bb2aed2f4ea57bfd0468e683bc33e795a2bf4711 (patch) | |
| tree | aec090c00e4d010f040a740737da1ed2841e0fde /scripts/theme-studio/colormath.js | |
| parent | 28d822e31238206a0554291318cccaa022f2ea83 (diff) | |
| download | dotemacs-bb2aed2f4ea57bfd0468e683bc33e795a2bf4711.tar.gz dotemacs-bb2aed2f4ea57bfd0468e683bc33e795a2bf4711.zip | |
fix(theme-studio): derive box bevel colors from the face background
The released/pressed bevel was a flat translucent white/black overlay, which reads weaker than the box Emacs draws. reliefColors in colormath.js now ports Emacs 30's x_alloc_lighter_color: highlight = bg x1.2, shadow = bg x0.6, an additive boost for dark backgrounds, and the same-color fallback for pure black and white. boxCss takes the face's effective bg and derives both edges from it. Pressed swaps the pair, and the translucent pair remains only when no bg is known. Width stays 1px because dupre's :line-width -1 draws 1px lines in Emacs too. The gap was color strength, not width. Five node tests pin hand-computed fixtures from the C source, and a new #beveltest gate pins the wiring.
Diffstat (limited to 'scripts/theme-studio/colormath.js')
| -rw-r--r-- | scripts/theme-studio/colormath.js | 29 |
1 files changed, 28 insertions, 1 deletions
diff --git a/scripts/theme-studio/colormath.js b/scripts/theme-studio/colormath.js index 167a5ea1..2a7328e5 100644 --- a/scripts/theme-studio/colormath.js +++ b/scripts/theme-studio/colormath.js @@ -190,4 +190,31 @@ function paletteWarnings(palette, threshold = 0.02, cap = 5) { return { warnings: pairs.slice(0, cap), overflow: Math.max(0, pairs.length - cap), nearest }; } -export { srgb2oklab, oklab2oklch, oklch2oklab, oklch2hex, apca, deltaE, hex2rgb, lin, rl, contrast, rating, hsv2rgb, rgb2hsv, rgb2hex, oklab2lrgb, inGamut, lrgb2hex, planeCell, paletteWarnings }; +// --- 3D-box relief colors, matching Emacs's renderer --------------------- +// Port of x_alloc_lighter_color (Emacs 30 xterm.c): highlight = bg x 1.2 +// (delta 0x8000), shadow = bg x 0.6 (delta 0x4000), both in 16-bit channel +// space. Backgrounds dimmer than 48000/65535 (by Emacs's 2R+3G+B/6 weighting) +// get an additive boost of delta*dimness*factor/2, because scaling alone +// barely moves a dark color. When the result still equals the background +// (pure black shadow, pure white highlight), Emacs retries with bg+delta. +function reliefColors(bgHex) { + const rgb = hex2rgb(bgHex); + if (rgb.some((c) => Number.isNaN(c))) return { hl: null, sh: null }; + const ch16 = rgb.map((c) => c * 257); + const one = (factor, delta) => { + let nw = ch16.map((c) => Math.min(0xffff, factor * c)); + const bright = (2 * ch16[0] + 3 * ch16[1] + ch16[2]) / 6; + if (bright < 48000) { + const md = delta * (1 - bright / 48000) * factor / 2; + nw = factor < 1 + ? nw.map((v) => Math.max(0, v - md)) + : nw.map((v) => Math.min(0xffff, v + md)); + } + if (nw.every((v, i) => Math.round(v) === ch16[i])) + nw = ch16.map((c) => Math.min(0xffff, c + delta)); + return '#' + nw.map((v) => Math.round(v / 257).toString(16).padStart(2, '0')).join(''); + }; + return { hl: one(1.2, 0x8000), sh: one(0.6, 0x4000) }; +} + +export { srgb2oklab, oklab2oklch, oklch2oklab, oklch2hex, apca, deltaE, hex2rgb, lin, rl, contrast, rating, hsv2rgb, rgb2hsv, rgb2hex, oklab2lrgb, inGamut, lrgb2hex, planeCell, paletteWarnings, reliefColors }; |
