css-shadow-box-maker / index.html
MarkTheArtist's picture
Add 2 files
3adb326 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS Box Shadow Generator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.color-picker {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
width: 40px;
height: 40px;
background-color: transparent;
border: none;
cursor: pointer;
}
.color-picker::-webkit-color-swatch {
border-radius: 8px;
border: 2px solid #e5e7eb;
}
.color-picker::-moz-color-swatch {
border-radius: 8px;
border: 2px solid #e5e7eb;
}
.range-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #3b82f6;
cursor: pointer;
}
.range-slider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #3b82f6;
cursor: pointer;
}
#preview-box {
transition: box-shadow 0.2s ease;
}
#cssOutput {
font-family: 'Courier New', monospace;
}
.shadow-layer {
position: relative;
margin-bottom: 1rem;
}
.delete-layer {
position: absolute;
top: -8px;
right: -8px;
background: #ef4444;
color: white;
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-gray-800 mb-2">CSS Box Shadow Generator</h1>
<p class="text-gray-600">Create beautiful shadows with this visual tool</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Controls Section -->
<div class="bg-white rounded-xl shadow-md p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold text-gray-800">Shadow Controls</h2>
<button id="addLayerBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-plus mr-2"></i> Add Layer
</button>
</div>
<div id="shadowLayers" class="mb-6">
<!-- Shadow layers will be added here -->
</div>
<div class="bg-gray-50 p-4 rounded-lg mb-6">
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Horizontal Offset</label>
<input type="range" id="hOffset" min="-50" max="50" value="10" class="w-full range-slider">
<div class="flex justify-between text-xs text-gray-500">
<span>-50px</span>
<span id="hOffsetValue">10px</span>
<span>50px</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Vertical Offset</label>
<input type="range" id="vOffset" min="-50" max="50" value="10" class="w-full range-slider">
<div class="flex justify-between text-xs text-gray-500">
<span>-50px</span>
<span id="vOffsetValue">10px</span>
<span>50px</span>
</div>
</div>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Blur Radius</label>
<input type="range" id="blur" min="0" max="100" value="15" class="w-full range-slider">
<div class="flex justify-between text-xs text-gray-500">
<span>0px</span>
<span id="blurValue">15px</span>
<span>100px</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Spread</label>
<input type="range" id="spread" min="-50" max="50" value="0" class="w-full range-slider">
<div class="flex justify-between text-xs text-gray-500">
<span>-50px</span>
<span id="spreadValue">0px</span>
<span>50px</span>
</div>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Color</label>
<div class="flex items-center">
<input type="color" id="shadowColor" value="#000000" class="color-picker mr-2">
<input type="text" id="shadowColorHex" value="#000000" class="border rounded px-2 py-1 w-24">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Opacity</label>
<input type="range" id="opacity" min="0" max="100" value="75" class="w-full range-slider">
<div class="flex justify-between text-xs text-gray-500">
<span>0%</span>
<span id="opacityValue">75%</span>
<span>100%</span>
</div>
</div>
</div>
<div class="mt-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Shadow Position</label>
<div class="flex space-x-2">
<button id="insetBtn" class="px-3 py-1 border rounded hover:bg-gray-100">Outset</button>
</div>
</div>
</div>
<div class="flex space-x-2">
<button id="copyBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex-1 flex items-center justify-center">
<i class="fas fa-copy mr-2"></i> Copy CSS
</button>
<button id="resetBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg flex-1 flex items-center justify-center">
<i class="fas fa-redo mr-2"></i> Reset
</button>
</div>
</div>
<!-- Preview and Output Section -->
<div class="space-y-6">
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Preview</h2>
<div class="flex justify-center">
<div id="previewBox" class="w-48 h-48 bg-white rounded-lg border border-gray-200 flex items-center justify-center">
<p class="text-gray-500">Your shadow appears here</p>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">CSS Output</h2>
<div class="bg-gray-800 text-gray-100 p-4 rounded-lg">
<code id="cssOutput" class="block whitespace-pre-wrap">box-shadow: 10px 10px 15px 0px rgba(0, 0, 0, 0.75);</code>
</div>
</div>
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-4">Presets</h2>
<div class="grid grid-cols-2 md:grid-cols-3 gap-3">
<button class="preset-btn p-3 rounded border hover:bg-gray-50" data-preset="0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)">
<div class="w-full h-16 bg-white rounded shadow-sm"></div>
<p class="text-xs mt-1">Small</p>
</button>
<button class="preset-btn p-3 rounded border hover:bg-gray-50" data-preset="0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)">
<div class="w-full h-16 bg-white rounded shadow-md"></div>
<p class="text-xs mt-1">Medium</p>
</button>
<button class="preset-btn p-3 rounded border hover:bg-gray-50" data-preset="0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)">
<div class="w-full h-16 bg-white rounded shadow-lg"></div>
<p class="text-xs mt-1">Large</p>
</button>
<button class="preset-btn p-3 rounded border hover:bg-gray-50" data-preset="0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)">
<div class="w-full h-16 bg-white rounded shadow-xl"></div>
<p class="text-xs mt-1">XL</p>
</button>
<button class="preset-btn p-3 rounded border hover:bg-gray-50" data-preset="0 25px 50px -12px rgba(0, 0, 0, 0.25)">
<div class="w-full h-16 bg-white rounded shadow-2xl"></div>
<p class="text-xs mt-1">2XL</p>
</button>
<button class="preset-btn p-3 rounded border hover:bg-gray-50" data-preset="inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)">
<div class="w-full h-16 bg-white rounded shadow-inner"></div>
<p class="text-xs mt-1">Inset</p>
</button>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const hOffsetInput = document.getElementById('hOffset');
const vOffsetInput = document.getElementById('vOffset');
const blurInput = document.getElementById('blur');
const spreadInput = document.getElementById('spread');
const shadowColorInput = document.getElementById('shadowColor');
const shadowColorHex = document.getElementById('shadowColorHex');
const opacityInput = document.getElementById('opacity');
const insetBtn = document.getElementById('insetBtn');
const previewBox = document.getElementById('previewBox');
const cssOutput = document.getElementById('cssOutput');
const copyBtn = document.getElementById('copyBtn');
const resetBtn = document.getElementById('resetBtn');
const addLayerBtn = document.getElementById('addLayerBtn');
const shadowLayers = document.getElementById('shadowLayers');
const presetBtns = document.querySelectorAll('.preset-btn');
// Current shadow state
let currentLayerIndex = 0;
let shadowLayersData = [{
hOffset: 10,
vOffset: 10,
blur: 15,
spread: 0,
color: '#000000',
opacity: 75,
inset: false
}];
// Initialize
updatePreview();
createLayerElements();
// Event Listeners
hOffsetInput.addEventListener('input', updateShadow);
vOffsetInput.addEventListener('input', updateShadow);
blurInput.addEventListener('input', updateShadow);
spreadInput.addEventListener('input', updateShadow);
shadowColorInput.addEventListener('input', updateColor);
shadowColorHex.addEventListener('input', updateColor);
opacityInput.addEventListener('input', updateShadow);
insetBtn.addEventListener('click', toggleInset);
copyBtn.addEventListener('click', copyToClipboard);
resetBtn.addEventListener('click', resetShadow);
addLayerBtn.addEventListener('click', addLayer);
presetBtns.forEach(btn => {
btn.addEventListener('click', function() {
const preset = this.getAttribute('data-preset');
cssOutput.textContent = `box-shadow: ${preset};`;
previewBox.style.boxShadow = preset;
// Parse the preset into layers
const layers = preset.split(/(?<=\)),/).map(layer => {
layer = layer.trim();
const parts = layer.match(/(inset)?\s*(-?\d+)px\s*(-?\d+)px\s*(\d+)px\s*(-?\d+)px\s*(rgba?\([^)]+\))/);
if (!parts) return null;
const colorParts = parts[6].match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
const opacity = colorParts[4] ? Math.round(parseFloat(colorParts[4]) * 100) : 100;
return {
hOffset: parseInt(parts[2]),
vOffset: parseInt(parts[3]),
blur: parseInt(parts[4]),
spread: parseInt(parts[5]),
color: parts[6],
opacity: opacity,
inset: !!parts[1]
};
}).filter(layer => layer !== null);
shadowLayersData = layers.length ? layers : [{
hOffset: 10,
vOffset: 10,
blur: 15,
spread: 0,
color: '#000000',
opacity: 75,
inset: false
}];
createLayerElements();
updateControlsForLayer(0);
});
});
// Functions
function updateShadow() {
const currentLayer = shadowLayersData[currentLayerIndex];
currentLayer.hOffset = parseInt(hOffsetInput.value);
currentLayer.vOffset = parseInt(vOffsetInput.value);
currentLayer.blur = parseInt(blurInput.value);
currentLayer.spread = parseInt(spreadInput.value);
currentLayer.opacity = parseInt(opacityInput.value);
document.getElementById('hOffsetValue').textContent = `${currentLayer.hOffset}px`;
document.getElementById('vOffsetValue').textContent = `${currentLayer.vOffset}px`;
document.getElementById('blurValue').textContent = `${currentLayer.blur}px`;
document.getElementById('spreadValue').textContent = `${currentLayer.spread}px`;
document.getElementById('opacityValue').textContent = `${currentLayer.opacity}%`;
updatePreview();
}
function updateColor() {
const currentLayer = shadowLayersData[currentLayerIndex];
if (this === shadowColorInput) {
currentLayer.color = shadowColorInput.value;
shadowColorHex.value = shadowColorInput.value;
} else {
currentLayer.color = shadowColorHex.value;
shadowColorInput.value = shadowColorHex.value;
}
updatePreview();
}
function toggleInset() {
const currentLayer = shadowLayersData[currentLayerIndex];
currentLayer.inset = !currentLayer.inset;
if (currentLayer.inset) {
insetBtn.textContent = 'Inset';
insetBtn.classList.add('bg-blue-500', 'text-white');
insetBtn.classList.remove('border', 'hover:bg-gray-100');
} else {
insetBtn.textContent = 'Outset';
insetBtn.classList.remove('bg-blue-500', 'text-white');
insetBtn.classList.add('border', 'hover:bg-gray-100');
}
updatePreview();
}
function updatePreview() {
const shadowValue = shadowLayersData.map(layer => {
const rgbaColor = hexToRgba(layer.color, layer.opacity / 100);
const inset = layer.inset ? 'inset ' : '';
return `${inset}${layer.hOffset}px ${layer.vOffset}px ${layer.blur}px ${layer.spread}px ${rgbaColor}`;
}).join(', ');
previewBox.style.boxShadow = shadowValue;
cssOutput.textContent = `box-shadow: ${shadowValue};`;
}
function hexToRgba(hex, opacity) {
// Remove # if present
hex = hex.replace('#', '');
// Parse r, g, b values
let r, g, b;
if (hex.length === 3) {
r = parseInt(hex.substring(0, 1).repeat(2), 16);
g = parseInt(hex.substring(1, 2).repeat(2), 16);
b = parseInt(hex.substring(2, 3).repeat(2), 16);
} else if (hex.length === 6) {
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
} else {
// Default to black if invalid
return `rgba(0, 0, 0, ${opacity})`;
}
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
}
function copyToClipboard() {
const text = cssOutput.textContent;
navigator.clipboard.writeText(text).then(() => {
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Copied!';
setTimeout(() => {
copyBtn.innerHTML = originalText;
}, 2000);
});
}
function resetShadow() {
shadowLayersData = [{
hOffset: 10,
vOffset: 10,
blur: 15,
spread: 0,
color: '#000000',
opacity: 75,
inset: false
}];
currentLayerIndex = 0;
createLayerElements();
updateControlsForLayer(0);
updatePreview();
}
function addLayer() {
const newLayer = {
hOffset: 5,
vOffset: 5,
blur: 10,
spread: 0,
color: '#000000',
opacity: 50,
inset: false
};
shadowLayersData.push(newLayer);
currentLayerIndex = shadowLayersData.length - 1;
createLayerElements();
updateControlsForLayer(currentLayerIndex);
updatePreview();
}
function createLayerElements() {
shadowLayers.innerHTML = '';
shadowLayersData.forEach((layer, index) => {
const layerElement = document.createElement('div');
layerElement.className = 'shadow-layer bg-gray-100 p-4 rounded-lg';
const rgbaColor = hexToRgba(layer.color, layer.opacity / 100);
const shadowValue = `${layer.inset ? 'inset ' : ''}${layer.hOffset}px ${layer.vOffset}px ${layer.blur}px ${layer.spread}px ${rgbaColor}`;
layerElement.innerHTML = `
<div class="flex items-center justify-between mb-2">
<h3 class="font-medium">Layer ${index + 1}</h3>
<div class="flex items-center">
<div class="w-4 h-4 rounded-full mr-2" style="background-color: ${layer.color}; opacity: ${layer.opacity/100}"></div>
<span class="text-xs">${shadowValue}</span>
</div>
</div>
<div class="h-4 rounded-full" style="box-shadow: ${shadowValue}; background-color: white;"></div>
${index > 0 ? '<div class="delete-layer"><i class="fas fa-times text-xs"></i></div>' : ''}
`;
if (index > 0) {
layerElement.querySelector('.delete-layer').addEventListener('click', () => {
shadowLayersData.splice(index, 1);
currentLayerIndex = Math.min(currentLayerIndex, shadowLayersData.length - 1);
createLayerElements();
updateControlsForLayer(currentLayerIndex);
updatePreview();
});
}
layerElement.addEventListener('click', () => {
currentLayerIndex = index;
updateControlsForLayer(index);
});
if (index === currentLayerIndex) {
layerElement.classList.add('ring-2', 'ring-blue-500');
}
shadowLayers.appendChild(layerElement);
});
}
function updateControlsForLayer(index) {
const layer = shadowLayersData[index];
hOffsetInput.value = layer.hOffset;
vOffsetInput.value = layer.vOffset;
blurInput.value = layer.blur;
spreadInput.value = layer.spread;
shadowColorInput.value = layer.color;
shadowColorHex.value = layer.color;
opacityInput.value = layer.opacity;
document.getElementById('hOffsetValue').textContent = `${layer.hOffset}px`;
document.getElementById('vOffsetValue').textContent = `${layer.vOffset}px`;
document.getElementById('blurValue').textContent = `${layer.blur}px`;
document.getElementById('spreadValue').textContent = `${layer.spread}px`;
document.getElementById('opacityValue').textContent = `${layer.opacity}%`;
if (layer.inset) {
insetBtn.textContent = 'Inset';
insetBtn.classList.add('bg-blue-500', 'text-white');
insetBtn.classList.remove('border', 'hover:bg-gray-100');
} else {
insetBtn.textContent = 'Outset';
insetBtn.classList.remove('bg-blue-500', 'text-white');
insetBtn.classList.add('border', 'hover:bg-gray-100');
}
// Update active layer indicator
document.querySelectorAll('.shadow-layer').forEach((el, i) => {
if (i === index) {
el.classList.add('ring-2', 'ring-blue-500');
} else {
el.classList.remove('ring-2', 'ring-blue-500');
}
});
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=MarkTheArtist/css-shadow-box-maker" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>