MarkTheArtist's picture
This utility is designed to be open to the public so that they can put in their information and it will draw plans so that they can cut them out and build it having a preview is also good. I currently can’t see the preview. - Follow Up Deployment
c6313bc verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Step Stool Designer</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>
.preview-container {
perspective: 1000px;
background: linear-gradient(135deg, #f0f4ff 0%, #e6ecfa 100%);
border: 1px solid #d1d5db;
box-shadow: inset 0 0 20px rgba(0,0,0,0.05);
}
.stool-preview {
transform-style: preserve-3d;
transition: transform 0.5s ease;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.stool-step {
position: absolute;
background: #d1a05a;
border: 2px solid #8b5a2b;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
box-shadow:
inset 0 0 10px rgba(0,0,0,0.2),
0 5px 15px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.stool-leg {
position: absolute;
background: #8b5a2b;
box-shadow: inset 0 0 5px rgba(0,0,0,0.3);
}
.color-option {
width: 30px;
height: 30px;
border-radius: 50%;
cursor: pointer;
transition: transform 0.2s;
}
.color-option:hover {
transform: scale(1.1);
}
.preview-container::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
linear-gradient(rgba(0,0,0,0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,0,0,0.05) 1px, transparent 1px);
background-size: 20px 20px;
opacity: 0.5;
pointer-events: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
background: #4f46e5;
border-radius: 50%;
cursor: pointer;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-12">
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Step Stool Designer</h1>
<p class="text-gray-600 max-w-2xl mx-auto">Create your perfect custom step stool with our interactive designer tool</p>
</header>
<div class="flex flex-col lg:flex-row gap-8">
<!-- Design Controls -->
<div class="lg:w-1/3 bg-white rounded-xl shadow-lg p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-6 flex items-center">
<i class="fas fa-ruler-combined mr-2 text-indigo-600"></i> Design Specifications
</h2>
<div class="space-y-6">
<!-- Step Count -->
<div>
<label class="block text-gray-700 font-medium mb-2">Number of Steps</label>
<div class="flex items-center space-x-4">
<button id="step-decrease" class="bg-gray-200 hover:bg-gray-300 rounded-full w-8 h-8 flex items-center justify-center">
<i class="fas fa-minus"></i>
</button>
<input type="number" id="step-count" min="1" max="5" value="2" class="w-16 text-center border border-gray-300 rounded py-2">
<button id="step-increase" class="bg-gray-200 hover:bg-gray-300 rounded-full w-8 h-8 flex items-center justify-center">
<i class="fas fa-plus"></i>
</button>
</div>
</div>
<!-- Dimensions -->
<div>
<label class="block text-gray-700 font-medium mb-2">Dimensions (cm)</label>
<div class="space-y-4">
<div>
<div class="flex justify-between mb-1">
<span>Height: <span id="height-value">30</span>cm</span>
<span class="text-gray-500">Max: 50cm</span>
</div>
<input type="range" id="height" min="20" max="50" value="30" class="w-full">
</div>
<div>
<div class="flex justify-between mb-1">
<span>Width: <span id="width-value">40</span>cm</span>
<span class="text-gray-500">Max: 60cm</span>
</div>
<input type="range" id="width" min="30" max="60" value="40" class="w-full">
</div>
<div>
<div class="flex justify-between mb-1">
<span>Depth: <span id="depth-value">30</span>cm</span>
<span class="text-gray-500">Max: 50cm</span>
</div>
<input type="range" id="depth" min="20" max="50" value="30" class="w-full">
</div>
</div>
</div>
<!-- Step Configuration -->
<div>
<label class="block text-gray-700 font-medium mb-2">Step Style</label>
<div class="grid grid-cols-2 gap-3">
<button id="style-open" class="py-2 px-4 border border-indigo-300 rounded-lg bg-indigo-50 text-indigo-700 font-medium flex items-center justify-center">
<i class="fas fa-box-open mr-2"></i> Open
</button>
<button id="style-box" class="py-2 px-4 border border-gray-300 rounded-lg bg-white text-gray-700 font-medium flex items-center justify-center">
<i class="fas fa-box mr-2"></i> Boxed
</button>
</div>
</div>
<!-- Color Selection -->
<div>
<label class="block text-gray-700 font-medium mb-2">Color</label>
<div class="flex flex-wrap gap-3">
<div class="color-option bg-amber-700 border-2 border-amber-900" data-color="#b45309"></div>
<div class="color-option bg-amber-600 border-2 border-amber-800" data-color="#d97706"></div>
<div class="color-option bg-amber-500 border-2 border-amber-700" data-color="#f59e0b"></div>
<div class="color-option bg-gray-700 border-2 border-gray-900" data-color="#4b5563"></div>
<div class="color-option bg-gray-500 border-2 border-gray-700" data-color="#6b7280"></div>
<div class="color-option bg-white border-2 border-gray-300" data-color="#ffffff"></div>
</div>
</div>
<!-- Safety Features -->
<div>
<label class="block text-gray-700 font-medium mb-2">Safety Features</label>
<div class="space-y-2">
<label class="flex items-center space-x-3">
<input type="checkbox" id="non-slip" class="rounded text-indigo-600">
<span>Non-slip surface</span>
</label>
<label class="flex items-center space-x-3">
<input type="checkbox" id="handrail" class="rounded text-indigo-600">
<span>Handrail</span>
</label>
</div>
</div>
</div>
<div class="mt-8 pt-6 border-t border-gray-200">
<button id="generate-btn" class="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center">
<i class="fas fa-magic mr-2"></i> Generate 3D Preview
</button>
</div>
</div>
<!-- 3D Preview -->
<div class="lg:w-2/3">
<div class="bg-white rounded-xl shadow-lg p-6 h-full">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold text-gray-800 flex items-center">
<i class="fas fa-cube mr-2 text-indigo-600"></i> 3D Preview
</h2>
<div class="flex space-x-2">
<button id="rotate-left" class="bg-gray-200 hover:bg-gray-300 rounded-full w-8 h-8 flex items-center justify-center">
<i class="fas fa-undo"></i>
</button>
<button id="rotate-right" class="bg-gray-200 hover:bg-gray-300 rounded-full w-8 h-8 flex items-center justify-center">
<i class="fas fa-redo"></i>
</button>
</div>
</div>
<div class="preview-container w-full h-96 bg-gray-50 rounded-lg flex items-center justify-center relative">
<div id="stool-preview" class="stool-preview relative">
<!-- Generated stool will appear here -->
</div>
<div id="pieces-view" class="absolute bottom-4 right-4 bg-white p-2 rounded-lg shadow-md hidden">
<h3 class="text-sm font-medium mb-1">Stool Pieces</h3>
<div id="pieces-list" class="text-xs space-y-1"></div>
</div>
</div>
<div class="mt-6 pt-6 border-t border-gray-200">
<div class="flex flex-col sm:flex-row justify-between items-center gap-4">
<div>
<h3 class="font-medium text-gray-700">Your Custom Stool</h3>
<p class="text-sm text-gray-500">Estimated material: <span id="material-estimate">2.5</span> sq ft</p>
</div>
<div class="flex space-x-3">
<button class="bg-white border border-indigo-600 text-indigo-600 hover:bg-indigo-50 font-medium py-2 px-4 rounded-lg flex items-center">
<i class="fas fa-download mr-2"></i> Save Design
</button>
<button class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-2 px-4 rounded-lg flex items-center">
<i class="fas fa-shopping-cart mr-2"></i> Order Now ($<span id="price">49</span>)
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Design Tips -->
<div class="mt-12 bg-white rounded-xl shadow-lg p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
<i class="fas fa-lightbulb mr-2 text-yellow-500"></i> Design Tips
</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="bg-indigo-50 p-4 rounded-lg">
<h3 class="font-medium text-indigo-800 mb-2 flex items-center">
<i class="fas fa-ruler-vertical mr-2"></i> Height Considerations
</h3>
<p class="text-gray-700">For safety, each step should be no more than 20cm high. Our tool automatically calculates optimal step heights based on your total height.</p>
</div>
<div class="bg-amber-50 p-4 rounded-lg">
<h3 class="font-medium text-amber-800 mb-2 flex items-center">
<i class="fas fa-weight mr-2"></i> Weight Capacity
</h3>
<p class="text-gray-700">Standard designs support up to 150kg. For heavier use, consider adding reinforcement or choosing boxed step style.</p>
</div>
<div class="bg-green-50 p-4 rounded-lg">
<h3 class="font-medium text-green-800 mb-2 flex items-center">
<i class="fas fa-child mr-2"></i> Child Safety
</h3>
<p class="text-gray-700">For children, add handrails and non-slip surfaces. Keep step depth at least 25cm for secure footing.</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const stepCountInput = document.getElementById('step-count');
const stepDecreaseBtn = document.getElementById('step-decrease');
const stepIncreaseBtn = document.getElementById('step-increase');
const heightSlider = document.getElementById('height');
const widthSlider = document.getElementById('width');
const depthSlider = document.getElementById('depth');
const heightValue = document.getElementById('height-value');
const widthValue = document.getElementById('width-value');
const depthValue = document.getElementById('depth-value');
const styleOpenBtn = document.getElementById('style-open');
const styleBoxBtn = document.getElementById('style-box');
const nonSlipCheckbox = document.getElementById('non-slip');
const handrailCheckbox = document.getElementById('handrail');
const generateBtn = document.getElementById('generate-btn');
const stoolPreview = document.getElementById('stool-preview');
const rotateLeftBtn = document.getElementById('rotate-left');
const rotateRightBtn = document.getElementById('rotate-right');
const materialEstimate = document.getElementById('material-estimate');
const priceElement = document.getElementById('price');
const colorOptions = document.querySelectorAll('.color-option');
// State variables
let stepCount = 2;
let height = 30;
let width = 40;
let depth = 30;
let stepStyle = 'open';
let stoolColor = '#d1a05a';
let rotationAngle = 0;
// Event Listeners
stepDecreaseBtn.addEventListener('click', () => {
if (stepCount > 1) {
stepCount--;
stepCountInput.value = stepCount;
updatePrice();
}
});
stepIncreaseBtn.addEventListener('click', () => {
if (stepCount < 5) {
stepCount++;
stepCountInput.value = stepCount;
updatePrice();
}
});
stepCountInput.addEventListener('change', (e) => {
let value = parseInt(e.target.value);
if (isNaN(value) || value < 1) value = 1;
if (value > 5) value = 5;
stepCount = value;
e.target.value = value;
updatePrice();
});
heightSlider.addEventListener('input', (e) => {
height = parseInt(e.target.value);
heightValue.textContent = height;
});
widthSlider.addEventListener('input', (e) => {
width = parseInt(e.target.value);
widthValue.textContent = width;
});
depthSlider.addEventListener('input', (e) => {
depth = parseInt(e.target.value);
depthValue.textContent = depth;
});
styleOpenBtn.addEventListener('click', () => {
stepStyle = 'open';
styleOpenBtn.classList.add('border-indigo-300', 'bg-indigo-50', 'text-indigo-700');
styleOpenBtn.classList.remove('border-gray-300', 'bg-white', 'text-gray-700');
styleBoxBtn.classList.add('border-gray-300', 'bg-white', 'text-gray-700');
styleBoxBtn.classList.remove('border-indigo-300', 'bg-indigo-50', 'text-indigo-700');
});
styleBoxBtn.addEventListener('click', () => {
stepStyle = 'box';
styleBoxBtn.classList.add('border-indigo-300', 'bg-indigo-50', 'text-indigo-700');
styleBoxBtn.classList.remove('border-gray-300', 'bg-white', 'text-gray-700');
styleOpenBtn.classList.add('border-gray-300', 'bg-white', 'text-gray-700');
styleOpenBtn.classList.remove('border-indigo-300', 'bg-indigo-50', 'text-indigo-700');
});
colorOptions.forEach(option => {
option.addEventListener('click', (e) => {
stoolColor = e.target.dataset.color;
// Update active color indicator
colorOptions.forEach(opt => opt.style.transform = 'scale(1)');
e.target.style.transform = 'scale(1.2)';
});
});
generateBtn.addEventListener('click', generateStoolPreview);
rotateLeftBtn.addEventListener('click', () => {
rotationAngle -= 45;
stoolPreview.style.transform = `rotateY(${rotationAngle}deg)`;
});
rotateRightBtn.addEventListener('click', () => {
rotationAngle += 45;
stoolPreview.style.transform = `rotateY(${rotationAngle}deg)`;
});
// Initialize
styleOpenBtn.click();
colorOptions[0].style.transform = 'scale(1.2)';
generateStoolPreview();
// Functions
function generateStoolPreview() {
// Clear previous stool
stoolPreview.innerHTML = '';
const piecesList = document.getElementById('pieces-list');
piecesList.innerHTML = '';
document.getElementById('pieces-view').classList.remove('hidden');
// Calculate dimensions
const stepHeight = height / stepCount;
const legWidth = 5; // cm in preview scale
const scaleFactor = 3; // Scale down for display
// Create steps
for (let i = 0; i < stepCount; i++) {
const step = document.createElement('div');
step.className = 'stool-step';
// Calculate dimensions for this step
const stepWidth = width - (i * 5);
const stepDepth = depth - (i * 5);
const stepTop = i * stepHeight;
// Set step styles
step.style.width = `${stepWidth / scaleFactor}px`;
step.style.height = `${stepHeight / scaleFactor}px`;
step.style.top = `${(height - stepTop - stepHeight) / scaleFactor}px`;
step.style.left = `${(i * 5 / 2) / scaleFactor}px`;
step.style.backgroundColor = stoolColor;
step.style.zIndex = stepCount - i;
// Add non-slip texture if selected
if (nonSlipCheckbox.checked) {
step.style.backgroundImage = 'radial-gradient(circle, rgba(0,0,0,0.1) 1px, transparent 1px)';
step.style.backgroundSize = '10px 10px';
}
// Add step number
step.textContent = i + 1;
stoolPreview.appendChild(step);
// Add to pieces list
const pieceItem = document.createElement('div');
pieceItem.textContent = `Step ${i+1}: ${stepWidth.toFixed(1)}cm × ${stepDepth.toFixed(1)}cm`;
pieceItem.style.color = stoolColor;
piecesList.appendChild(pieceItem);
// Create legs for open style
if (stepStyle === 'open' && i === 0) {
// Front legs
const frontLeftLeg = createLeg(0, stepHeight * stepCount, stepHeight, legWidth, scaleFactor);
const frontRightLeg = createLeg(stepWidth - legWidth, stepHeight * stepCount, stepHeight, legWidth, scaleFactor);
// Back legs (only visible in 3D)
const backLeftLeg = createLeg(0, stepHeight * stepCount, stepHeight, legWidth, scaleFactor);
const backRightLeg = createLeg(stepWidth - legWidth, stepHeight * stepCount, stepHeight, legWidth, scaleFactor);
// Position back legs in 3D space
backLeftLeg.style.transform = `translateZ(${stepDepth / scaleFactor}px)`;
backRightLeg.style.transform = `translateZ(${stepDepth / scaleFactor}px)`;
stoolPreview.appendChild(frontLeftLeg);
const legItem = document.createElement('div');
legItem.textContent = `Leg: 5cm × ${stepHeight.toFixed(1)}cm`;
legItem.style.color = '#8b5a2b';
piecesList.appendChild(legItem);
stoolPreview.appendChild(frontRightLeg);
stoolPreview.appendChild(backLeftLeg);
stoolPreview.appendChild(backRightLeg);
}
// For box style, add side panels
if (stepStyle === 'box' && i < stepCount - 1) {
const sidePanelDepth = stepDepth - ((i + 1) * 5);
// Left side panel
const leftPanel = document.createElement('div');
leftPanel.className = 'stool-leg';
leftPanel.style.width = `${legWidth / scaleFactor}px`;
leftPanel.style.height = `${stepHeight / scaleFactor}px`;
leftPanel.style.top = `${(height - stepTop - stepHeight) / scaleFactor}px`;
leftPanel.style.left = `${(i * 5 / 2) / scaleFactor}px`;
leftPanel.style.transform = `translateZ(${(stepDepth - legWidth) / scaleFactor}px) rotateY(90deg)`;
leftPanel.style.transformOrigin = 'left center';
leftPanel.style.backgroundColor = '#8b5a2b';
leftPanel.style.zIndex = stepCount - i - 0.5;
// Right side panel
const rightPanel = document.createElement('div');
rightPanel.className = 'stool-leg';
rightPanel.style.width = `${legWidth / scaleFactor}px`;
rightPanel.style.height = `${stepHeight / scaleFactor}px`;
rightPanel.style.top = `${(height - stepTop - stepHeight) / scaleFactor}px`;
rightPanel.style.left = `${((i * 5 / 2) + stepWidth - legWidth) / scaleFactor}px`;
rightPanel.style.transform = `translateZ(${(stepDepth - legWidth) / scaleFactor}px) rotateY(90deg)`;
rightPanel.style.transformOrigin = 'left center';
rightPanel.style.backgroundColor = '#8b5a2b';
rightPanel.style.zIndex = stepCount - i - 0.5;
stoolPreview.appendChild(leftPanel);
const panelItem = document.createElement('div');
panelItem.textContent = `Side Panel: 5cm × ${stepHeight.toFixed(1)}cm`;
panelItem.style.color = '#8b5a2b';
piecesList.appendChild(panelItem);
stoolPreview.appendChild(rightPanel);
}
}
// Add handrail if selected
if (handrailCheckbox.checked) {
const handrailHeight = stepHeight * 1.5;
const handrail = document.createElement('div');
handrail.className = 'stool-leg';
handrail.style.width = `${legWidth / scaleFactor}px`;
handrail.style.height = `${handrailHeight / scaleFactor}px`;
handrail.style.top = `${(height - stepHeight * stepCount - handrailHeight) / scaleFactor}px`;
handrail.style.left = `${(width / 2 - legWidth / 2) / scaleFactor}px`;
handrail.style.backgroundColor = '#8b5a2b';
handrail.style.zIndex = stepCount + 1;
// Horizontal rail
const topRail = document.createElement('div');
topRail.className = 'stool-leg';
topRail.style.width = `${(width * 0.8) / scaleFactor}px`;
topRail.style.height = `${legWidth / scaleFactor}px`;
topRail.style.top = `${(height - stepHeight * stepCount - handrailHeight) / scaleFactor}px`;
topRail.style.left = `${(width * 0.1) / scaleFactor}px`;
topRail.style.backgroundColor = '#8b5a2b';
topRail.style.zIndex = stepCount + 2;
stoolPreview.appendChild(handrail);
const railItem = document.createElement('div');
railItem.textContent = `Handrail: 5cm × ${handrailHeight.toFixed(1)}cm`;
railItem.style.color = '#8b5a2b';
piecesList.appendChild(railItem);
stoolPreview.appendChild(topRail);
}
// Update material estimate and price
updateMaterialEstimate();
updatePrice();
}
function createLeg(x, totalHeight, stepHeight, width, scaleFactor) {
const leg = document.createElement('div');
leg.className = 'stool-leg';
leg.style.width = `${width / scaleFactor}px`;
leg.style.height = `${totalHeight / scaleFactor}px`;
leg.style.top = '0';
leg.style.left = `${x / scaleFactor}px`;
return leg;
}
function updateMaterialEstimate() {
// Simplified material calculation
const baseMaterial = width * depth / 600; // Base material in sq ft
const stepMaterial = (width * depth * 0.7 * stepCount) / 600; // Steps material
const legMaterial = (height * 4 * 5) / 600; // Legs material
let total = baseMaterial + stepMaterial + legMaterial;
if (stepStyle === 'box') total *= 1.3;
if (handrailCheckbox.checked) total += 0.5;
materialEstimate.textContent = total.toFixed(1);
}
function updatePrice() {
// Base price
let price = 30;
// Add for steps
price += (stepCount - 1) * 10;
// Add for size
price += (width - 30) * 0.2;
price += (depth - 20) * 0.2;
// Add for features
if (stepStyle === 'box') price += 15;
if (nonSlipCheckbox.checked) price += 8;
if (handrailCheckbox.checked) price += 12;
priceElement.textContent = Math.round(price);
}
});
</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/step-stool-configuration" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>