Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Read the colour the browser actually resolved

aceaabbf44f3e20ab97e39c1de5047d11547a283 · Keenan Nemetz · 7d ago · parent 214145e5

Read the colour the browser actually resolved

The palette came out black-skied and oversaturated because the bundle read
every colour with "the first three numbers", and getComputedStyle
serializes a color-mix() result as color(srgb 0.571765 0.658824
0.461961). Those decimals contain digits, so the parser built
#00b36b600 — not a colour — for the sky and garbage for every hill.

Parse both forms the browser can return: rgb()/rgba() for a legacy sRGB
value and color(srgb r g b) with components in 0..1 for one that came
through color-mix(). Clamp, because color() components are floats.

Reproduced and checked in headless Chromium against a real instance, in
both themes and at both sizes, before and after: the fills are now
#bba575 / #9da9a0 / #886759 / #596855 / #445a52 on dark-medium and
#e7c76e / #f57d26 / #b6987a / #889371 / #6f8984 on light-medium.

scripts/avatars-entry.js +32−15

@@ -88,9 +88,38 @@ const landscape = {
88 88 },
89 89 };
90 90
91 // getComputedStyle serializes a resolved colour two ways: rgb()/rgba() for a
92 // legacy sRGB value, and color(srgb r g b) with components in 0..1 when the value
93 // came through color-mix(). Reading "the first three numbers" therefore splits
94 // 0.571765 into 0 and 571765 and builds a six-digit-looking string that is not a
95 // colour; the browser then paints the sky black. Parse both forms, and clamp,
96 // because color() components are floats.
97 const toHex = (value) => {
98 const text = value.trim();
99 const srgb = /^color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/.exec(text);
100 if (srgb) {
101 return [1, 2, 3]
102 .map((i) => Math.round(Math.min(1, Math.max(0, parseFloat(srgb[i]))) * 255))
103 .map((n) => n.toString(16).padStart(2, "0"))
104 .join("");
105 }
106 const legacy = /^rgba?\(([^)]+)\)/.exec(text);
107 if (legacy) {
108 const parts = legacy[1].split(/[\s,/]+/).filter(Boolean).map(Number);
109 if (parts.length >= 3) {
110 return parts
111 .slice(0, 3)
112 .map((n) => Math.round(Math.min(255, Math.max(0, n))))
113 .map((n) => n.toString(16).padStart(2, "0"))
114 .join("");
115 }
116 }
117 return "";
118 };
119
91 120 // A custom property can hold a color-mix(), and getComputedStyle reads that back
92 121 // as text rather than as a colour. One hidden element resolves any of them to the
93 // rgb the browser actually paints, so the palette follows the theme exactly.
122 // colour the browser actually paints, so the palette follows the theme exactly.
94 123 let probe;
95 124 const resolve = (name) => {
96 125 if (!probe) {
@@ -99,12 +128,7 @@ const resolve = (name) => {
99 128 document.body.appendChild(probe);
100 129 }
101 130 probe.style.color = `var(${name})`;
102 const parts = (getComputedStyle(probe).color.match(/\d+/g) || []).map(Number);
103 if (parts.length < 3) return "";
104 return parts
105 .slice(0, 3)
106 .map((n) => n.toString(16).padStart(2, "0"))
107 .join("");
131 return toHex(getComputedStyle(probe).color);
108 132 };
109 133
110 134 // The palette follows the active Everforest variant, so it is resolved once per
@@ -130,14 +154,7 @@ const themePalette = () => {
130 154 // The sky comes from the element, not from this file: vole.css and everforest.css
131 155 // own the seven grounds, and reading the computed colour keeps the initial the
132 156 // server renders and the finished landscape on the same sky.
133 const ground = (el) => {
134 const parts = (getComputedStyle(el).backgroundColor.match(/\d+/g) || []).map(Number);
135 if (parts.length < 3) return "";
136 return parts
137 .slice(0, 3)
138 .map((n) => n.toString(16).padStart(2, "0"))
139 .join("");
140 };
157 const ground = (el) => toHex(getComputedStyle(el).backgroundColor);
141 158
142 159 const render = (el) => {
143 160 if (el.querySelector("svg")) return;