.profile/templates/palette.html

306 lines
8.4 KiB
HTML

<h1>A colour palette in plain HTML</h1>
<label>
<span>Background Colour:</span>
<input value="#f2f2f3" type="color" id="background-color"></input>
</label>
<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 -->
<section style="--base-hue: 280deg; --saturation: 50%; --offset: calc(360deg / 6)">
<h2>Primary</h2>
<!-- Only override values here for slight tweaking -->
<colour-palette name="primary" style="--hue: var(--base-hue);"></colour-palette>
<section class="hint box">
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>
<colour-palette name="secondary" style="--hue: calc(var(--base-hue) + var(--offset));"></colour-palette>
<section class="hint box">
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>
<h2>Tertiary</h2>
<colour-palette name="tertiary" style="--hue: calc(var(--base-hue) - var(--offset));"></colour-palette>
<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>
<colour-palette name="neutral" style="--hue: 210deg; --saturation: 5%;"></colour-palette>
<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>
</section>
<button id="export">Export as GIMP Palette</button>
<!-- ======================================== -->
<!-- ===== STYLES AND APPLICATION LOGIC ===== -->
<!-- ======================================== -->
<style>
:root {
font-size: 1em;
color: hsl(220deg, 8%, 20%);
font-family: "Open Sans", sans-serif;
}
body.dark {
color: hsl(220deg, 8%, 80%);
}
* {
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;
}
colour-palette {
--separation2: .6em; /* Halved separation */
display: flex;
/* padding: var(--separation2); */
}
colour-palette button {
--radius: 50%;
all: unset;
margin: var(--separation2);
background: hsl(var(--hue), var(--saturation, 50%), var(--lightness, 50%));
cursor: pointer;
border-radius: var(--radius);
overflow: hidden;
transition: box-shadow 0.3s;
} colour-palette button::before {
content: '';
border-radius: var(--radius);
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;
}
colour-palette button:hover {
box-shadow: .4em .4em .6em #0004;
}
colour-palette button:hover::before {
box-shadow: none;
}
body.dark colour-palette button:hover {
box-shadow: .0em .0em .6em hsla(var(--hue), calc(var(--saturation) + 30%), 80%, 60%);
}
/* === 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;
}
body.dark .box {
background: hsla(200deg, 5%, 6%, 1);
box-shadow: .0em .1em .2em inset #0006;
}
.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;
}
#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;
}
/* === Utility === */
[center] { text-align: center; }
</style>
<script type="module">
const showMessage = function(message, color, x, y) {
let box = document.createElement('div');
box.innerHTML = message;
box.classList.add('notification');
if (x) box.style.left = x+"px";
if (y) box.style.top = y+"px";
document.querySelector("body").appendChild(box);
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())
box.style.setProperty('color', color);
}
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")
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")
}
})
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))
.map((match, index) => `${match[0]} ${match[1]} ${match[2]} ${this.name}-${index+1}`)
}
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);
});
});
});
}
get name() {
return this.getAttribute("name") || "Unnamed"
}
})
const exportButton = document.querySelector("#export")
exportButton.addEventListener("click", event => {
const colours = Array.from(document.querySelectorAll("colour-palette")).map(palette => palette.colours)
const template = `GIMP Palette\nName: Palette\nColumns: ${colours.length}\n#\n${colours.map(c => c.join("\n")).join("\n#\n")}`
const link = document.createElement("a")
link.href = `data:text/plain;base64,${btoa(template)}`
link.download = "palette.gpl"
link.click()
})
</script>