├── palette.png
├── README.md
├── style.css
├── index.html
└── app.js
/palette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/japanihon/palettecolorgen/HEAD/palette.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Palette Color Generator 2.0
2 |
3 | # Link: https://palettegencolor.netlify.app/
4 |
5 | 
6 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | box-sizing: border-box;
5 | }
6 | body {
7 | font-family: "Muli", sans-serif;
8 | color: rgb(51, 51, 51);
9 | }
10 | button {
11 | font-family: "Muli", sans-serif;
12 | }
13 | path,
14 | i,
15 | svg {
16 | pointer-events: none;
17 | }
18 | .colors {
19 | min-height: 80vh;
20 | display: flex;
21 | color: rgb(212, 212, 212);
22 | }
23 | .color {
24 | height: 80vh;
25 | display: flex;
26 | flex: 1;
27 | flex-direction: column;
28 | align-items: center;
29 | justify-content: space-evenly;
30 | position: relative;
31 | overflow: hidden;
32 | }
33 | .color h2 {
34 | font-size: 2rem;
35 | cursor: pointer;
36 | }
37 | .sliders {
38 | display: flex;
39 | flex-direction: column;
40 | position: absolute;
41 | bottom: 0%;
42 | background: rgb(255, 255, 255);
43 | opacity: 0;
44 | padding: 1rem;
45 | width: 80%;
46 | border-top-right-radius: 1rem;
47 | pointer-events: none;
48 | border-top-left-radius: 1rem;
49 | /* We are adding an adjustemnt class */
50 | transform: translateY(100px);
51 | transition: all 0.5s ease-in-out;
52 | }
53 | .sliders.active {
54 | opacity: 1;
55 | transform: translateY(0px);
56 | pointer-events: all;
57 | }
58 | .sliders button,
59 | .close-save,
60 | .close-library {
61 | position: absolute;
62 | top: 0;
63 | right: 0;
64 | padding: 0.5rem;
65 | border-top-left-radius: 1rem;
66 | border-bottom-left-radius: 1rem;
67 | border: none;
68 | background: rgb(73, 73, 73);
69 | color: white;
70 | cursor: pointer;
71 | font-weight: bold;
72 | }
73 | .controls {
74 | display: flex;
75 | flex-direction: column;
76 | }
77 | .panel {
78 | display: flex;
79 | align-items: center;
80 | justify-content: space-evenly;
81 | height: 20vh;
82 | }
83 | .panel button {
84 | font-size: 1.2rem;
85 | margin: 1rem;
86 | padding: 1rem 2rem;
87 | background: rgb(31, 33, 63);
88 | border: none;
89 | color: white;
90 | cursor: pointer;
91 | border-radius: 1rem;
92 | }
93 | .panel p {
94 | font-size: 1.2rem;
95 | }
96 | .library-panel,
97 | .generate-panel,
98 | .save-panel {
99 | display: flex;
100 | flex-direction: column;
101 | align-items: center;
102 | }
103 |
104 | .adjust,
105 | .lock {
106 | font-size: 2rem;
107 | border: none;
108 | background: none;
109 | cursor: pointer;
110 | margin: 2rem 0rem;
111 | }
112 |
113 | /* Slider Stuff */
114 | input[type="range"] {
115 | -webkit-appearance: none;
116 | margin: 1rem 0rem;
117 | width: 100%;
118 | position: relative;
119 | border-radius: 1rem;
120 | cursor: pointer;
121 | }
122 |
123 | .copy-container,
124 | .save-container,
125 | .library-container {
126 | position: fixed;
127 | top: 0%;
128 | left: 0%;
129 | background: rgba(0, 0, 0, 0.5);
130 | width: 100%;
131 | height: 100%;
132 | display: flex;
133 | justify-content: center;
134 | transition: all 0.5s ease-in-out;
135 | align-items: center;
136 | opacity: 0;
137 | pointer-events: none;
138 | }
139 | .copy-popup,
140 | .save-popup,
141 | .library-popup {
142 | background: white;
143 | display: flex;
144 | flex-direction: column;
145 | justify-content: space-evenly;
146 | align-items: center;
147 | border-radius: 2rem;
148 | transition: transform 0.5s ease;
149 | transform: translateY(-2rem);
150 | min-width: 30%;
151 | min-height: 30vh;
152 | }
153 | .copy-popup h4,
154 | .save-popup h4,
155 | .library-popup h4 {
156 | font-size: 2rem;
157 | padding: 2rem;
158 | }
159 | .copy-container.active,
160 | .save-container.active,
161 | .library-container.active {
162 | opacity: 1;
163 | pointer-events: all;
164 | }
165 | .copy-popup.active,
166 | .save-popup.active,
167 | .library-popup.active {
168 | transform: translateY(0rem);
169 | }
170 |
171 | .save-name {
172 | font-size: 1.5rem;
173 | padding: 1rem;
174 | }
175 | .close-save,
176 | .close-library {
177 | border-top-right-radius: 1rem;
178 | border-bottom-left-radius: 1rem;
179 | padding: 0.5rem;
180 | }
181 | .submit-save {
182 | margin: 2rem;
183 | padding: 1rem 3rem;
184 | background: rgb(60, 60, 92);
185 | border: none;
186 | border-radius: 10px;
187 | color: white;
188 | cursor: pointer;
189 | }
190 |
191 | .library-popup {
192 | min-width: 40%;
193 | padding: 1rem;
194 | justify-content: flex-start;
195 | overflow-y: scroll;
196 | max-height: 50vh;
197 | }
198 | .custom-palette {
199 | display: flex;
200 | align-items: center;
201 | justify-content: space-evenly;
202 | width: 100%;
203 | padding: 2rem;
204 | }
205 | .small-preview {
206 | display: flex;
207 | flex: 1;
208 | }
209 | .custom-palette h4 {
210 | flex: 1;
211 | }
212 |
213 | .small-preview div {
214 | height: 5rem;
215 | flex: 1;
216 | }
217 | .pick-palette-btn {
218 | height: 5rem;
219 | border: none;
220 | padding: 1rem;
221 | cursor: pointer;
222 | font-size: 1.2rem;
223 | background: rgb(41, 41, 41);
224 | color: white;
225 | }
226 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Palette Color Generator 2.0
7 |
11 |
12 |
17 |
18 |
19 |
20 |
60 |
100 |
140 |
180 |
220 |
221 |
222 |
223 |
224 |
Library
225 |
226 |
227 |
228 |
Generate
229 |
230 |
234 |
235 |
236 |
240 |
241 |
242 |
248 |
249 |
250 |
254 |
255 |
260 |
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //Global selections and variables
2 | const colorDivs = document.querySelectorAll(".color");
3 | const generateBtn = document.querySelector(".generate");
4 | const sliders = document.querySelectorAll('input[type="range"]');
5 | const currentHexes = document.querySelectorAll(".color h2");
6 | const popup = document.querySelector(".copy-container");
7 | const adjustButton = document.querySelectorAll(".adjust");
8 | const lockButton = document.querySelectorAll(".lock");
9 | const closeAdjustments = document.querySelectorAll(".close-adjustment");
10 | const sliderContainers = document.querySelectorAll(".sliders");
11 | let initialColors;
12 | //This is for local storage
13 | let savedPalettes = [];
14 |
15 | //Add our event listeners
16 | generateBtn.addEventListener("click", randomColors);
17 | sliders.forEach(slider => {
18 | slider.addEventListener("input", hslControls);
19 | });
20 | colorDivs.forEach((div, index) => {
21 | div.addEventListener("change", () => {
22 | updateTextUI(index);
23 | });
24 | });
25 | currentHexes.forEach(hex => {
26 | hex.addEventListener("click", () => {
27 | copyToClipboard(hex);
28 | });
29 | });
30 | popup.addEventListener("transitionend", () => {
31 | const popupBox = popup.children[0];
32 | popup.classList.remove("active");
33 | popupBox.classList.remove("active");
34 | });
35 | adjustButton.forEach((button, index) => {
36 | button.addEventListener("click", () => {
37 | openAdjustmentPanel(index);
38 | });
39 | });
40 | closeAdjustments.forEach((button, index) => {
41 | button.addEventListener("click", () => {
42 | closeAdjustmentPanel(index);
43 | });
44 | });
45 | lockButton.forEach((button, index) => {
46 | button.addEventListener("click", e => {
47 | lockLayer(e, index);
48 | });
49 | });
50 |
51 | //Functions
52 | //Color Generator
53 | function generateHex() {
54 | const hexColor = chroma.random();
55 | return hexColor;
56 | }
57 |
58 | function randomColors() {
59 | initialColors = [];
60 |
61 | colorDivs.forEach((div, index) => {
62 | const hexText = div.children[0];
63 | const randomColor = generateHex();
64 | //Add it to the array
65 | if (div.classList.contains("locked")) {
66 | initialColors.push(hexText.innerText);
67 | return;
68 | } else {
69 | initialColors.push(chroma(randomColor).hex());
70 | }
71 |
72 | //Add the color to the bg
73 | div.style.backgroundColor = randomColor;
74 | hexText.innerText = randomColor;
75 |
76 | //Check for contrast
77 | checkTextContrast(randomColor, hexText);
78 | //Initial Colorize Sliders
79 | const color = chroma(randomColor);
80 | const sliders = div.querySelectorAll(".sliders input");
81 | const hue = sliders[0];
82 | const brightness = sliders[1];
83 | const saturation = sliders[2];
84 |
85 | colorizeSliders(color, hue, brightness, saturation);
86 | });
87 | //Reset Inputs
88 | resetInputs();
89 | //Check For Button Contrast
90 | adjustButton.forEach((button, index) => {
91 | checkTextContrast(initialColors[index], button);
92 | checkTextContrast(initialColors[index], lockButton[index]);
93 | });
94 | }
95 |
96 | function checkTextContrast(color, text) {
97 | const luminance = chroma(color).luminance();
98 | if (luminance > 0.5) {
99 | text.style.color = "black";
100 | } else {
101 | text.style.color = "white";
102 | }
103 | }
104 |
105 | function colorizeSliders(color, hue, brightness, saturation) {
106 | //Scale Saturation
107 | const noSat = color.set("hsl.s", 0);
108 | const fullSat = color.set("hsl.s", 1);
109 | const scaleSat = chroma.scale([noSat, color, fullSat]);
110 | //Scale Brightness
111 | const midBright = color.set("hsl.l", 0.5);
112 | const scaleBright = chroma.scale(["black", midBright, "white"]);
113 |
114 | //Update Input Colors
115 | saturation.style.backgroundImage = `linear-gradient(to right,${scaleSat(
116 | 0
117 | )}, ${scaleSat(1)})`;
118 | brightness.style.backgroundImage = `linear-gradient(to right,${scaleBright(
119 | 0
120 | )},${scaleBright(0.5)} ,${scaleBright(1)})`;
121 | hue.style.backgroundImage = `linear-gradient(to right, rgb(204,75,75),rgb(204,204,75),rgb(75,204,75),rgb(75,204,204),rgb(75,75,204),rgb(204,75,204),rgb(204,75,75))`;
122 | }
123 |
124 | function hslControls(e) {
125 | const index =
126 | e.target.getAttribute("data-bright") ||
127 | e.target.getAttribute("data-sat") ||
128 | e.target.getAttribute("data-hue");
129 |
130 | let sliders = e.target.parentElement.querySelectorAll('input[type="range"]');
131 | const hue = sliders[0];
132 | const brightness = sliders[1];
133 | const saturation = sliders[2];
134 |
135 | const bgColor = initialColors[index];
136 |
137 | let color = chroma(bgColor)
138 | .set("hsl.s", saturation.value)
139 | .set("hsl.l", brightness.value)
140 | .set("hsl.h", hue.value);
141 |
142 | colorDivs[index].style.backgroundColor = color;
143 |
144 | //Colorize inputs/sliders
145 | colorizeSliders(color, hue, brightness, saturation);
146 | }
147 | function updateTextUI(index) {
148 | const activeDiv = colorDivs[index];
149 | const color = chroma(activeDiv.style.backgroundColor);
150 | const textHex = activeDiv.querySelector("h2");
151 | const icons = activeDiv.querySelectorAll(".controls button");
152 | textHex.innerText = color.hex();
153 | //Check Contrast
154 | checkTextContrast(color, textHex);
155 | for (icon of icons) {
156 | checkTextContrast(color, icon);
157 | }
158 | }
159 | function resetInputs() {
160 | const sliders = document.querySelectorAll(".sliders input");
161 | sliders.forEach(slider => {
162 | if (slider.name === "hue") {
163 | const hueColor = initialColors[slider.getAttribute("data-hue")];
164 | const hueValue = chroma(hueColor).hsl()[0];
165 | slider.value = Math.floor(hueValue);
166 | }
167 | if (slider.name === "brightness") {
168 | const brightColor = initialColors[slider.getAttribute("data-bright")];
169 | const brightValue = chroma(brightColor).hsl()[2];
170 | slider.value = Math.floor(brightValue * 100) / 100;
171 | }
172 | if (slider.name === "saturation") {
173 | const satColor = initialColors[slider.getAttribute("data-sat")];
174 | const satValue = chroma(satColor).hsl()[1];
175 | slider.value = Math.floor(satValue * 100) / 100;
176 | }
177 | });
178 | }
179 | function copyToClipboard(hex) {
180 | const el = document.createElement("textarea");
181 | el.value = hex.innerText;
182 | document.body.appendChild(el);
183 | el.select();
184 | document.execCommand("copy");
185 | document.body.removeChild(el);
186 | //Pop up animation
187 | const popupBox = popup.children[0];
188 | popup.classList.add("active");
189 | popupBox.classList.add("active");
190 | }
191 | function openAdjustmentPanel(index) {
192 | sliderContainers[index].classList.toggle("active");
193 | }
194 | function closeAdjustmentPanel(index) {
195 | sliderContainers[index].classList.remove("active");
196 | }
197 | function lockLayer(e, index) {
198 | const lockSVG = e.target.children[0];
199 | const activeBg = colorDivs[index];
200 | activeBg.classList.toggle("locked");
201 |
202 | if (lockSVG.classList.contains("fa-lock-open")) {
203 | e.target.innerHTML = '';
204 | } else {
205 | e.target.innerHTML = '';
206 | }
207 | }
208 |
209 | //Implement Save to palette and LOCAL STORAGE STUFF
210 | const saveBtn = document.querySelector(".save");
211 | const submitSave = document.querySelector(".submit-save");
212 | const closeSave = document.querySelector(".close-save");
213 | const saveContainer = document.querySelector(".save-container");
214 | const saveInput = document.querySelector(".save-container input");
215 | const libraryContainer = document.querySelector(".library-container");
216 | const libraryBtn = document.querySelector(".library");
217 | const closeLibraryBtn = document.querySelector(".close-library");
218 |
219 | //Event Listeners
220 | saveBtn.addEventListener("click", openPalette);
221 | closeSave.addEventListener("click", closePalette);
222 | submitSave.addEventListener("click", savePalette);
223 | libraryBtn.addEventListener("click", openLibrary);
224 | closeLibraryBtn.addEventListener("click", closeLibrary);
225 |
226 | function openPalette(e) {
227 | const popup = saveContainer.children[0];
228 | saveContainer.classList.add("active");
229 | popup.classList.add("active");
230 | }
231 | function closePalette(e) {
232 | const popup = saveContainer.children[0];
233 | saveContainer.classList.remove("active");
234 | popup.classList.add("remove");
235 | }
236 | function savePalette(e) {
237 | saveContainer.classList.remove("active");
238 | popup.classList.remove("active");
239 | const name = saveInput.value;
240 | const colors = [];
241 | currentHexes.forEach(hex => {
242 | colors.push(hex.innerText);
243 | });
244 | //Generate Object
245 | //*1
246 | // const paletteObjects = JSON.parse(localStorage.getItem("palettes"));
247 | // let paletteNr;
248 | // if (paletteObjects) {
249 | // paletteNr = paletteObjects.length;
250 | // } else {
251 | // paletteNr = savedPalettes.length;
252 | // }
253 |
254 | let paletteNr;
255 | const paletteObjects = JSON.parse(localStorage.getItem("palettes"));
256 | if (paletteObjects) {
257 | paletteNr = paletteObjects.length;
258 | } else {
259 | paletteNr = savedPalettes.length;
260 | }
261 |
262 | const paletteObj = { name, colors, nr: paletteNr };
263 | savedPalettes.push(paletteObj);
264 | //Save to localStorage
265 | savetoLocal(paletteObj);
266 | saveInput.value = "";
267 | //Generate the palette for Library
268 | const palette = document.createElement("div");
269 | palette.classList.add("custom-palette");
270 | const title = document.createElement("h4");
271 | title.innerText = paletteObj.name;
272 | const preview = document.createElement("div");
273 | preview.classList.add("small-preview");
274 | paletteObj.colors.forEach(smallColor => {
275 | const smallDiv = document.createElement("div");
276 | smallDiv.style.backgroundColor = smallColor;
277 | preview.appendChild(smallDiv);
278 | });
279 | const paletteBtn = document.createElement("button");
280 | paletteBtn.classList.add("pick-palette-btn");
281 | paletteBtn.classList.add(paletteObj.nr);
282 | paletteBtn.innerText = "Select";
283 |
284 | //Attach event to the btn
285 | paletteBtn.addEventListener("click", e => {
286 | closeLibrary();
287 | const paletteIndex = e.target.classList[1];
288 | initialColors = [];
289 | savedPalettes[paletteIndex].colors.forEach((color, index) => {
290 | initialColors.push(color);
291 | colorDivs[index].style.backgroundColor = color;
292 | const text = colorDivs[index].children[0];
293 | checkTextContrast(color, text);
294 | updateTextUI(index);
295 | });
296 | resetInputs();
297 | });
298 |
299 | //Append to Library
300 | palette.appendChild(title);
301 | palette.appendChild(preview);
302 | palette.appendChild(paletteBtn);
303 | libraryContainer.children[0].appendChild(palette);
304 | }
305 |
306 | function savetoLocal(paletteObj) {
307 | let localPalettes;
308 | if (localStorage.getItem("palettes") === null) {
309 | localPalettes = [];
310 | } else {
311 | localPalettes = JSON.parse(localStorage.getItem("palettes"));
312 | }
313 | localPalettes.push(paletteObj);
314 | localStorage.setItem("palettes", JSON.stringify(localPalettes));
315 | }
316 | function openLibrary() {
317 | const popup = libraryContainer.children[0];
318 | libraryContainer.classList.add("active");
319 | popup.classList.add("active");
320 | }
321 | function closeLibrary() {
322 | const popup = libraryContainer.children[0];
323 | libraryContainer.classList.remove("active");
324 | popup.classList.remove("active");
325 | }
326 |
327 | function getLocal() {
328 | if (localStorage.getItem("palettes") === null) {
329 | //Local Palettes
330 | localPalettes = [];
331 | } else {
332 | const paletteObjects = JSON.parse(localStorage.getItem("palettes"));
333 | // *2
334 |
335 | savedPalettes = [...paletteObjects];
336 | paletteObjects.forEach(paletteObj => {
337 | //Generate the palette for Library
338 | const palette = document.createElement("div");
339 | palette.classList.add("custom-palette");
340 | const title = document.createElement("h4");
341 | title.innerText = paletteObj.name;
342 | const preview = document.createElement("div");
343 | preview.classList.add("small-preview");
344 | paletteObj.colors.forEach(smallColor => {
345 | const smallDiv = document.createElement("div");
346 | smallDiv.style.backgroundColor = smallColor;
347 | preview.appendChild(smallDiv);
348 | });
349 | const paletteBtn = document.createElement("button");
350 | paletteBtn.classList.add("pick-palette-btn");
351 | paletteBtn.classList.add(paletteObj.nr);
352 | paletteBtn.innerText = "Select";
353 |
354 | //Attach event to the btn
355 | paletteBtn.addEventListener("click", e => {
356 | closeLibrary();
357 | const paletteIndex = e.target.classList[1];
358 | initialColors = [];
359 | paletteObjects[paletteIndex].colors.forEach((color, index) => {
360 | initialColors.push(color);
361 | colorDivs[index].style.backgroundColor = color;
362 | const text = colorDivs[index].children[0];
363 | checkTextContrast(color, text);
364 | updateTextUI(index);
365 | });
366 | resetInputs();
367 | });
368 |
369 | //Append to Library
370 | palette.appendChild(title);
371 | palette.appendChild(preview);
372 | palette.appendChild(paletteBtn);
373 | libraryContainer.children[0].appendChild(palette);
374 | });
375 | }
376 | }
377 |
378 | getLocal();
379 | randomColors();
380 |
--------------------------------------------------------------------------------