├── 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 | ![Captura de pantalla 2023-01-04 a la(s) 2 07 48](https://user-images.githubusercontent.com/77374408/210488563-3177fd47-48c4-4b63-bc19-7c3271cac66d.jpg) 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 |
21 |

Hex

22 |
23 | 24 | 25 |
26 |
27 | 28 | Hue 29 | 38 | Brightness 39 | 48 | Saturation 49 | 58 |
59 |
60 |
61 |

Hex

62 |
63 | 64 | 65 |
66 |
67 | 68 | Hue 69 | 78 | Brightness 79 | 88 | Saturation 89 | 98 |
99 |
100 |
101 |

Hex

102 |
103 | 104 | 105 |
106 |
107 | 108 | Hue 109 | 118 | Brightness 119 | 128 | Saturation 129 | 138 |
139 |
140 |
141 |

Hex

142 |
143 | 144 | 145 |
146 |
147 | 148 | Hue 149 | 158 | Brightness 159 | 168 | Saturation 169 | 178 |
179 |
180 |
181 |

Hex

182 |
183 | 184 | 185 |
186 |
187 | 188 | Hue 189 | 198 | Brightness 199 | 208 | Saturation 209 | 218 |
219 |
220 |
221 |
222 |
223 | 224 |

Library

225 |
226 |
227 | 228 |

Generate

229 |
230 |
231 | 232 |

Save

233 |
234 |
235 |
236 |
237 |

Copied to clipboard!

238 |

👍

239 |
240 |
241 |
242 |
243 | 244 |

Give a name to your pallete

245 | 246 | 247 |
248 |
249 |
250 |
251 | 252 |

Pick your palette

253 |
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 | --------------------------------------------------------------------------------