<title>Talia: A colour palette</title>

<h1>Talia: A colour palette</h1>

<section class="important info box">
	A general-purpose colour palette to use for whatever.
</section>

<label>
	<span>Background Colour:</span>
	<input value="#f2f2f3" type="color" id="background-color"></input>
</label>

<!-- TODO: Change the base hue and offset here -->
<section style="--base-hue: 280deg; --saturation: 40%; --offset: calc(360deg / 6)">
	<h2>Primary</h2>
	<colour-palette name="primary" style="--hue: calc(var(--base-hue) - var(--offset));"></colour-palette>

	<section class="info box">
		The main colour of the scheme. Should be used for the majority of the content.
	</section>

	<h2>Secondary</h2>
	<colour-palette name="secondary" style="--hue: var(--base-hue); --saturation: 30%;"></colour-palette>

	<section class="info box">
		A secondary colour to provide some contrast to the primary colour without drawing too much attention.
	</section>

	<h2>Contrast</h2>
	<colour-palette name="contrast" style="--hue: calc(var(--base-hue) + var(--offset)); --saturation: 50%;"></colour-palette>

	<section class="info box">
		This colour is intended to provide a lot more contrast to the primary colour for elements that require user attention.
	</section>

	<h2>Neutral</h2>
	<colour-palette name="neutral" style="--hue: 210deg; --saturation: 5%;"></colour-palette>

	<section class="info box">
		Just a more neutral scale of gray tones 
	</section>
</section>

<button id="export-gimp">Export as GIMP Palette</button>
<button id="export-css">Export as CSS Variables</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: hsl(220deg, 40%, 50%) }
	.important { --color: hsl(280deg, 30%, 50%) }

	.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;
	}

	button {
		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(340deg, 50%, 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 exportGimpButton = document.querySelector("#export-gimp")
	exportGimpButton.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.map(c=>c.join(' ')).join("\n")).join("\n#\n")}`
		const link = document.createElement("a")
		link.href = `data:text/plain;base64,${btoa(template)}`
		link.download = "palette.gpl"
		link.click()
	})

	const hexByte = byte => byte <= 16 ? `0${byte.toString(16)}` : byte.toString(16)

	const exportCSSButton = document.querySelector("#export-css")
	exportCSSButton.addEventListener("click", event => {
		const colours = Array.from(document.querySelectorAll("colour-palette"))
			.map(palette => palette.colours.map(colour => `--${colour[3]}: #${colour.slice(0,3).map(Number).map(hexByte).join('')};`))
		const template = `/*Talia Palette*/\n:root {\n${colours.map(p => p.map(c => "\t" + c).join("\n")).join("\n\n")}\n}`
		console.log(template)
		const link = document.createElement("a")
		link.href = `data:text/plain;base64,${btoa(template)}`
		link.download = "palette.css"
		link.click()
	})
</script>