Created
February 3, 2026 21:31
-
-
Save roobie/87139aa7997494b7a0ff9fb3ace08291 to your computer and use it in GitHub Desktop.
colors.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>Live Editable Tech Palette with Randomizer</title> | |
| <!-- Coloris --> | |
| <link | |
| rel="stylesheet" | |
| href="https://cdn.jsdelivr.net/gh/mdbassit/Coloris@latest/dist/coloris.min.css" | |
| /> | |
| <script src="https://cdn.jsdelivr.net/gh/mdbassit/Coloris@latest/dist/coloris.min.js"></script> | |
| <!-- | |
| <link | |
| rel="stylesheet" | |
| href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" | |
| /> | |
| --> | |
| <style> | |
| :root { | |
| --bg: #211a3d; /* Background */ | |
| --fg: #fffeee; /* Foreground */ | |
| --accent1: #1d9ec2; /* Accent 1 */ | |
| --accent2: #b5be00; /* Accent 2 */ | |
| --text-dark: #111827; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| margin: 0; | |
| font-family: | |
| system-ui, | |
| -apple-system, | |
| BlinkMacSystemFont, | |
| "Segoe UI", | |
| sans-serif; | |
| background: var(--bg); | |
| color: var(--fg); | |
| } | |
| .app-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 1rem 2rem; | |
| border-bottom: 1px solid rgba(232, 241, 255, 0.14); | |
| background: linear-gradient(90deg, #020617, #0f172a); | |
| } | |
| .logo { | |
| font-weight: 600; | |
| letter-spacing: 0.14em; | |
| text-transform: uppercase; | |
| font-size: 0.9rem; | |
| color: var(--fg); | |
| } | |
| .nav-links { | |
| display: flex; | |
| gap: 1.5rem; | |
| font-size: 0.85rem; | |
| opacity: 0.9; | |
| } | |
| .nav-links a { | |
| color: var(--fg); | |
| text-decoration: none; | |
| } | |
| .nav-links a:hover { | |
| color: var(--accent1); | |
| } | |
| .button { | |
| display: inline-block; | |
| margin-top: 1rem; | |
| padding: 0.55rem 1.1rem; | |
| border-radius: 999px; | |
| font-size: 0.85rem; | |
| font-weight: 600; | |
| letter-spacing: 0.04em; | |
| text-transform: uppercase; | |
| border: none; | |
| cursor: pointer; | |
| transition: | |
| transform 0.08s ease, | |
| box-shadow 0.08s ease, | |
| background 0.12s ease; | |
| } | |
| .button-primary { | |
| background: linear-gradient(135deg, var(--accent1), var(--accent2)); | |
| color: #102a3a; | |
| box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35); | |
| } | |
| .button-primary:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45); | |
| } | |
| .button-secondary { | |
| background: transparent; | |
| color: var(--fg); | |
| border: 1px solid rgba(232, 241, 255, 0.55); | |
| } | |
| .hero { | |
| padding: 3rem 2rem 1.5rem; | |
| max-width: 1100px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: minmax(0, 2fr) minmax(0, 1.3fr); | |
| gap: 2.5rem; | |
| } | |
| .hero-kicker { | |
| font-size: 0.8rem; | |
| letter-spacing: 0.18em; | |
| text-transform: uppercase; | |
| opacity: 0.7; | |
| margin-bottom: 0.75rem; | |
| } | |
| .hero-title { | |
| font-size: 2.6rem; | |
| line-height: 1.1; | |
| margin: 0 0 1rem; | |
| font-weight: 700; | |
| } | |
| .hero-subtitle { | |
| max-width: 34rem; | |
| font-size: 0.98rem; | |
| opacity: 0.9; | |
| margin: 0 0 1.5rem; | |
| } | |
| .gradient-text { | |
| background: linear-gradient(120deg, var(--accent1), var(--accent2)); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .panel { | |
| background: rgba(15, 23, 42, 0.9); | |
| border-radius: 14px; | |
| padding: 1.3rem 1.4rem 1.1rem; | |
| box-shadow: 0 14px 40px rgba(0, 0, 0, 0.45); | |
| border: 1px solid rgba(232, 241, 255, 0.12); | |
| } | |
| .panel-title { | |
| font-size: 0.85rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.16em; | |
| opacity: 0.7; | |
| margin-bottom: 0.75rem; | |
| } | |
| .picker-row { | |
| display: grid; | |
| grid-template-columns: 0.6fr 1.2fr auto; | |
| gap: 0.5rem; | |
| align-items: center; | |
| margin-bottom: 0.7rem; | |
| } | |
| .picker-label { | |
| font-size: 0.8rem; | |
| opacity: 0.8; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| } | |
| .picker-input { | |
| width: 100%; | |
| padding: 0.45rem 0.6rem; | |
| border-radius: 8px; | |
| border: 1px solid rgba(148, 163, 184, 0.6); | |
| background: rgba(15, 23, 42, 0.95); | |
| color: var(--fg); | |
| font-size: 0.8rem; | |
| font-family: | |
| ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, | |
| "Liberation Mono", "Courier New", monospace; | |
| } | |
| .picker-random { | |
| padding: 0.35rem 0.7rem; | |
| border-radius: 999px; | |
| border: 1px solid rgba(148, 163, 184, 0.7); | |
| background: rgba(15, 23, 42, 0.9); | |
| color: var(--fg); | |
| font-size: 0.7rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| cursor: pointer; | |
| white-space: nowrap; | |
| } | |
| .picker-random:hover { | |
| background: rgba(30, 64, 175, 0.7); | |
| } | |
| .swatch-grid { | |
| max-width: 1100px; | |
| margin: 0 auto 2.5rem; | |
| padding: 0 2rem 0; | |
| display: grid; | |
| grid-template-columns: repeat(4, minmax(0, 1fr)); | |
| gap: 1.4rem; | |
| } | |
| .swatch { | |
| border-radius: 12px; | |
| padding: 1.1rem 1.2rem; | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.35); | |
| font-size: 0.8rem; | |
| } | |
| .swatch h2 { | |
| margin: 0 0 0.55rem; | |
| font-size: 0.75rem; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| opacity: 0.9; | |
| } | |
| .swatch p { | |
| margin: 0.15rem 0; | |
| opacity: 0.9; | |
| } | |
| .bg-1 { | |
| background: var(--bg); | |
| color: var(--fg); | |
| border: 1px solid rgba(232, 241, 255, 0.14); | |
| } | |
| .bg-2 { | |
| background: var(--fg); | |
| color: var(--text-dark); | |
| } | |
| .bg-3 { | |
| background: var(--accent1); | |
| color: #1f2933; | |
| } | |
| .bg-4 { | |
| background: var(--accent2); | |
| color: #043b3a; | |
| } | |
| .chip { | |
| display: inline-block; | |
| margin-top: 0.4rem; | |
| padding: 0.18rem 0.55rem; | |
| border-radius: 999px; | |
| font-size: 0.7rem; | |
| background: rgba(15, 23, 42, 0.68); | |
| } | |
| @media (max-width: 940px) { | |
| .hero { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| @media (max-width: 720px) { | |
| .app-header { | |
| padding-inline: 1.25rem; | |
| } | |
| .hero { | |
| padding-inline: 1.25rem; | |
| } | |
| .swatch-grid { | |
| padding-inline: 1.25rem; | |
| grid-template-columns: repeat(2, minmax(0, 1fr)); | |
| } | |
| .picker-row { | |
| grid-template-columns: 0.75fr 1.25fr auto; | |
| } | |
| } | |
| @media (max-width: 520px) { | |
| .hero-title { | |
| font-size: 2rem; | |
| } | |
| .swatch-grid { | |
| grid-template-columns: 1fr; | |
| } | |
| .picker-row { | |
| grid-template-columns: 1fr; | |
| } | |
| .picker-random { | |
| justify-self: flex-start; | |
| } | |
| } | |
| #export-code pre { | |
| background: rgba(15, 23, 42, 0.95); | |
| border: 1px solid rgba(148, 163, 184, 0.6); | |
| border-radius: 8px; | |
| padding: 1rem; | |
| overflow-x: auto; | |
| font-family: | |
| ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, | |
| "Liberation Mono", "Courier New", monospace; | |
| font-size: 0.8rem; | |
| color: var(--fg); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="app-header"> | |
| <div class="logo">YourTech</div> | |
| <nav class="nav-links"> | |
| <a href="#">Product</a> | |
| <a href="#">Pricing</a> | |
| <a href="#">Docs</a> | |
| </nav> | |
| <button class="button button-primary">Get Started</button> | |
| </header> | |
| <section class="hero"> | |
| <div> | |
| <div class="hero-kicker">Palette Playground</div> | |
| <h1 class="hero-title"> | |
| Design a | |
| <span class="gradient-text">soft‑bold tech identity</span> | |
| </h1> | |
| <p class="hero-subtitle"> | |
| Tweak your background, foreground, and dual accents live. The hero, | |
| buttons, and swatches update instantly to show how the palette behaves | |
| in a real interface. | |
| </p> | |
| <button class="button button-primary">See It in Action</button> | |
| <button class="button button-secondary" style="margin-left: 0.75rem"> | |
| Secondary Action | |
| </button> | |
| </div> | |
| <aside class="panel"> | |
| <div class="panel-title">Adjust Colors</div> | |
| <div class="picker-row"> | |
| <span class="picker-label">Background</span> | |
| <input | |
| id="bg-picker" | |
| class="picker-input coloris" | |
| data-coloris | |
| value="#211a3d" | |
| /> | |
| <button type="button" class="picker-random" data-target="bg-picker"> | |
| Randomize | |
| </button> | |
| </div> | |
| <div class="picker-row"> | |
| <span class="picker-label">Foreground</span> | |
| <input | |
| id="fg-picker" | |
| class="picker-input coloris" | |
| data-coloris | |
| value="#fffeee" | |
| /> | |
| <button type="button" class="picker-random" data-target="fg-picker"> | |
| Randomize | |
| </button> | |
| </div> | |
| <div class="picker-row"> | |
| <span class="picker-label">Accent 1</span> | |
| <input | |
| id="accent1-picker" | |
| class="picker-input coloris" | |
| data-coloris | |
| value="#1d9ec2" | |
| /> | |
| <button | |
| type="button" | |
| class="picker-random" | |
| data-target="accent1-picker" | |
| > | |
| Randomize | |
| </button> | |
| </div> | |
| <div class="picker-row"> | |
| <span class="picker-label">Accent 2</span> | |
| <input | |
| id="accent2-picker" | |
| class="picker-input coloris" | |
| data-coloris | |
| value="#b5be00" | |
| /> | |
| <button | |
| type="button" | |
| class="picker-random" | |
| data-target="accent2-picker" | |
| > | |
| Randomize | |
| </button> | |
| </div> | |
| <button type="button" id="export-button" class="export-button"> | |
| Export CSS | |
| </button> | |
| </aside> | |
| </section> | |
| <main class="swatch-grid"> | |
| <section class="swatch bg-1"> | |
| <h2>Background</h2> | |
| <p><strong>Var:</strong> --bg</p> | |
| <p><strong>Use:</strong> App shell, hero, dark sections.</p> | |
| <span class="chip">Text & UI on background</span> | |
| </section> | |
| <section class="swatch bg-2"> | |
| <h2>Foreground</h2> | |
| <p><strong>Var:</strong> --fg</p> | |
| <p><strong>Use:</strong> Cards, light panels, primary text.</p> | |
| <span class="chip" style="background: var(--bg); color: var(--fg)"> | |
| Foreground on dark | |
| </span> | |
| </section> | |
| <section class="swatch bg-3"> | |
| <h2>Accent 1</h2> | |
| <p><strong>Var:</strong> --accent1</p> | |
| <p><strong>Use:</strong> Primary CTAs, key highlights.</p> | |
| <span class="chip" style="background: var(--bg); color: var(--accent1)"> | |
| Warm accent on dark | |
| </span> | |
| </section> | |
| <section class="swatch bg-4"> | |
| <h2>Accent 2</h2> | |
| <p><strong>Var:</strong> --accent2</p> | |
| <p><strong>Use:</strong> Secondary CTAs, charts, tags.</p> | |
| <span class="chip" style="background: var(--bg); color: var(--accent2)"> | |
| Cool accent on dark | |
| </span> | |
| </section> | |
| </main> | |
| <div id="export-code" class="panel" style="display: none; margin-top: 2rem"> | |
| <div class="panel-title">Exported CSS</div> | |
| <pre><code id="css-code"></code></pre> | |
| <button | |
| id="close-export" | |
| class="button button-secondary" | |
| style="margin-top: 1rem" | |
| > | |
| Close | |
| </button> | |
| </div> | |
| <script> | |
| // Coloris config | |
| Coloris.setInstance(".coloris", { | |
| theme: "large", | |
| alpha: false, | |
| formatToggle: true, | |
| closeButton: true, | |
| clearButton: false, | |
| }); | |
| const root = document.documentElement; | |
| const bindings = [ | |
| { id: "bg-picker", var: "--bg" }, | |
| { id: "fg-picker", var: "--fg" }, | |
| { id: "accent1-picker", var: "--accent1" }, | |
| { id: "accent2-picker", var: "--accent2" }, | |
| ]; | |
| function hexToRgb(hex) { | |
| let h = hex.trim(); | |
| if (h.startsWith("#")) h = h.slice(1); | |
| if (h.length === 3) { | |
| h = h | |
| .split("") | |
| .map((c) => c + c) | |
| .join(""); | |
| } | |
| if (h.length !== 6) return null; | |
| const r = parseInt(h.slice(0, 2), 16); | |
| const g = parseInt(h.slice(2, 4), 16); | |
| const b = parseInt(h.slice(4, 6), 16); | |
| if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null; | |
| return { r, g, b }; | |
| } | |
| function rgbToHex(r, g, b) { | |
| const toHex = (v) => v.toString(16).padStart(2, "0"); | |
| return "#" + toHex(r) + toHex(g) + toHex(b); | |
| } | |
| // Randomize within ±10% of the current channel value, clamped to [0,255] | |
| function randomNearHex(hex) { | |
| const rgb = hexToRgb(hex); | |
| if (!rgb) return hex; | |
| const jitterChannel = (val) => { | |
| const delta = Math.max(1, Math.round(val * 0.1)); // 10% of channel | |
| const min = Math.max(0, val - delta); | |
| const max = Math.min(255, val + delta); | |
| return Math.round(min + Math.random() * (max - min)); | |
| }; | |
| const r = jitterChannel(rgb.r); | |
| const g = jitterChannel(rgb.g); | |
| const b = jitterChannel(rgb.b); | |
| return rgbToHex(r, g, b); | |
| } | |
| // Bind inputs to CSS variables | |
| bindings.forEach(({ id, var: cssVar }) => { | |
| const input = document.getElementById(id); | |
| if (!input) return; | |
| const updateVar = () => { | |
| const value = input.value.trim(); | |
| if (!value) return; | |
| root.style.setProperty(cssVar, value); | |
| }; | |
| input.addEventListener("input", updateVar); | |
| input.addEventListener("change", updateVar); | |
| }); | |
| // Randomize buttons | |
| document.querySelectorAll(".picker-random").forEach((btn) => { | |
| const targetId = btn.getAttribute("data-target"); | |
| const binding = bindings.find((b) => b.id === targetId); | |
| if (!binding) return; | |
| const input = document.getElementById(binding.id); | |
| btn.addEventListener("click", () => { | |
| const current = input.value || "#000000"; | |
| const next = randomNearHex(current); | |
| input.value = next; | |
| root.style.setProperty(binding.var, next); | |
| // Trigger Coloris preview update if open | |
| const ev = new Event("input", { bubbles: true }); | |
| input.dispatchEvent(ev); | |
| }); | |
| }); | |
| // Export CSS functionality | |
| const exportButton = document.getElementById("export-button"); | |
| const exportCodeDiv = document.getElementById("export-code"); | |
| const cssCodeEl = document.getElementById("css-code"); | |
| const closeExportBtn = document.getElementById("close-export"); | |
| exportButton.addEventListener("click", () => { | |
| // Get current values from CSS variables | |
| const bg = getComputedStyle(root).getPropertyValue("--bg").trim(); | |
| const fg = getComputedStyle(root).getPropertyValue("--fg").trim(); | |
| const accent1 = getComputedStyle(root) | |
| .getPropertyValue("--accent1") | |
| .trim(); | |
| const accent2 = getComputedStyle(root) | |
| .getPropertyValue("--accent2") | |
| .trim(); | |
| // Generate CSS string | |
| const css = `:root { | |
| --bg: ${bg}; | |
| --fg: ${fg}; | |
| --accent1: ${accent1}; | |
| --accent2: ${accent2}; | |
| }`; | |
| // Populate and show the code block | |
| cssCodeEl.textContent = css; | |
| exportCodeDiv.style.display = "block"; | |
| // Optional: Scroll to the code block | |
| exportCodeDiv.scrollIntoView({ behavior: "smooth" }); | |
| }); | |
| // Close button | |
| closeExportBtn.addEventListener("click", () => { | |
| exportCodeDiv.style.display = "none"; | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment