vitality-bmi / index.html
sourcingnuances's picture
Add 3 files
219f68a verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vitality BMI Calculator</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
<style>
:root {
--bg-primary: #f8fafc;
--bg-secondary: #ffffff;
--text-primary: #1e293b;
--text-secondary: #64748b;
--accent: #0ea5e9;
--border: #e2e8f0;
--underweight: #06b6d4;
--normal: #10b981;
--overweight: #f59e0b;
--obese: #ef4444;
}
.dark {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--accent: #38bdf8;
--border: #334155;
--underweight: #22d3ee;
--normal: #34d399;
--overweight: #fbbf24;
--obese: #f87171;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
transition: all 0.3s ease;
}
.card {
background-color: var(--bg-secondary);
border-color: var(--border);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.input-dial {
width: 120px;
height: 120px;
border-radius: 50%;
background: var(--bg-secondary);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.input-dial:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.bmi-bar {
width: 24px;
height: 200px;
border-radius: 12px;
background: linear-gradient(to top, var(--underweight), var(--normal), var(--overweight), var(--obese));
position: relative;
}
.bmi-indicator {
width: 36px;
height: 8px;
border-radius: 4px;
background-color: var(--bg-secondary);
position: absolute;
left: -6px;
transform: translateY(-4px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.bmi-category {
transition: all 0.3s ease;
}
.bmi-category.active {
transform: scale(1.05);
font-weight: 600;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.celebrate {
animation: pulse 1.5s infinite;
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center p-4 font-sans">
<div class="w-full max-w-md mx-auto">
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold bg-gradient-to-r from-teal-400 to-coral-500 bg-clip-text text-transparent">Vitality BMI</h1>
<button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</button>
</div>
<div class="card rounded-2xl shadow-xl p-6 mb-6">
<div class="flex justify-between items-center mb-8">
<div class="input-dial">
<label for="height" class="text-sm font-medium text-gray-500 dark:text-gray-400">Height (cm)</label>
<input id="height" type="number" class="text-3xl font-bold text-center bg-transparent border-none w-20 focus:outline-none focus:ring-0" value="170" min="100" max="250">
</div>
<div class="input-dial">
<label for="weight" class="text-sm font-medium text-gray-500 dark:text-gray-400">Weight (kg)</label>
<input id="weight" type="number" class="text-3xl font-bold text-center bg-transparent border-none w-20 focus:outline-none focus:ring-0" value="70" min="30" max="200">
</div>
</div>
<div class="flex items-center justify-between mb-8">
<div class="flex-1">
<div class="text-5xl font-bold mb-2" id="bmi-value">24.2</div>
<div class="text-lg font-medium" id="bmi-category-text">Normal weight</div>
</div>
<div class="relative">
<div class="bmi-bar">
<div class="bmi-indicator" id="bmi-indicator" style="top: 50%"></div>
</div>
</div>
</div>
<div class="grid grid-cols-4 gap-2 text-center text-sm">
<div class="bmi-category" id="underweight">
<div class="font-medium">Underweight</div>
<div class="text-xs text-gray-500 dark:text-gray-400">&lt; 18.5</div>
</div>
<div class="bmi-category" id="normal">
<div class="font-medium">Normal</div>
<div class="text-xs text-gray-500 dark:text-gray-400">18.5 - 24.9</div>
</div>
<div class="bmi-category" id="overweight">
<div class="font-medium">Overweight</div>
<div class="text-xs text-gray-500 dark:text-gray-400">25 - 29.9</div>
</div>
<div class="bmi-category" id="obese">
<div class="font-medium">Obese</div>
<div class="text-xs text-gray-500 dark:text-gray-400">≥ 30</div>
</div>
</div>
</div>
<div class="text-center text-sm text-gray-500 dark:text-gray-400">
BMI is a simple index of weight-for-height that is commonly used to classify underweight, overweight and obesity in adults.
</div>
</div>
<script>
// DOM elements
const heightInput = document.getElementById('height');
const weightInput = document.getElementById('weight');
const bmiValue = document.getElementById('bmi-value');
const bmiCategoryText = document.getElementById('bmi-category-text');
const bmiIndicator = document.getElementById('bmi-indicator');
const underweightEl = document.getElementById('underweight');
const normalEl = document.getElementById('normal');
const overweightEl = document.getElementById('overweight');
const obeseEl = document.getElementById('obese');
const themeToggle = document.getElementById('theme-toggle');
// Initialize
calculateBMI();
// Event listeners
heightInput.addEventListener('input', calculateBMI);
weightInput.addEventListener('input', calculateBMI);
themeToggle.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light');
});
// Check for saved theme preference
if (localStorage.getItem('theme') === 'dark' ||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
// Calculate BMI and update UI
function calculateBMI() {
const height = parseFloat(heightInput.value) / 100; // Convert cm to m
const weight = parseFloat(weightInput.value);
if (height <= 0 || weight <= 0) return;
const bmi = weight / (height * height);
const roundedBMI = Math.round(bmi * 10) / 10;
bmiValue.textContent = roundedBMI;
// Position indicator on the bar (BMI range: 15 to 40)
const position = Math.min(Math.max((bmi - 15) / (40 - 15), 0), 1);
bmiIndicator.style.top = `${100 - (position * 100)}%`;
// Update category text and styling
let category = '';
let categoryEl = null;
let isHealthy = false;
if (bmi < 18.5) {
category = 'Underweight';
categoryEl = underweightEl;
} else if (bmi >= 18.5 && bmi < 25) {
category = 'Normal weight';
categoryEl = normalEl;
isHealthy = true;
} else if (bmi >= 25 && bmi < 30) {
category = 'Overweight';
categoryEl = overweightEl;
} else {
category = 'Obese';
categoryEl = obeseEl;
}
bmiCategoryText.textContent = category;
// Reset all categories
[underweightEl, normalEl, overweightEl, obeseEl].forEach(el => {
el.classList.remove('active');
el.classList.remove('celebrate');
});
// Highlight current category
if (categoryEl) {
categoryEl.classList.add('active');
// Celebrate if in healthy range
if (isHealthy) {
categoryEl.classList.add('celebrate');
triggerConfetti();
}
}
}
// Trigger confetti effect
function triggerConfetti() {
const end = Date.now() + 1000;
const colors = ['#06b6d4', '#10b981', '#0ea5e9', '#8b5cf6'];
(function frame() {
confetti({
particleCount: 4,
angle: 60,
spread: 55,
origin: { x: 0, y: 0.7 },
colors: colors
});
confetti({
particleCount: 4,
angle: 120,
spread: 55,
origin: { x: 1, y: 0.7 },
colors: colors
});
if (Date.now() < end) {
requestAnimationFrame(frame);
}
}());
}
</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=sourcingnuances/vitality-bmi" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>