Skip to content

Commit e0c8a42

Browse files
authored
feat(css): add color-mix() tool (#184)
* feat(css): add color-mix() tool * fix column widths * apply review comments * in percentages use step 0.1 * for consistancy make shorder hue rotation default
1 parent 84ab8c4 commit e0c8a42

File tree

3 files changed

+332
-0
lines changed

3 files changed

+332
-0
lines changed

tools/color-mixer/classic.min.css

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/color-mixer/index.html

+327
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
<!DOCTYPE html>
2+
<html lang="en-us">
3+
<head>
4+
<link rel="stylesheet" href="classic.min.css" />
5+
<script src="pickr.es5.min.js"></script>
6+
<title>Color mixer: Utility to test color-mix() function</title>
7+
<style>
8+
#color-mixer {
9+
font-family: sans-serif;
10+
display: grid;
11+
grid-template-columns: 150px 1fr 1fr 150px;
12+
grid-template-areas:
13+
"header header header header"
14+
"text text text text"
15+
"color-one-text mix-text mix-text color-two-text"
16+
"color-picker-one mix-output mix-output color-picker-two"
17+
"mix-output-text mix-output-text mix-output-text mix-output-text"
18+
"percent-one percent-one percent-two percent-two"
19+
"color-space-label color-space interpolation-label interpolation";
20+
}
21+
22+
#color-mixer > * {
23+
padding: 0;
24+
margin: 0 auto;
25+
}
26+
27+
#color-mixer > :nth-child(1) {
28+
grid-area: header;
29+
}
30+
31+
#color-mixer > :nth-child(2) {
32+
grid-area: text;
33+
margin-bottom: 2rem;
34+
}
35+
36+
#color-mixer > :nth-child(3) {
37+
grid-area: color-one-text;
38+
}
39+
40+
#color-mixer > :nth-child(4) {
41+
grid-area: mix-text;
42+
}
43+
44+
#color-mixer > :nth-child(5) {
45+
grid-area: color-two-text;
46+
}
47+
48+
#color-mixer > :nth-child(6) {
49+
grid-area: color-picker-one;
50+
width: 100% !important;
51+
height: 100px !important;
52+
border-radius: 20% 0 0 20%;
53+
cursor: pointer;
54+
}
55+
56+
#color-mixer > :nth-child(7) {
57+
grid-area: mix-output;
58+
width: 100%;
59+
height: 100px;
60+
}
61+
62+
#color-mixer > :nth-child(8) {
63+
grid-area: color-picker-two;
64+
width: 100%;
65+
height: 100px;
66+
border-radius: 0 20% 20% 0;
67+
cursor: pointer;
68+
}
69+
70+
#color-mixer > :nth-child(9) {
71+
grid-area: mix-output-text;
72+
margin: 1rem auto;
73+
font-weight: bold;
74+
font-size: 0.8rem;
75+
}
76+
77+
#color-mixer > :nth-child(10) {
78+
grid-area: percent-one;
79+
width: 100%;
80+
display: flex;
81+
}
82+
83+
#color-mixer > :nth-child(11) {
84+
grid-area: percent-two;
85+
width: 100%;
86+
display: flex;
87+
}
88+
89+
#color-mixer > :nth-child(12) {
90+
grid-area: color-space-label;
91+
margin: 0 0 0 auto;
92+
}
93+
94+
#color-mixer > :nth-child(13) {
95+
grid-area: color-space;
96+
margin: 0 auto 0 1rem;
97+
}
98+
99+
#color-mixer > :nth-child(14) {
100+
grid-area: interpolation-label;
101+
margin: 0 0 0 auto;
102+
}
103+
104+
#color-mixer > :nth-child(15) {
105+
grid-area: interpolation;
106+
margin: 0 auto 0 1rem;
107+
}
108+
109+
input[type="range"] {
110+
width: 80%;
111+
}
112+
113+
label {
114+
font-family: monospace;
115+
margin: 1rem;
116+
}
117+
</style>
118+
</head>
119+
120+
<body>
121+
<div id="color-mixer">
122+
<h3>Color mixer</h3>
123+
<p>Click on 'color-one' and 'color-two' to select colors.</p>
124+
125+
<div>color-one</div>
126+
<div>mixed-color</div>
127+
<div>color-two</div>
128+
129+
<div id="color-one" class="color-one"></div>
130+
<div id="mixed-color"></div>
131+
<div id="color-two" class="color-two"></div>
132+
133+
<label id="mix-output-text"></label>
134+
135+
<div>
136+
<label id="percentage-one-label">50%</label>
137+
<input id="percentage-one" type="range" name="percent1" step="0.1" />
138+
</div>
139+
<div>
140+
<label id="percentage-two-label">50%</label>
141+
<input id="percentage-two" type="range" name="percent2" step="0.1" />
142+
</div>
143+
144+
<label>color space: </label>
145+
<select id="color-space">
146+
<option value="srgb">srgb</option>
147+
<option value="srgb-linear">srgb-linear</option>
148+
<option value="lab">lab</option>
149+
<option value="oklab" selected>oklab</option>
150+
<option value="xyz">xyz</option>
151+
<option value="xyz-d50">xyz-d50</option>
152+
<option value="xyz-d65">xyz-d65</option>
153+
<option value="hsl">hsl</option>
154+
<option value="hwb">hwb</option>
155+
<option value="lch">lch</option>
156+
<option value="oklch">oklch</option>
157+
</select>
158+
159+
<label id="interpolation-method-label">interpolation method: </label>
160+
<select id="interpolation-method">
161+
<option value="shorter hue" selected>shorter hue</option>
162+
<option value="longer hue">longer hue</option>
163+
<option value="increasing hue">increasing hue</option>
164+
<option value="decreasing hue">decreasing hue</option>
165+
</select>
166+
</div>
167+
168+
<script>
169+
const polarColorSpaces = ["hsl", "hwb", "lch", "oklch"];
170+
const root = document.querySelector(":root");
171+
172+
const colorSpace = document.getElementById("color-space");
173+
const interpolationMethod = document.getElementById(
174+
"interpolation-method"
175+
);
176+
const interpolationMethodLabel = document.getElementById(
177+
"interpolation-method-label"
178+
);
179+
180+
const colorOne = document.getElementById("color-one");
181+
const colorTwo = document.getElementById("color-two");
182+
const mixedColor = document.getElementById("mixed-color");
183+
const mixedOutputText = document.getElementById("mix-output-text");
184+
185+
const colorTextOne = document.getElementById("color-one-text");
186+
const colorTextTwo = document.getElementById("color-two-text");
187+
188+
const percentageOneLabel = document.getElementById(
189+
"percentage-one-label"
190+
);
191+
const percentageTwoLabel = document.getElementById(
192+
"percentage-two-label"
193+
);
194+
const percentageOne = document.getElementById("percentage-one");
195+
const percentageTwo = document.getElementById("percentage-two");
196+
197+
let pickerOne, pickerTwo;
198+
199+
function init() {
200+
const pickerOptions = {
201+
el: ".color-one",
202+
theme: "classic",
203+
useAsButton: true,
204+
default: "#ff7f50",
205+
position: "bottom-middle",
206+
components: {
207+
preview: true,
208+
opacity: true,
209+
hue: true,
210+
interaction: {
211+
hex: true,
212+
rgba: true,
213+
hsla: true,
214+
input: true,
215+
clear: false,
216+
save: true,
217+
},
218+
},
219+
swatches: [
220+
"rgba(244, 67, 54, 1)",
221+
"rgba(233, 30, 99, 1)",
222+
"rgba(156, 39, 176, 1)",
223+
"rgba(103, 58, 183, 1)",
224+
"rgba(63, 81, 181, 1)",
225+
"rgba(33, 150, 243, 1)",
226+
"rgba(3, 169, 244, 1)",
227+
"rgba(0, 188, 212, 1)",
228+
"rgba(0, 150, 136, 1)",
229+
"rgba(76, 175, 80, 1)",
230+
"rgba(139, 195, 74, 1)",
231+
"rgba(205, 220, 57, 1)",
232+
"rgba(255, 235, 59, 1)",
233+
"rgba(255, 193, 7, 1)",
234+
],
235+
};
236+
237+
pickerOne = Pickr.create(pickerOptions);
238+
pickerOne.setColor("#ff7f50");
239+
colorOne.style.setProperty("background-color", "#ff7f50");
240+
pickerOne.on("change", (color, source, instance) => {
241+
colorOne.style.setProperty("background-color", color.toRGBA());
242+
updateColorMix();
243+
});
244+
245+
pickerOptions.el = ".color-two";
246+
pickerOptions.default = "#00ffff";
247+
pickerTwo = Pickr.create(pickerOptions);
248+
pickerTwo.setColor("#00ffff");
249+
colorTwo.style.setProperty("background-color", "#00ffff");
250+
pickerTwo.on("change", (color, source, instance) => {
251+
colorTwo.style.setProperty("background-color", color.toRGBA());
252+
updateColorMix();
253+
});
254+
255+
percentageOne.addEventListener("input", (e) => {
256+
percentageOneLabel.innerText = e.target.value + "%";
257+
updateColorMix();
258+
});
259+
percentageTwo.addEventListener("input", (e) => {
260+
percentageTwoLabel.innerText = e.target.value + "%";
261+
updateColorMix();
262+
});
263+
colorSpace.addEventListener("change", (e) => {
264+
if (polarColorSpaces.includes(e.target.value)) {
265+
interpolationMethod.style.visibility = "visible";
266+
interpolationMethodLabel.style.visibility = "visible";
267+
} else {
268+
interpolationMethod.style.visibility = "hidden";
269+
interpolationMethodLabel.style.visibility = "hidden";
270+
}
271+
updateColorMix();
272+
});
273+
interpolationMethod.addEventListener("change", (e) => {
274+
updateColorMix();
275+
});
276+
277+
interpolationMethod.style.visibility = "hidden";
278+
interpolationMethodLabel.style.visibility = "hidden";
279+
}
280+
281+
function updateColorMix() {
282+
let colorMixFunction = "color-mix(in ";
283+
284+
root.style.setProperty("--color-space", colorSpace.value);
285+
colorMixFunction += colorSpace.value;
286+
287+
if (polarColorSpaces.includes(colorSpace.value)) {
288+
root.style.setProperty(
289+
"--interpolation-method",
290+
interpolationMethod.value
291+
);
292+
colorMixFunction += ` ${interpolationMethod.value}, `;
293+
} else {
294+
root.style.setProperty("--interpolation-method", "");
295+
colorMixFunction += `, `;
296+
}
297+
root.style.setProperty(
298+
"--color-one",
299+
colorOne.style.getPropertyValue("background-color")
300+
);
301+
root.style.setProperty("--percentage-one", percentageOne.value + "%");
302+
colorMixFunction += `${pickerOne.getColor().toHEXA().toString()} ${
303+
percentageOne.value
304+
}%, `;
305+
306+
root.style.setProperty(
307+
"--color-two",
308+
colorTwo.style.getPropertyValue("background-color")
309+
);
310+
root.style.setProperty("--percentage-two", percentageTwo.value + "%");
311+
colorMixFunction += `${pickerTwo.getColor().toHEXA().toString()} ${
312+
percentageTwo.value
313+
}%)`;
314+
315+
mixedColor.style.setProperty("background-color", colorMixFunction);
316+
317+
colorMixFunction += ` = ${
318+
window.getComputedStyle(mixedColor).backgroundColor
319+
}`;
320+
mixedOutputText.innerText = colorMixFunction;
321+
}
322+
323+
init();
324+
updateColorMix();
325+
</script>
326+
</body>
327+
</html>

tools/color-mixer/pickr.es5.min.js

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)