Add colours generator.

This commit is contained in:
emil28092005
2026-05-09 13:30:43 +03:00
parent b1db390c06
commit 6a8630a00d
3 changed files with 198 additions and 1 deletions
@@ -0,0 +1,134 @@
---
---
<div id="color-generator" class="mt-8">
<!-- Swatch -->
<div class="flex justify-center mb-8">
<div
id="cg-swatch"
class="w-44 h-44 rounded-2xl border border-zinc-800"
style="background-color: #1f1f22; transition: background-color 0.35s cubic-bezier(0.4,0,0.2,1);"
aria-hidden="true"
></div>
</div>
<!-- Format rows -->
<div
id="cg-values"
class="mb-8 divide-y divide-zinc-800 border border-zinc-800 rounded-xl overflow-hidden opacity-0"
style="transition: opacity 0.25s ease;"
aria-live="polite"
>
{[
{ id: 'cg-hex', label: 'HEX' },
{ id: 'cg-rgb', label: 'RGB' },
{ id: 'cg-hsl', label: 'HSL' },
].map(({ id, label }) => (
<div class="flex items-center gap-3 px-4 py-3 bg-zinc-900/40">
<span class="w-10 text-xs font-semibold uppercase tracking-widest text-zinc-500 shrink-0">
{label}
</span>
<span
id={id}
class="flex-1 font-mono text-sm text-zinc-100 tabular-nums select-all"
></span>
<button
id={`${id}-copy`}
type="button"
aria-label={`Copy ${label} value`}
class="group shrink-0 p-1 rounded text-zinc-600 hover:text-zinc-300 focus:outline-none focus-visible:ring-1 focus-visible:ring-[#534AB7] transition-colors cursor-pointer"
>
<svg id={`${id}-copy-icon`} xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
</svg>
<svg id={`${id}-check-icon`} class="hidden text-[#534AB7]" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 6 9 17l-5-5"/>
</svg>
</button>
</div>
))}
</div>
<!-- Generate button -->
<div class="flex justify-center">
<button
id="cg-btn"
type="button"
class="w-full sm:w-auto px-8 py-3 bg-[#534AB7] text-white font-semibold rounded-lg hover:bg-[#4740a0] active:bg-[#3d3990] focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950 transition-colors duration-100 cursor-pointer"
>
Generate
</button>
</div>
</div>
<script>
const swatch = document.getElementById('cg-swatch') as HTMLDivElement;
const valuesEl = document.getElementById('cg-values') as HTMLDivElement;
const hexEl = document.getElementById('cg-hex') as HTMLSpanElement;
const rgbEl = document.getElementById('cg-rgb') as HTMLSpanElement;
const hslEl = document.getElementById('cg-hsl') as HTMLSpanElement;
const btn = document.getElementById('cg-btn') as HTMLButtonElement;
function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
const rn = r / 255, gn = g / 255, bn = b / 255;
const max = Math.max(rn, gn, bn), min = Math.min(rn, gn, bn);
const l = (max + min) / 2;
if (max === min) return [0, 0, Math.round(l * 100)];
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h = 0;
if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
else if (max === gn) h = ((bn - rn) / d + 2) / 6;
else h = ((rn - gn) / d + 4) / 6;
return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
}
function toHex(n: number) {
return n.toString(16).padStart(2, '0');
}
function makeCopyHandler(el: HTMLSpanElement, copyIconId: string, checkIconId: string) {
const copyIcon = document.getElementById(copyIconId)!;
const checkIcon = document.getElementById(checkIconId)!;
let timer: ReturnType<typeof setTimeout> | null = null;
return async () => {
const value = el.textContent?.trim();
if (!value) return;
await navigator.clipboard.writeText(value);
copyIcon.classList.add('hidden');
checkIcon.classList.remove('hidden');
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
checkIcon.classList.add('hidden');
copyIcon.classList.remove('hidden');
}, 1500);
};
}
document.getElementById('cg-hex-copy')!
.addEventListener('click', makeCopyHandler(hexEl, 'cg-hex-copy-icon', 'cg-hex-check-icon'));
document.getElementById('cg-rgb-copy')!
.addEventListener('click', makeCopyHandler(rgbEl, 'cg-rgb-copy-icon', 'cg-rgb-check-icon'));
document.getElementById('cg-hsl-copy')!
.addEventListener('click', makeCopyHandler(hslEl, 'cg-hsl-copy-icon', 'cg-hsl-check-icon'));
function generate() {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
const [h, s, l] = rgbToHsl(r, g, b);
const hex = `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
swatch.style.backgroundColor = hex;
hexEl.textContent = hex;
rgbEl.textContent = `rgb(${r}, ${g}, ${b})`;
hslEl.textContent = `hsl(${h}, ${s}%, ${l}%)`;
valuesEl.style.opacity = '1';
}
btn.addEventListener('click', generate);
</script>
+1 -1
View File
@@ -19,7 +19,7 @@ export const generators: Generator[] = [
title: 'Color',
description: 'Generate a random color in HEX, RGB, or HSL format.',
icon: 'palette',
status: 'coming-soon',
status: 'live',
},
{
slug: 'password',
+63
View File
@@ -0,0 +1,63 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import ColorGenerator from '../../components/generators/ColorGenerator.astro';
import AdBanner from '../../components/AdBanner.astro';
import { generators } from '../../data/generators';
const generator = generators.find((g) => g.slug === 'colors')!;
---
<BaseLayout
title={`${generator.title} Generator — Randify`}
description={generator.description}
>
<div class="max-w-2xl mx-auto px-4 py-12 sm:py-20">
<nav aria-label="Breadcrumb" class="mb-10">
<a
href="/"
class="inline-flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-200 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[#534AB7] rounded"
aria-label="Back to all generators"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
>
<path d="m15 18-6-6 6-6"/>
</svg>
All generators
</a>
</nav>
<header class="mb-2">
<div class="flex items-center gap-2 mb-4">
<span
class="inline-block w-2.5 h-2.5 rounded-full bg-[#534AB7]"
aria-hidden="true"
></span>
<span class="text-sm font-medium text-zinc-400 uppercase tracking-widest">randify</span>
</div>
<h1 class="text-3xl sm:text-4xl font-bold text-zinc-100">
{generator.title} Generator
</h1>
<p class="mt-2 text-base text-zinc-400">{generator.description}</p>
</header>
<main>
<ColorGenerator />
</main>
<div class="mt-12 grid grid-cols-3 gap-3">
<AdBanner size="tile" />
<AdBanner size="tile" />
<AdBanner size="tile" />
</div>
</div>
</BaseLayout>