Custom Code Experiment •
The Color API
Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Color API Tool</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body {
background: #000000;
color: #ffffff;
min-height: 100vh;
padding: 0.75rem;
line-height: 1.4;
font-size: 0.875rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.main-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 768px) {
.main-grid {
grid-template-columns: 1fr 1fr;
}
}
.card {
background: #111111;
border-radius: 6px;
padding: 1.25rem;
border: 1px solid #222222;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
transition: all 0.2s ease;
}
.card:hover {
border-color: #333333;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
.card h2 {
font-size: 1rem;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
color: #ffffff;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
padding-bottom: 0.75rem;
border-bottom: 1px solid #222222;
}
.card h2 i {
color: #0066ff;
}
.input-group {
margin-bottom: 0.75rem;
}
.input-group label {
display: block;
margin-bottom: 0.375rem;
font-size: 0.8rem;
color: #888888;
font-weight: 500;
}
.input-row {
display: flex;
gap: 0.5rem;
}
input, select, button {
padding: 0.5rem 0.75rem;
border-radius: 4px;
border: 1px solid #333333;
background: #1a1a1a;
color: #ffffff;
font-size: 0.85rem;
transition: all 0.2s;
}
input:focus, select:focus {
outline: none;
border-color: #0066ff;
box-shadow: 0 0 0 1px rgba(0, 102, 255, 0.2);
}
input {
flex: 1;
}
button {
background: #0066ff;
border: none;
color: white;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: all 0.2s;
}
button:hover {
background: #0052d4;
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.color-preview {
width: 100%;
height: 40px;
border-radius: 4px;
margin: 0.75rem 0;
border: 1px solid #333333;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
color: white;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
font-family: 'Monaco', 'Courier New', monospace;
font-size: 0.8rem;
}
.results {
margin-top: 1rem;
max-height: 250px;
overflow-y: auto;
}
.results h3 {
font-size: 0.9rem;
margin-bottom: 0.75rem;
color: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
}
.results h3 span {
font-size: 0.75rem;
color: #888888;
font-weight: normal;
}
.color-info {
background: #0a0a0a;
border-radius: 4px;
padding: 0.75rem;
margin-bottom: 1rem;
border: 1px solid #222222;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
font-size: 0.85rem;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
color: #888888;
width: 50px;
flex-shrink: 0;
font-weight: 500;
text-transform: uppercase;
font-size: 0.75rem;
}
.info-value {
font-weight: 500;
color: #ffffff;
display: flex;
justify-content: space-between;
align-items: center;
flex-grow: 1;
margin-left: 0.75rem;
}
.color-text {
font-family: 'Monaco', 'Courier New', monospace;
font-size: 0.8rem;
color: #e0e0e0;
}
.scheme-colors {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
gap: 0.375rem;
margin-top: 0.75rem;
}
.scheme-color {
height: 50px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.65rem;
font-weight: 600;
color: white;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
cursor: pointer;
transition: transform 0.2s, filter 0.2s;
border: 1px solid rgba(255, 255, 255, 0.1);
font-family: 'Monaco', 'Courier New', monospace;
}
.scheme-color:hover {
transform: scale(1.05);
filter: brightness(1.1);
}
.loading {
display: none;
text-align: center;
padding: 0.75rem;
color: #888888;
font-size: 0.8rem;
}
.loading i {
margin-right: 0.5rem;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.error {
background: rgba(255, 50, 50, 0.1);
border: 1px solid #ff3333;
color: #ff6666;
padding: 0.75rem;
border-radius: 4px;
margin-top: 0.75rem;
display: none;
font-size: 0.8rem;
}
.footer {
text-align: center;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid #222222;
color: #666666;
font-size: 0.75rem;
}
.footer a {
color: #0066ff;
text-decoration: none;
transition: color 0.2s;
}
.footer a:hover {
color: #0052d4;
text-decoration: underline;
}
.copy-btn {
background: #222222;
padding: 0.2rem 0.6rem;
font-size: 0.7rem;
border-radius: 3px;
cursor: pointer;
border: 1px solid #333333;
color: #ffffff;
transition: all 0.2s;
flex-shrink: 0;
width: 55px;
text-align: center;
font-weight: 500;
}
.copy-btn:hover {
background: #333333;
border-color: #444444;
}
.color-inputs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.color-inputs input {
text-align: center;
}
.color-format {
display: flex;
gap: 0.375rem;
margin-bottom: 0.75rem;
}
.format-btn {
flex: 1;
background: #1a1a1a;
text-align: center;
padding: 0.5rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
font-size: 0.8rem;
border: 1px solid #333333;
color: #888888;
font-weight: 500;
}
.format-btn.active {
background: #0066ff;
color: white;
border-color: #0066ff;
}
.format-btn:hover:not(.active) {
background: #222222;
color: #ffffff;
}
.color-value-display {
text-align: center;
font-weight: 600;
margin: 0.5rem 0;
font-size: 0.9rem;
color: #ffffff;
font-family: 'Monaco', 'Courier New', monospace;
background: #1a1a1a;
padding: 0.5rem;
border-radius: 4px;
border: 1px solid #222222;
}
.api-info {
font-size: 0.75rem;
color: #666666;
margin-top: 0.5rem;
line-height: 1.3;
}
.header {
text-align: center;
margin-bottom: 1.25rem;
padding-bottom: 1rem;
border-bottom: 1px solid #222222;
}
.header h1 {
font-size: 1.25rem;
background: linear-gradient(90deg, #0066ff, #00ccff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 0.25rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.header .subtitle {
color: #888888;
font-size: 0.8rem;
font-weight: 400;
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #1a1a1a;
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: #333333;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #444444;
}
/* Compact utility classes */
.compact-text {
font-size: 0.85rem;
}
.compact-padding {
padding: 0.5rem;
}
.compact-margin {
margin-bottom: 0.5rem;
}
/* Status indicators */
.status-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: #00cc00;
margin-right: 6px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Badges */
.badge {
display: inline-block;
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.7rem;
font-weight: 600;
background: #222222;
color: #888888;
margin-left: 0.5rem;
}
.badge.api {
background: #0066ff;
color: white;
}
/* Tooltip */
.tooltip {
position: relative;
cursor: help;
}
.tooltip:hover:after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: #222222;
color: #ffffff;
padding: 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
white-space: nowrap;
z-index: 100;
border: 1px solid #333333;
margin-bottom: 5px;
}
</style>
</head>
<body>
<div class="container">
<div class="main-grid">
<div class="card">
<h2><i class="fas fa-eye-dropper"></i> Colour Analysis <span class="badge">ID</span></h2>
<div class="color-format">
<div class="format-btn active" data-format="hex" data-tooltip="Hex color code">HEX</div>
<div class="format-btn" data-format="rgb" data-tooltip="Red, Green, Blue">RGB</div>
<div class="format-btn" data-format="hsl" data-tooltip="Hue, Saturation, Lightness">HSL</div>
<div class="format-btn" data-format="cmyk" data-tooltip="Cyan, Magenta, Yellow, Black">CMYK</div>
</div>
<div class="input-group">
<div class="color-inputs" id="colorInputs">
<div class="input-row">
<input type="text" id="hexInput" placeholder="e.g., 0047AB or #0047AB" value="0047AB">
</div>
</div>
</div>
<button id="identifyBtn">
<i class="fas fa-search"></i> Analyse Colour
</button>
<div class="color-preview" id="colorPreview" style="background-color: #0047AB;">
#0047AB
</div>
<div class="loading" id="identifyLoading">
<i class="fas fa-circle-notch"></i> Querying API...
</div>
<div class="error" id="identifyError"></div>
<div class="results" id="identifyResults">
<h3>Color Data <span id="colorName">Cobalt Blue</span></h3>
<div class="color-info">
<div class="info-row">
<span class="info-label">HEX</span>
<div class="info-value">
<span class="color-text">#0047AB</span>
<button class="copy-btn" data-value="#0047AB">Copy</button>
</div>
</div>
<div class="info-row">
<span class="info-label">RGB</span>
<div class="info-value">
<span class="color-text">rgb(0, 71, 171)</span>
<button class="copy-btn" data-value="rgb(0, 71, 171)">Copy</button>
</div>
</div>
<div class="info-row">
<span class="info-label">HSL</span>
<div class="info-value">
<span class="color-text">hsl(215, 100%, 34%)</span>
<button class="copy-btn" data-value="hsl(215, 100%, 34%)">Copy</button>
</div>
</div>
<div class="info-row">
<span class="info-label">CMYK</span>
<div class="info-value">
<span class="color-text">cmyk(100, 58, 0, 33)</span>
<button class="copy-btn" data-value="cmyk(100, 58, 0, 33)">Copy</button>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<h2><i class="fas fa-swatchbook"></i> Scheme Generator <span class="badge api">API</span></h2>
<div class="input-group">
<label for="seedColor">Base Colour</label>
<div class="input-row">
<input type="text" id="seedColor" placeholder="e.g., 0047AB or #0047AB" value="0047AB">
</div>
</div>
<div class="input-group">
<div class="input-row">
<div style="flex: 1;">
<label for="schemeMode">Scheme Type</label>
<select id="schemeMode">
<option value="monochrome">Monochrome</option>
<option value="monochrome-dark">Monochrome Dark</option>
<option value="monochrome-light">Monochrome Light</option>
<option value="analogic">Analogic</option>
<option value="complement">Complement</option>
<option value="analogic-complement">Analogic Complement</option>
<option value="triad">Triad</option>
<option value="quad">Quad</option>
</select>
</div>
<div style="flex: 1;">
<label for="schemeCount">Colors</label>
<select id="schemeCount">
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5" selected>5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
</select>
</div>
</div>
</div>
<button id="generateSchemeBtn">
<i class="fas fa-bolt"></i> Generate
</button>
<div class="loading" id="schemeLoading">
<i class="fas fa-circle-notch"></i> Processing...
</div>
<div class="error" id="schemeError"></div>
<div class="results" id="schemeResults">
<h3>Colour Palette <span id="schemeInfo">Analogic • 5 colors</span></h3>
<div class="color-value-display" id="schemeColorsDisplay">
#0047AB • #005CE0 • #267AFF • #669DFF • #A3C4FF
</div>
<div class="scheme-colors" id="schemeColors">
<div class="scheme-color" style="background-color: #0047AB;" data-value="#0047AB">#0047AB</div>
<div class="scheme-color" style="background-color: #005CE0;" data-value="#005CE0">#005CE0</div>
<div class="scheme-color" style="background-color: #267AFF;" data-value="#267AFF">#267AFF</div>
<div class="scheme-color" style="background-color: #669DFF;" data-value="#669DFF">#669DFF</div>
<div class="scheme-color" style="background-color: #A3C4FF;" data-value="#A3C4FF">#A3C4FF</div>
</div>
<p class="api-info">
<i class="fas fa-info-circle"></i> Color schemes generated via The Color API based on color theory relationships.
</p>
</div>
</div>
</div>
<div class="footer">
<p><i class="fas fa-code"></i> Built with <a href="https://www.thecolorapi.com" target="_blank">The Color API</a> • Created by <a href="https://github.com/andjosh" target="_blank">Josh Beckman</a></p>
</div>
</div>
<script>
const formatButtons = document.querySelectorAll('.format-btn');
const colorInputsContainer = document.getElementById('colorInputs');
const hexInput = document.getElementById('hexInput');
const identifyBtn = document.getElementById('identifyBtn');
const identifyLoading = document.getElementById('identifyLoading');
const identifyError = document.getElementById('identifyError');
const identifyResults = document.getElementById('identifyResults');
const colorPreview = document.getElementById('colorPreview');
const colorName = document.getElementById('colorName');
const colorInfo = document.querySelector('.color-info');
const seedColorInput = document.getElementById('seedColor');
const schemeMode = document.getElementById('schemeMode');
const schemeCount = document.getElementById('schemeCount');
const generateSchemeBtn = document.getElementById('generateSchemeBtn');
const schemeLoading = document.getElementById('schemeLoading');
const schemeError = document.getElementById('schemeError');
const schemeResults = document.getElementById('schemeResults');
const schemeColorsDisplay = document.getElementById('schemeColorsDisplay');
const schemeColorsContainer = document.getElementById('schemeColors');
const schemeInfo = document.getElementById('schemeInfo');
let currentFormat = 'hex';
let currentColor = '#0047AB';
document.addEventListener('DOMContentLoaded', function() {
updateColorPreview();
fetchColorData('hex=0047AB');
fetchColorScheme('hex=0047AB', 'analogic', 5);
setupEventListeners();
setupCopyButtons();
setupTooltips();
});
function setupEventListeners() {
formatButtons.forEach(btn => {
btn.addEventListener('click', function() {
const format = this.getAttribute('data-format');
switchColorFormat(format);
});
});
hexInput.addEventListener('input', updateColorPreview);
identifyBtn.addEventListener('click', identifyColor);
seedColorInput.addEventListener('input', function() {
const color = this.value.trim();
if (isValidColor(color, 'hex')) {
currentColor = normalizeHex(color);
updateColorPreview();
}
});
generateSchemeBtn.addEventListener('click', generateScheme);
}
function setupCopyButtons() {
document.addEventListener('click', function(e) {
// Check if the clicked element is a copy button or inside one
let copyBtn = e.target.closest('.copy-btn');
if (copyBtn) {
e.preventDefault();
const value = copyBtn.getAttribute('data-value');
if (value) {
copyToClipboard(value);
const originalText = copyBtn.textContent;
copyBtn.textContent = '✓';
copyBtn.style.background = '#00cc00';
copyBtn.style.borderColor = '#00cc00';
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.style.background = '';
copyBtn.style.borderColor = '';
}, 1500);
}
}
let schemeColor = e.target.closest('.scheme-color');
if (schemeColor) {
e.preventDefault();
const colorValue = schemeColor.getAttribute('data-value');
if (colorValue) {
copyToClipboard(colorValue);
const originalText = schemeColor.textContent;
const originalBg = schemeColor.style.backgroundColor;
schemeColor.textContent = '✓';
schemeColor.style.backgroundColor = '#00cc00';
setTimeout(() => {
schemeColor.textContent = originalText;
schemeColor.style.backgroundColor = originalBg;
}, 1500);
}
}
});
}
function setupTooltips() {
const tooltipElements = document.querySelectorAll('[data-tooltip]');
tooltipElements.forEach(el => {
el.addEventListener('mouseenter', function() {
// Tooltip is handled by CSS
});
});
}
function switchColorFormat(format) {
if (format === currentFormat) return;
formatButtons.forEach(btn => {
if (btn.getAttribute('data-format') === format) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
let hexValue, rgbValue, hslValue, cmykValue;
const infoRows = document.querySelectorAll('.info-row');
infoRows.forEach(row => {
const label = row.querySelector('.info-label').textContent;
const valueSpan = row.querySelector('.info-value');
const copyBtn = valueSpan.querySelector('.copy-btn');
const value = copyBtn ? copyBtn.getAttribute('data-value') : '';
if (label.includes('HEX')) hexValue = value;
if (label.includes('RGB')) rgbValue = value;
if (label.includes('HSL')) hslValue = value;
if (label.includes('CMYK')) cmykValue = value;
});
colorInputsContainer.innerHTML = '';
if (format === 'hex') {
const inputRow = document.createElement('div');
inputRow.className = 'input-row';
inputRow.innerHTML = `
<input type="text" id="hexInput" placeholder="e.g., 0047AB or #0047AB" value="${hexValue ? hexValue.replace('#', '') : '0047AB'}">
`;
colorInputsContainer.appendChild(inputRow);
// Add event listener
const newHexInput = document.getElementById('hexInput');
newHexInput.addEventListener('input', updateColorPreview);
}
else if (format === 'rgb') {
let r = '0', g = '71', b = '171';
if (rgbValue) {
const rgbValues = rgbValue.replace('rgb(', '').replace(')', '').split(',');
r = rgbValues[0].trim();
g = rgbValues[1].trim();
b = rgbValues[2].trim();
}
const inputRow = document.createElement('div');
inputRow.className = 'input-row';
inputRow.innerHTML = `
<input type="number" min="0" max="255" placeholder="R" value="${r}" class="rgb-input">
<input type="number" min="0" max="255" placeholder="G" value="${g}" class="rgb-input">
<input type="number" min="0" max="255" placeholder="B" value="${b}" class="rgb-input">
`;
colorInputsContainer.appendChild(inputRow);
const rgbInputs = document.querySelectorAll('.rgb-input');
rgbInputs.forEach(input => {
input.addEventListener('input', updateColorPreviewFromRGB);
});
}
else if (format === 'hsl') {
let h = '215', s = '100', l = '34';
if (hslValue) {
const hslValues = hslValue.replace('hsl(', '').replace(')', '').replace(/%/g, '').split(',');
h = hslValues[0].trim();
s = hslValues[1].trim();
l = hslValues[2].trim();
}
const inputRow = document.createElement('div');
inputRow.className = 'input-row';
inputRow.innerHTML = `
<input type="number" min="0" max="360" placeholder="H" value="${h}" class="hsl-input">
<input type="number" min="0" max="100" placeholder="S%" value="${s}" class="hsl-input">
<input type="number" min="0" max="100" placeholder="L%" value="${l}" class="hsl-input">
`;
colorInputsContainer.appendChild(inputRow);
const hslInputs = document.querySelectorAll('.hsl-input');
hslInputs.forEach(input => {
input.addEventListener('input', updateColorPreviewFromHSL);
});
}
else if (format === 'cmyk') {
let c = '100', m = '58', y = '0', k = '33';
if (cmykValue) {
const cmykValues = cmykValue.replace('cmyk(', '').replace(')', '').split(',');
c = cmykValues[0].trim();
m = cmykValues[1].trim();
y = cmykValues[2].trim();
k = cmykValues[3].trim();
}
const inputRow = document.createElement('div');
inputRow.className = 'input-row';
inputRow.innerHTML = `
<input type="number" min="0" max="100" placeholder="C%" value="${c}" class="cmyk-input">
<input type="number" min="0" max="100" placeholder="M%" value="${m}" class="cmyk-input">
<input type="number" min="0" max="100" placeholder="Y%" value="${y}" class="cmyk-input">
<input type="number" min="0" max="100" placeholder="K%" value="${k}" class="cmyk-input">
`;
colorInputsContainer.appendChild(inputRow);
const cmykInputs = document.querySelectorAll('.cmyk-input');
cmykInputs.forEach(input => {
input.addEventListener('input', updateColorPreviewFromCMYK);
});
}
currentFormat = format;
}
function updateColorPreview() {
const hexInput = document.getElementById('hexInput');
if (!hexInput) return;
let hex = hexInput.value.trim();
hex = normalizeHex(hex);
if (isValidColor(hex, 'hex')) {
colorPreview.style.backgroundColor = hex;
colorPreview.textContent = hex;
currentColor = hex;
}
}
function updateColorPreviewFromRGB() {
const rgbInputs = document.querySelectorAll('.rgb-input');
const r = parseInt(rgbInputs[0].value) || 0;
const g = parseInt(rgbInputs[1].value) || 0;
const b = parseInt(rgbInputs[2].value) || 0;
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
const hex = rgbToHex(r, g, b);
colorPreview.style.backgroundColor = hex;
colorPreview.textContent = hex;
currentColor = hex;
}
}
function updateColorPreviewFromHSL() {
const hslInputs = document.querySelectorAll('.hsl-input');
const h = parseInt(hslInputs[0].value) || 0;
const s = parseInt(hslInputs[1].value) || 0;
const l = parseInt(hslInputs[2].value) || 0;
if (h >= 0 && h <= 360 && s >= 0 && s <= 100 && l >= 0 && l <= 100) {
const hex = hslToHex(h, s, l);
colorPreview.style.backgroundColor = hex;
colorPreview.textContent = hex;
currentColor = hex;
}
}
function updateColorPreviewFromCMYK() {
const cmykInputs = document.querySelectorAll('.cmyk-input');
const c = parseInt(cmykInputs[0].value) || 0;
const m = parseInt(cmykInputs[1].value) || 0;
const y = parseInt(cmykInputs[2].value) || 0;
const k = parseInt(cmykInputs[3].value) || 0;
if (c >= 0 && c <= 100 && m >= 0 && m <= 100 && y >= 0 && y <= 100 && k >= 0 && k <= 100) {
const hex = cmykToHex(c, m, y, k);
colorPreview.style.backgroundColor = hex;
colorPreview.textContent = hex;
currentColor = hex;
}
}
function identifyColor() {
let apiParam = '';
if (currentFormat === 'hex') {
const hexInput = document.getElementById('hexInput');
const hex = hexInput.value.trim();
if (!isValidColor(hex, 'hex')) {
showError(identifyError, 'Please enter a valid HEX color (e.g., 0047AB or #0047AB)');
return;
}
apiParam = `hex=${normalizeHex(hex).replace('#', '')}`;
}
else if (currentFormat === 'rgb') {
const rgbInputs = document.querySelectorAll('.rgb-input');
const r = parseInt(rgbInputs[0].value) || 0;
const g = parseInt(rgbInputs[1].value) || 0;
const b = parseInt(rgbInputs[2].value) || 0;
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
showError(identifyError, 'RGB values must be between 0 and 255');
return;
}
apiParam = `rgb=${r},${g},${b}`;
}
else if (currentFormat === 'hsl') {
const hslInputs = document.querySelectorAll('.hsl-input');
const h = parseInt(hslInputs[0].value) || 0;
const s = parseInt(hslInputs[1].value) || 0;
const l = parseInt(hslInputs[2].value) || 0;
if (h < 0 || h > 360 || s < 0 || s > 100 || l < 0 || l > 100) {
showError(identifyError, 'H must be 0-360, S and L must be 0-100');
return;
}
apiParam = `hsl=${h},${s}%,${l}%`;
}
else if (currentFormat === 'cmyk') {
const cmykInputs = document.querySelectorAll('.cmyk-input');
const c = parseInt(cmykInputs[0].value) || 0;
const m = parseInt(cmykInputs[1].value) || 0;
const y = parseInt(cmykInputs[2].value) || 0;
const k = parseInt(cmykInputs[3].value) || 0;
if (c < 0 || c > 100 || m < 0 || m > 100 || y < 0 || y > 100 || k < 0 || k > 100) {
showError(identifyError, 'CMYK values must be between 0 and 100');
return;
}
apiParam = `cmyk=${c},${m},${y},${k}`;
}
fetchColorData(apiParam);
}
function generateScheme() {
const seedColor = seedColorInput.value.trim();
const mode = schemeMode.value;
const count = parseInt(schemeCount.value);
if (!isValidColor(seedColor, 'hex')) {
showError(schemeError, 'Please enter a valid HEX color for the base color');
return;
}
const hex = normalizeHex(seedColor).replace('#', '');
fetchColorScheme(`hex=${hex}`, mode, count);
}
async function fetchColorData(apiParam) {
identifyLoading.style.display = 'block';
identifyError.style.display = 'none';
identifyResults.style.display = 'none';
try {
const response = await fetch(`https://www.thecolorapi.com/id?${apiParam}&format=json`);
if (!response.ok) throw new Error('Failed to fetch color data');
const data = await response.json();
displayColorData(data);
} catch (error) {
showError(identifyError, `Error: ${error.message}`);
} finally {
identifyLoading.style.display = 'none';
}
}
async function fetchColorScheme(apiParam, mode, count) {
schemeLoading.style.display = 'block';
schemeError.style.display = 'none';
try {
const response = await fetch(`https://www.thecolorapi.com/scheme?${apiParam}&mode=${mode}&count=${count}&format=json`);
if (!response.ok) throw new Error('Failed to fetch color scheme');
const data = await response.json();
displayColorScheme(data, mode, count);
} catch (error) {
showError(schemeError, `Error: ${error.message}`);
} finally {
schemeLoading.style.display = 'none';
}
}
function displayColorData(data) {
colorName.textContent = data.name.value || 'Unknown Color';
// Update color preview
const hex = data.hex.value;
colorPreview.style.backgroundColor = hex;
colorPreview.textContent = hex;
currentColor = hex;
// Update info rows
const colorInfoHTML = `
<div class="info-row">
<span class="info-label">HEX</span>
<div class="info-value">
<span class="color-text">${data.hex.value}</span>
<button class="copy-btn" data-value="${data.hex.value}">Copy</button>
</div>
</div>
<div class="info-row">
<span class="info-label">RGB</span>
<div class="info-value">
<span class="color-text">rgb(${data.rgb.r}, ${data.rgb.g}, ${data.rgb.b})</span>
<button class="copy-btn" data-value="rgb(${data.rgb.r}, ${data.rgb.g}, ${data.rgb.b})">Copy</button>
</div>
</div>
<div class="info-row">
<span class="info-label">HSL</span>
<div class="info-value">
<span class="color-text">hsl(${data.hsl.h}, ${data.hsl.s}%, ${data.hsl.l}%)</span>
<button class="copy-btn" data-value="hsl(${data.hsl.h}, ${data.hsl.s}%, ${data.hsl.l}%)">Copy</button>
</div>
</div>
<div class="info-row">
<span class="info-label">CMYK</span>
<div class="info-value">
<span class="color-text">cmyk(${data.cmyk.c}%, ${data.cmyk.m}%, ${data.cmyk.y}%, ${data.cmyk.k}%)</span>
<button class="copy-btn" data-value="cmyk(${data.cmyk.c}%, ${data.cmyk.m}%, ${data.cmyk.y}%, ${data.cmyk.k}%)">Copy</button>
</div>
</div>
`;
colorInfo.innerHTML = colorInfoHTML;
identifyResults.style.display = 'block';
}
function displayColorScheme(data, mode, count) {
schemeInfo.textContent = `${formatModeName(mode)} • ${count} colors`;
schemeColorsContainer.innerHTML = '';
schemeColorsDisplay.textContent = '';
const colors = data.colors;
const colorHexValues = colors.map(color => color.hex.value);
schemeColorsDisplay.textContent = colorHexValues.join(' • ');
colors.forEach(color => {
const colorDiv = document.createElement('div');
colorDiv.className = 'scheme-color';
colorDiv.style.backgroundColor = color.hex.value;
colorDiv.textContent = color.hex.value;
colorDiv.setAttribute('data-value', color.hex.value);
schemeColorsContainer.appendChild(colorDiv);
});
schemeResults.style.display = 'block';
}
function showError(element, message) {
element.textContent = message;
element.style.display = 'block';
}
function isValidColor(color, format) {
if (format === 'hex') {
const hexRegex = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
return hexRegex.test(color);
}
return true;
}
function normalizeHex(hex) {
hex = hex.replace('#', '');
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('');
}
return `#${hex.toUpperCase()}`;
}
function copyToClipboard(text) {
// Create a temporary textarea element
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-999999px';
textarea.style.top = '-999999px';
document.body.appendChild(textarea);
// Select and copy the text
textarea.select();
textarea.setSelectionRange(0, 99999); // For mobile devices
try {
const successful = document.execCommand('copy');
console.log('Copied to clipboard:', text, successful ? 'successfully' : 'unsuccessfully');
} catch (err) {
console.error('Failed to copy text:', err);
}
// Clean up
document.body.removeChild(textarea);
}
function formatModeName(mode) {
const names = {
'monochrome': 'Monochrome',
'monochrome-dark': 'Monochrome Dark',
'monochrome-light': 'Monochrome Light',
'analogic': 'Analogic',
'complement': 'Complement',
'analogic-complement': 'Analogic Complement',
'triad': 'Triad',
'quad': 'Quad'
};
return names[mode] || mode;
}
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
}
function hslToHex(h, s, l) {
s /= 100;
l /= 100;
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
const m = l - c / 2;
let r = 0, g = 0, b = 0;
if (0 <= h && h < 60) {
r = c; g = x; b = 0;
} else if (60 <= h && h < 120) {
r = x; g = c; b = 0;
} else if (120 <= h && h < 180) {
r = 0; g = c; b = x;
} else if (180 <= h && h < 240) {
r = 0; g = x; b = c;
} else if (240 <= h && h < 300) {
r = x; g = 0; b = c;
} else if (300 <= h && h < 360) {
r = c; g = 0; b = x;
}
r = Math.round((r + m) * 255);
g = Math.round((g + m) * 255);
b = Math.round((b + m) * 255);
return rgbToHex(r, g, b);
}
function cmykToHex(c, m, y, k) {
c /= 100;
m /= 100;
y /= 100;
k /= 100;
const r = Math.round(255 * (1 - c) * (1 - k));
const g = Math.round(255 * (1 - m) * (1 - k));
const b = Math.round(255 * (1 - y) * (1 - k));
return rgbToHex(r, g, b);
}
</script>
</body>
</html>