2020-09-29 13:19:45 +00:00
|
|
|
<h1>A colour palette in plain HTML</h1>
|
|
|
|
|
2022-09-07 13:44:23 +00:00
|
|
|
<label>
|
|
|
|
<span>Background Colour:</span>
|
|
|
|
<input value="#f2f2f3" type="color" id="background-color"></input>
|
|
|
|
</label>
|
|
|
|
|
2020-09-29 13:19:45 +00:00
|
|
|
<section class="box">
|
|
|
|
A template colour palette written as a self-contained HTML file.
|
|
|
|
New palettes can be created easily by copying the file and changing a few CSS variables.
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<!-- TODO: Change the base hue and offset here -->
|
2022-09-07 12:38:27 +00:00
|
|
|
<section style="--base-hue: 280deg; --saturation: 50%; --offset: calc(360deg / 6)">
|
2020-09-29 13:19:45 +00:00
|
|
|
<h2>Primary</h2>
|
|
|
|
<!-- Only override values here for slight tweaking -->
|
2022-10-11 07:36:12 +00:00
|
|
|
<colour-palette name="primary" style="--hue: var(--base-hue);"></colour-palette>
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2020-09-30 06:58:05 +00:00
|
|
|
<section class="hint box">
|
2020-09-29 13:19:45 +00:00
|
|
|
Modify as needed by changing the <code>--hue</code> attribute in the inlined CSS.<br>
|
|
|
|
Usage of a separate color picker tool for this step might be preferable.<br>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<h2>Secondary</h2>
|
2022-10-11 07:36:12 +00:00
|
|
|
<colour-palette name="secondary" style="--hue: calc(var(--base-hue) + var(--offset));"></colour-palette>
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2020-09-30 06:58:05 +00:00
|
|
|
<section class="hint box">
|
2020-09-29 13:19:45 +00:00
|
|
|
You can copy any colour to the clipboard by just clicking on its box.
|
|
|
|
By default colours are copied as HSLA values.
|
|
|
|
This can be changed in the script block at the bottom of the document.
|
|
|
|
</section>
|
|
|
|
|
2020-09-30 06:58:05 +00:00
|
|
|
<h2>Tertiary</h2>
|
2022-10-11 07:36:12 +00:00
|
|
|
<colour-palette name="tertiary" style="--hue: calc(var(--base-hue) - var(--offset));"></colour-palette>
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2020-09-30 06:58:05 +00:00
|
|
|
<section class="hint box">
|
|
|
|
By making use of <code>calc</code> and <code>var</code>, most of the colour palette can be automated.
|
|
|
|
This makes it easier and quicker to make adjustments.
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<h2>Neutral</h2>
|
2022-10-11 07:36:12 +00:00
|
|
|
<colour-palette name="neutral" style="--hue: 210deg; --saturation: 5%;"></colour-palette>
|
2020-09-30 06:58:05 +00:00
|
|
|
|
|
|
|
<section class="hint box">
|
2020-09-29 13:19:45 +00:00
|
|
|
By making use of <code>calc</code> and <code>var</code>, most of the colour palette can be automated.
|
|
|
|
This makes it easier and quicker to make adjustments.
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
2022-10-10 20:15:42 +00:00
|
|
|
<button id="export">Export as GIMP Palette</button>
|
|
|
|
|
2020-09-29 13:19:45 +00:00
|
|
|
<!-- ======================================== -->
|
|
|
|
<!-- ===== STYLES AND APPLICATION LOGIC ===== -->
|
|
|
|
<!-- ======================================== -->
|
|
|
|
|
|
|
|
<style>
|
2022-09-07 12:38:27 +00:00
|
|
|
:root {
|
|
|
|
font-size: 1em;
|
|
|
|
color: hsl(220deg, 8%, 20%);
|
|
|
|
font-family: "Open Sans", sans-serif;
|
|
|
|
}
|
2022-09-07 13:44:23 +00:00
|
|
|
body.dark {
|
|
|
|
color: hsl(220deg, 8%, 80%);
|
|
|
|
}
|
2022-09-07 12:38:27 +00:00
|
|
|
|
|
|
|
* {
|
|
|
|
box-sizing: border-box;
|
|
|
|
}
|
|
|
|
|
|
|
|
code {
|
|
|
|
font-family: "Hack", monospace;
|
|
|
|
}
|
|
|
|
|
|
|
|
body {
|
|
|
|
display: flex;
|
|
|
|
flex-flow: column nowrap;
|
|
|
|
align-items: center;
|
|
|
|
background: hsl(200deg, 5%, 95%);
|
|
|
|
}
|
|
|
|
/* The following only does font size shenanigans! */
|
|
|
|
|
|
|
|
h1, h2 {
|
|
|
|
text-align: center;
|
|
|
|
font-weight: normal;
|
|
|
|
font-family: "Raleway", "Quicksand", serif;
|
|
|
|
}
|
|
|
|
|
|
|
|
h1 {
|
|
|
|
font-size: 2.4em;
|
|
|
|
--border: dotted .08em currentcolor;
|
|
|
|
border-bottom: var(--border);
|
|
|
|
border-top: var(---border);
|
|
|
|
width: calc((3rem + .6rem * 2) * 9);
|
|
|
|
text-align: center;
|
|
|
|
padding: .2em 0;
|
|
|
|
}
|
|
|
|
h2 {
|
|
|
|
font-size: 2em;
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:15:42 +00:00
|
|
|
colour-palette {
|
2022-09-07 12:38:27 +00:00
|
|
|
--separation2: .6em; /* Halved separation */
|
|
|
|
display: flex;
|
|
|
|
/* padding: var(--separation2); */
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:15:42 +00:00
|
|
|
colour-palette button {
|
2022-09-07 14:07:48 +00:00
|
|
|
--radius: 50%;
|
2022-09-07 12:38:27 +00:00
|
|
|
all: unset;
|
|
|
|
margin: var(--separation2);
|
2022-10-10 20:15:42 +00:00
|
|
|
background: hsl(var(--hue), var(--saturation, 50%), var(--lightness, 50%));
|
2022-09-07 12:38:27 +00:00
|
|
|
cursor: pointer;
|
2022-09-07 14:07:48 +00:00
|
|
|
border-radius: var(--radius);
|
2022-09-07 12:38:27 +00:00
|
|
|
overflow: hidden;
|
|
|
|
transition: box-shadow 0.3s;
|
2022-10-10 20:15:42 +00:00
|
|
|
} colour-palette button::before {
|
2022-09-07 12:38:27 +00:00
|
|
|
content: '';
|
2022-09-07 14:07:48 +00:00
|
|
|
border-radius: var(--radius);
|
2022-09-07 12:38:27 +00:00
|
|
|
background: hsl(var(--hue), var(--saturation, 50%), var(--lightness, 50%));
|
|
|
|
box-shadow: .1em .1em .8em hsla(calc(var(--hue) + 30deg), calc(100% - var(--lightness) - 10%), calc(var(--lightness) - 40%), .4) inset;
|
|
|
|
width: 3em;
|
|
|
|
height: 3em;
|
|
|
|
display: block;
|
|
|
|
transition: box-shadow 0.3s;
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:15:42 +00:00
|
|
|
colour-palette button:hover {
|
2022-09-07 12:38:27 +00:00
|
|
|
box-shadow: .4em .4em .6em #0004;
|
|
|
|
}
|
2022-10-10 20:15:42 +00:00
|
|
|
colour-palette button:hover::before {
|
2022-09-07 12:38:27 +00:00
|
|
|
box-shadow: none;
|
|
|
|
}
|
2022-10-10 20:15:42 +00:00
|
|
|
body.dark colour-palette button:hover {
|
2022-09-07 13:44:23 +00:00
|
|
|
box-shadow: .0em .0em .6em hsla(var(--hue), calc(var(--saturation) + 30%), 80%, 60%);
|
|
|
|
}
|
2022-09-07 12:38:27 +00:00
|
|
|
|
|
|
|
/* === Popup notification shenanigans === */
|
|
|
|
|
|
|
|
div.notification {
|
|
|
|
top: 0em;
|
|
|
|
border: 1px solid currentcolor;
|
|
|
|
background: hsla(0deg, 0%, 100%, .8);
|
|
|
|
font-size: 1em;
|
|
|
|
opacity: 0;
|
|
|
|
position: fixed;
|
|
|
|
padding: .4em; 1em;
|
|
|
|
font-weight: bold;
|
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* === Info Boxes === */
|
|
|
|
|
|
|
|
.box {
|
|
|
|
--radius: .3em;
|
|
|
|
width: calc((3rem + .6rem * 2) * 9);
|
|
|
|
font-style: italic;
|
|
|
|
border-radius: var(--radius);
|
|
|
|
background: hsla(200deg, 5%, 92%, 1);
|
|
|
|
box-shadow: .0em .1em .2em inset hsl(220deg, 8%, 80%);
|
|
|
|
padding: 1em 2em;
|
|
|
|
|
|
|
|
position: relative;
|
|
|
|
margin: 2em 0em;
|
|
|
|
}
|
|
|
|
|
2022-09-07 13:44:23 +00:00
|
|
|
body.dark .box {
|
|
|
|
background: hsla(200deg, 5%, 6%, 1);
|
|
|
|
box-shadow: .0em .1em .2em inset #0006;
|
|
|
|
}
|
|
|
|
|
2022-09-07 12:38:27 +00:00
|
|
|
.hint, .info { --color: hsla(calc( 280deg - calc(360deg / 6)), 50%, 50%, 1) }
|
|
|
|
|
|
|
|
.box::after {
|
|
|
|
display: block;
|
|
|
|
position: absolute;
|
|
|
|
left: 0;
|
|
|
|
top: 0;
|
|
|
|
height: 100%;
|
|
|
|
width: .4em;
|
|
|
|
background-color: var(--color, transparent);
|
|
|
|
border-radius: var(--radius) 0 0 var(--radius);
|
|
|
|
content: '';
|
|
|
|
}
|
|
|
|
|
|
|
|
.box.hint::before {
|
|
|
|
content: "Hint: ";
|
|
|
|
display: inline-block;
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
|
2022-10-10 20:15:42 +00:00
|
|
|
#export {
|
|
|
|
all: unset;
|
|
|
|
font-size: 1.2em;
|
|
|
|
cursor: pointer;
|
|
|
|
padding: .4em 1.2em;
|
|
|
|
border-radius: 1.8em;
|
|
|
|
font-weight: bold;
|
|
|
|
background-color: hsl(340deg, 50%, 40%);
|
|
|
|
color: hsl(210deg, 5%, 95%);
|
|
|
|
margin: .4em;
|
|
|
|
}
|
|
|
|
|
2022-09-07 12:38:27 +00:00
|
|
|
/* === Utility === */
|
|
|
|
|
|
|
|
[center] { text-align: center; }
|
2020-09-29 13:19:45 +00:00
|
|
|
</style>
|
|
|
|
|
2022-09-07 13:44:23 +00:00
|
|
|
<script type="module">
|
2022-09-07 12:38:27 +00:00
|
|
|
const showMessage = function(message, color, x, y) {
|
|
|
|
let box = document.createElement('div');
|
|
|
|
box.innerHTML = message;
|
|
|
|
box.classList.add('notification');
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2022-09-07 12:38:27 +00:00
|
|
|
if (x) box.style.left = x+"px";
|
|
|
|
if (y) box.style.top = y+"px";
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2022-09-07 12:38:27 +00:00
|
|
|
document.querySelector("body").appendChild(box);
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2022-10-11 07:15:53 +00:00
|
|
|
box.animate([
|
|
|
|
{ offset: 0, opacity: 1, transform: "translate(-50%, -10%)"},
|
|
|
|
{ offset: .3, opacity: 1 },
|
|
|
|
{ offset: 1, opacity: 0, transform: "translate(-50%, -200%)"},
|
|
|
|
], {
|
|
|
|
duration: 1e3
|
|
|
|
}).finished.then(() => box.remove())
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2022-10-11 07:15:53 +00:00
|
|
|
box.style.setProperty('color', color);
|
2022-09-07 12:38:27 +00:00
|
|
|
}
|
2020-09-29 13:19:45 +00:00
|
|
|
|
2022-09-07 13:44:23 +00:00
|
|
|
const register = (type, name, initial=undefined) => CSS.registerProperty({
|
|
|
|
name: `--${name}`,
|
|
|
|
syntax: `<${type}>`,
|
|
|
|
inherits: true,
|
|
|
|
initialValue: initial,
|
|
|
|
})
|
|
|
|
|
|
|
|
register("angle", "hue", "0deg")
|
|
|
|
register("percentage", "saturation", "100%")
|
|
|
|
register("percentage", "lightness", "50%")
|
|
|
|
|
|
|
|
const backgroundColourButton = document.getElementById("background-color")
|
2022-10-11 07:15:53 +00:00
|
|
|
|
2022-09-07 13:44:23 +00:00
|
|
|
backgroundColourButton.addEventListener("input", event => {
|
|
|
|
const rgb = backgroundColourButton.value
|
|
|
|
document.body.style.background = rgb
|
|
|
|
const channels = rgb
|
|
|
|
.match(/[0-9a-f][0-9a-f]/g)
|
|
|
|
.map(hex => parseInt(hex, 16))
|
|
|
|
.map(num => num / 255)
|
|
|
|
const luminosity = channels.reduce((a,b)=>a+b) / 3
|
|
|
|
if (luminosity < 0.4) {
|
|
|
|
document.body.classList.add("dark")
|
|
|
|
} else {
|
|
|
|
document.body.classList.remove("dark")
|
|
|
|
}
|
2022-09-07 12:38:27 +00:00
|
|
|
})
|
2022-10-10 20:15:42 +00:00
|
|
|
|
|
|
|
customElements.define("colour-palette", class extends HTMLElement {
|
|
|
|
get colours() {
|
|
|
|
return Array.from(this.querySelectorAll("button"))
|
|
|
|
.map(button => getComputedStyle(button).backgroundColor)
|
|
|
|
.map(colour => colour.match(/\d+/g))
|
2022-10-11 07:36:12 +00:00
|
|
|
.map((match, index) => `${match[0]} ${match[1]} ${match[2]} ${this.name}-${index+1}`)
|
2022-10-10 20:15:42 +00:00
|
|
|
}
|
2022-10-11 07:15:53 +00:00
|
|
|
|
|
|
|
connectedCallback() {
|
|
|
|
this.innerHTML = `
|
|
|
|
<button style="--lightness: 05%"></button>
|
|
|
|
<button style="--lightness: 15%"></button>
|
|
|
|
<button style="--lightness: 30%"></button>
|
|
|
|
<button style="--lightness: 40%"></button>
|
|
|
|
<button style="--lightness: 50%"></button>
|
|
|
|
<button style="--lightness: 60%"></button>
|
|
|
|
<button style="--lightness: 70%"></button>
|
|
|
|
<button style="--lightness: 80%"></button>
|
|
|
|
<button style="--lightness: 95%"></button>
|
|
|
|
`
|
|
|
|
this.querySelectorAll('button').forEach(button => {
|
|
|
|
button.addEventListener('click', event => {
|
|
|
|
const prop = name => getComputedStyle(button)
|
|
|
|
.getPropertyValue(name)
|
|
|
|
.replace(/^ *| *$/g, "")
|
|
|
|
const colour = `hsl(${prop("--hue")}, ${prop("--saturation")}, ${prop("--lightness")})`
|
|
|
|
navigator.clipboard.writeText(colour).then(() => {
|
|
|
|
const box = button.getBoundingClientRect()
|
|
|
|
showMessage(`Copied to Clipboard:<br>${colour}`, colour, box.x+box.width/2, box.y+box.height/2);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2022-10-11 07:36:12 +00:00
|
|
|
get name() {
|
|
|
|
return this.getAttribute("name") || "Unnamed"
|
|
|
|
}
|
2022-10-10 20:15:42 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const exportButton = document.querySelector("#export")
|
|
|
|
exportButton.addEventListener("click", event => {
|
|
|
|
const colours = Array.from(document.querySelectorAll("colour-palette")).map(palette => palette.colours)
|
2022-10-11 07:36:12 +00:00
|
|
|
const template = `GIMP Palette\nName: Palette\nColumns: ${colours.length}\n#\n${colours.map(c => c.join("\n")).join("\n#\n")}`
|
2022-10-10 20:15:42 +00:00
|
|
|
const link = document.createElement("a")
|
|
|
|
link.href = `data:text/plain;base64,${btoa(template)}`
|
|
|
|
link.download = "palette.gpl"
|
|
|
|
link.click()
|
|
|
|
})
|
2020-09-29 13:19:45 +00:00
|
|
|
</script>
|