Skip to content

Commit 19c8e40

Browse files
committed
- redesign BMI calculator with modern UI and responsive layout
- add imperial/metric unit toggle functionality - implement more granular BMI categories (now includes obesity classes I-III) - add visual BMI scale with marker indicator - implement form validation with error messages - add localStorage to persist user data between sessions - replace bootstrap and jquery dependencies with vanilla JS and CSS - update favicon from 🔎 to 📊 - switch from `sidebar.js` to `logo.js` script
1 parent 822cef7 commit 19c8e40

File tree

1 file changed

+260
-90
lines changed

1 file changed

+260
-90
lines changed

tools/bmi_calculator.html

Lines changed: 260 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,109 +4,279 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>BMI Calculator</title>
7-
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔎</text></svg>">
8-
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
9-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" crossorigin="anonymous" />
7+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📊</text></svg>">
108
<style>
11-
body {
12-
font-family: Arial, sans-serif;
13-
background-color: #f8f9fa;
14-
}
15-
.container {
16-
max-width: 500px;
17-
margin: 0 auto;
18-
padding: 20px;
19-
background-color: #fff;
20-
border-radius: 5px;
21-
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
22-
}
23-
h1 {
24-
text-align: center;
25-
margin-bottom: 30px;
26-
}
27-
.form-group {
28-
margin-bottom: 20px;
29-
}
30-
label {
31-
font-weight: bold;
32-
}
33-
input[type="number"] {
34-
width: 100%;
35-
padding: 10px;
36-
border-radius: 5px;
37-
border: 1px solid #ccc;
38-
box-sizing: border-box;
39-
}
40-
.btn {
41-
width: 100%;
42-
padding: 10px;
43-
border-radius: 5px;
44-
background-color: #007bff;
45-
color: #fff;
46-
border: none;
47-
cursor: pointer;
48-
}
49-
.btn:hover {
50-
background-color: #0069d9;
51-
}
52-
.result {
53-
margin-top: 30px;
54-
text-align: center;
55-
font-size: 24px;
56-
font-weight: bold;
57-
}
58-
.result i {
59-
margin-right: 10px;
60-
}
9+
:root{--primary:#4285f4;--primary-dark:#2a66c8;--success:#34a853;--warning:#fbbc05;--danger:#ea4335;--light:#f8f9fa;--dark:#212121;--gray:#e0e0e0;--gray-dark:#757575;--radius:8px;--shadow:0 3px 6px rgba(0,0,0,0.1);--transition:all 0.3s ease;}*{margin:0;padding:0;box-sizing:border-box;}body{font-family:'Segoe UI',Roboto,system-ui,sans-serif;background-color:var(--light);color:var(--dark);line-height:1.6;padding:20px;display:flex;flex-direction:column;min-height:100vh;}.container{max-width:550px;margin:0 auto;background:#fff;border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden;width:100%;}header{background:var(--primary);color:white;padding:20px;text-align:center;}header h1{margin:0;font-size:1.8rem;font-weight:500;}.unit-toggle{display:flex;justify-content:center;margin-top:12px;}.unit-toggle-btn{background:rgba(255,255,255,0.2);border:none;color:white;padding:5px 15px;font-size:0.9rem;cursor:pointer;transition:var(--transition);}.unit-toggle-btn:first-child{border-radius:20px 0 0 20px;}.unit-toggle-btn:last-child{border-radius:0 20px 20px 0;}.unit-toggle-btn.active{background:white;color:var(--primary);}.calculator-form{padding:24px;}.input-group{margin-bottom:20px;}.input-group label{display:block;margin-bottom:8px;font-weight:500;color:var(--gray-dark);font-size:0.9rem;}.input-row{display:flex;gap:10px;}.input-field{position:relative;flex:1;}.input-field input{width:100%;padding:12px 15px;border:1px solid var(--gray);border-radius:var(--radius);font-size:1rem;transition:var(--transition);}.input-field input:focus{outline:none;border-color:var(--primary);box-shadow:0 0 0 2px rgba(66,133,244,0.2);}.input-field .unit{position:absolute;right:15px;top:50%;transform:translateY(-50%);color:var(--gray-dark);font-size:0.9rem;pointer-events:none;}.error-message{color:var(--danger);font-size:0.8rem;margin-top:5px;display:none;}.btn-row{display:flex;gap:10px;margin-top:25px;}.btn{flex:1;padding:12px;border:none;border-radius:var(--radius);font-size:1rem;cursor:pointer;transition:var(--transition);font-weight:500;}.btn-primary{background:var(--primary);color:white;}.btn-primary:hover{background:var(--primary-dark);}.btn-secondary{background:var(--gray);color:var(--dark);}.btn-secondary:hover{background:var(--gray-dark);color:white;}.results{background:var(--light);padding:24px;border-top:1px solid var(--gray);display:none;}.result-header{text-align:center;margin-bottom:20px;}.bmi-value{font-size:2.5rem;font-weight:700;color:var(--primary);margin:0;}.bmi-category{font-size:1.2rem;color:var(--gray-dark);}.bmi-scale{height:15px;background:linear-gradient(to right,#3498db 0%,#2ecc71 18.5%,#27ae60 25%,#f1c40f 30%,#e67e22 35%,#e74c3c 100%);border-radius:10px;margin:30px 0 10px;position:relative;}.bmi-marker{width:2px;height:20px;background:var(--dark);position:absolute;top:-10px;transform:translateX(-50%);}.bmi-marker::after{content:'';position:absolute;bottom:-5px;left:-4px;width:10px;height:10px;background:var(--dark);border-radius:50%;}.scale-labels{display:flex;justify-content:space-between;margin-top:5px;color:var(--gray-dark);font-size:0.8rem;}.bmi-description{margin-top:20px;line-height:1.6;}.about-section{padding:24px;border-top:1px solid var(--gray);}.about-section h2{font-size:1.2rem;margin-bottom:10px;}.about-section p{font-size:0.9rem;color:var(--gray-dark);line-height:1.6;}footer{text-align:center;margin-top:20px;font-size:0.8rem;color:var(--gray-dark);}@media (max-width:600px){body{padding:10px;}.calculator-form,.results,.about-section{padding:15px;}.bmi-value{font-size:2rem;}.input-row{flex-direction:column;}}button:focus,input:focus{outline:2px solid var(--primary);outline-offset:2px;}@keyframes fadeIn{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}.fade-in{animation:fadeIn 0.3s ease-out forwards;}
6110
</style>
6211
</head>
6312
<body>
64-
<div class="container">
13+
<div class="container">
14+
<header>
6515
<h1>BMI Calculator</h1>
66-
<form>
67-
<div class="form-group">
68-
<label for="height">Height (cm)</label>
69-
<input type="number" id="height" name="height" placeholder="Enter your height in centimeters">
16+
<div class="unit-toggle">
17+
<button type="button" class="unit-toggle-btn active" id="metric">Metric</button>
18+
<button type="button" class="unit-toggle-btn" id="imperial">Imperial</button>
19+
</div>
20+
</header>
21+
<form class="calculator-form" id="bmiForm">
22+
<div class="input-group metric-inputs">
23+
<label for="height">Height</label>
24+
<div class="input-field">
25+
<input type="number" id="height" min="50" max="300" placeholder="Enter your height" aria-describedby="heightError">
26+
<span class="unit">cm</span>
27+
<div class="error-message" id="heightError">Please enter a valid height (50-300 cm)</div>
28+
</div>
29+
</div>
30+
<div class="input-group imperial-inputs" style="display: none;">
31+
<label for="heightFeet">Height</label>
32+
<div class="input-row">
33+
<div class="input-field">
34+
<input type="number" id="heightFeet" min="1" max="8" placeholder="Feet" aria-describedby="heightImperialError">
35+
<span class="unit">ft</span>
36+
</div>
37+
<div class="input-field">
38+
<input type="number" id="heightInches" min="0" max="11" placeholder="Inches" aria-describedby="heightImperialError">
39+
<span class="unit">in</span>
40+
<div class="error-message" id="heightImperialError">Please enter a valid height</div>
41+
</div>
7042
</div>
71-
<div class="form-group">
72-
<label for="weight">Weight (kg)</label>
73-
<input type="number" id="weight" name="weight" placeholder="Enter your weight in kilograms">
43+
</div>
44+
<div class="input-group metric-inputs">
45+
<label for="weight">Weight</label>
46+
<div class="input-field">
47+
<input type="number" id="weight" min="10" max="500" placeholder="Enter your weight" aria-describedby="weightError">
48+
<span class="unit">kg</span>
49+
<div class="error-message" id="weightError">Please enter a valid weight (10-500 kg)</div>
7450
</div>
75-
<div class="form-group">
76-
<button type="button" class="btn btn-primary" onclick="calculateBMI()">Calculate</button>
77-
<button type="button" class="btn btn-secondary mt-2" onclick="clearForm()">Clear</button>
51+
</div>
52+
<div class="input-group imperial-inputs" style="display: none;">
53+
<label for="weightPounds">Weight</label>
54+
<div class="input-field">
55+
<input type="number" id="weightPounds" min="20" max="1000" placeholder="Enter your weight" aria-describedby="weightImperialError">
56+
<span class="unit">lb</span>
57+
<div class="error-message" id="weightImperialError">Please enter a valid weight (20-1000 lb)</div>
7858
</div>
79-
</form>
80-
<div class="result" id="result"></div>
81-
<div class="about mt-4">
82-
<h2>About BMI Calculator</h2>
83-
<p>Body Mass Index (BMI) is a number calculated from a person's weight and height to determine a person's body fatness or skinniness, and is defined as the individual's body mass divided by the square of their height. The BMI Calculator tool helps you to find out your BMI value and the corresponding weight category.</p>
8459
</div>
60+
<div class="btn-row">
61+
<button type="button" class="btn btn-primary" id="calculateBtn">Calculate BMI</button>
62+
<button type="button" class="btn btn-secondary" id="clearBtn">Clear</button>
63+
</div>
64+
</form>
65+
<div class="results" id="results">
66+
<div class="result-header">
67+
<p class="bmi-value" id="bmiValue">25.0</p>
68+
<p class="bmi-category" id="bmiCategory">Normal weight</p>
69+
</div>
70+
<div class="bmi-scale">
71+
<div class="bmi-marker" id="bmiMarker"></div>
72+
</div>
73+
<div class="scale-labels">
74+
<span>16</span>
75+
<span>18.5</span>
76+
<span>25</span>
77+
<span>30</span>
78+
<span>35</span>
79+
<span>40</span>
80+
</div>
81+
<div class="bmi-description" id="bmiDescription"></div>
82+
</div>
83+
<div class="about-section">
84+
<h2>About BMI Calculator</h2>
85+
<p>Body Mass Index (BMI) is a measurement of body fat based on height and weight. While BMI is not perfect, it provides a useful starting point for evaluating health risks. A healthy BMI typically ranges from 18.5 to 24.9.</p>
86+
<p style="margin-top: 10px;">This calculator provides an estimate only and is not intended to replace professional medical advice.</p>
8587
</div>
86-
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" crossorigin="anonymous"></script>
87-
<script>
88-
function calculateBMI() {
89-
var height = parseFloat($("#height").val());
90-
var weight = parseFloat($("#weight").val());
91-
var bmi = weight / Math.pow(height / 100, 2);
92-
var category = "";
93-
if (bmi < 18.5) {
94-
category = "Underweight";
95-
} else if (bmi < 25) {
96-
category = "Normal weight";
97-
} else if (bmi < 30) {
98-
category = "Overweight";
88+
</div>
89+
<footer>
90+
<p>© 2025 BMI Calculator | For informational purposes only</p>
91+
</footer>
92+
<script>
93+
const metricToggle = document.getElementById('metric');
94+
const imperialToggle = document.getElementById('imperial');
95+
const metricInputs = document.querySelectorAll('.metric-inputs');
96+
const imperialInputs = document.querySelectorAll('.imperial-inputs');
97+
const heightInput = document.getElementById('height');
98+
const weightInput = document.getElementById('weight');
99+
const heightFeetInput = document.getElementById('heightFeet');
100+
const heightInchesInput = document.getElementById('heightInches');
101+
const weightPoundsInput = document.getElementById('weightPounds');
102+
const calculateBtn = document.getElementById('calculateBtn');
103+
const clearBtn = document.getElementById('clearBtn');
104+
const resultsSection = document.getElementById('results');
105+
const bmiValueElement = document.getElementById('bmiValue');
106+
const bmiCategoryElement = document.getElementById('bmiCategory');
107+
const bmiMarker = document.getElementById('bmiMarker');
108+
const bmiDescription = document.getElementById('bmiDescription');
109+
110+
metricToggle.addEventListener('click', () => {
111+
metricToggle.classList.add('active');
112+
imperialToggle.classList.remove('active');
113+
metricInputs.forEach(el => el.style.display = 'block');
114+
imperialInputs.forEach(el => el.style.display = 'none');
115+
});
116+
117+
imperialToggle.addEventListener('click', () => {
118+
imperialToggle.classList.add('active');
119+
metricToggle.classList.remove('active');
120+
imperialInputs.forEach(el => el.style.display = 'block');
121+
metricInputs.forEach(el => el.style.display = 'none');
122+
});
123+
124+
function validateMetricInputs() {
125+
let isValid = true;
126+
if (!heightInput.value || heightInput.value < 50 || heightInput.value > 300) {
127+
document.getElementById('heightError').style.display = 'block';
128+
isValid = false;
129+
} else {
130+
document.getElementById('heightError').style.display = 'none';
131+
}
132+
if (!weightInput.value || weightInput.value < 10 || weightInput.value > 500) {
133+
document.getElementById('weightError').style.display = 'block';
134+
isValid = false;
135+
} else {
136+
document.getElementById('weightError').style.display = 'none';
137+
}
138+
return isValid;
139+
}
140+
141+
function validateImperialInputs() {
142+
let isValid = true;
143+
if (!heightFeetInput.value || heightFeetInput.value < 1 || heightFeetInput.value > 8 ||
144+
!heightInchesInput.value || heightInchesInput.value < 0 || heightInchesInput.value > 11) {
145+
document.getElementById('heightImperialError').style.display = 'block';
146+
isValid = false;
147+
} else {
148+
document.getElementById('heightImperialError').style.display = 'none';
149+
}
150+
if (!weightPoundsInput.value || weightPoundsInput.value < 20 || weightPoundsInput.value > 1000) {
151+
document.getElementById('weightImperialError').style.display = 'block';
152+
isValid = false;
153+
} else {
154+
document.getElementById('weightImperialError').style.display = 'none';
155+
}
156+
return isValid;
157+
}
158+
159+
function calculateBMI() {
160+
let bmi;
161+
if (metricToggle.classList.contains('active')) {
162+
if (!validateMetricInputs()) return;
163+
const height = parseFloat(heightInput.value);
164+
const weight = parseFloat(weightInput.value);
165+
bmi = weight / Math.pow(height / 100, 2);
166+
} else {
167+
if (!validateImperialInputs()) return;
168+
const heightFeet = parseFloat(heightFeetInput.value);
169+
const heightInches = parseFloat(heightInchesInput.value);
170+
const totalInches = (heightFeet * 12) + heightInches;
171+
const weightPounds = parseFloat(weightPoundsInput.value);
172+
bmi = (weightPounds / Math.pow(totalInches, 2)) * 703;
173+
}
174+
displayResults(bmi);
175+
}
176+
177+
function displayResults(bmi) {
178+
const roundedBMI = Math.round(bmi * 10) / 10;
179+
let category, description, color;
180+
181+
if (bmi < 16) {
182+
category = "Severe Underweight";
183+
description = "Your BMI indicates severe underweight, which may lead to health issues including weakened immune system, nutritional deficiencies, and hormonal imbalances. Please consult a healthcare professional for guidance.";
184+
color = "#3498db";
185+
} else if (bmi < 18.5) {
186+
category = "Underweight";
187+
description = "Your BMI indicates underweight. This may be normal for some individuals, but could suggest inadequate nutrition. Consider consulting a healthcare professional to ensure your diet meets your needs.";
188+
color = "#3498db";
189+
} else if (bmi < 25) {
190+
category = "Normal Weight";
191+
description = "Your BMI falls within the normal weight range associated with healthy outcomes for most adults. Maintain a balanced diet and regular physical activity to preserve your health.";
192+
color = "#2ecc71";
193+
} else if (bmi < 30) {
194+
category = "Overweight";
195+
description = "Your BMI indicates overweight. This may increase risk for certain health conditions. Focus on balanced nutrition and regular physical activity, and consider consulting a healthcare professional for personalized advice.";
196+
color = "#f1c40f";
197+
} else if (bmi < 35) {
198+
category = "Obesity Class I";
199+
description = "Your BMI indicates Class I obesity, which increases risk for conditions like heart disease, diabetes, and high blood pressure. Consider consulting healthcare professionals about lifestyle changes or weight management strategies.";
200+
color = "#e67e22";
201+
} else if (bmi < 40) {
202+
category = "Obesity Class II";
203+
description = "Your BMI indicates Class II obesity, which is associated with higher health risks. It's strongly recommended to seek advice from healthcare professionals about appropriate weight management strategies.";
204+
color = "#e74c3c";
205+
} else {
206+
category = "Obesity Class III";
207+
description = "Your BMI indicates Class III obesity, which carries significant health risks. Please consult healthcare professionals promptly for comprehensive assessment and guidance on appropriate interventions.";
208+
color = "#c0392b";
209+
}
210+
211+
bmiValueElement.textContent = roundedBMI.toFixed(1);
212+
bmiCategoryElement.textContent = category;
213+
bmiDescription.innerHTML = description;
214+
215+
const percentage = Math.min(100, Math.max(0, ((bmi - 16) / (40 - 16)) * 100));
216+
bmiMarker.style.left = `${percentage}%`;
217+
bmiValueElement.style.color = color;
218+
219+
resultsSection.style.display = 'block';
220+
resultsSection.classList.add('fade-in');
221+
222+
saveToLocalStorage();
223+
}
224+
225+
function saveToLocalStorage() {
226+
const bmiData = {
227+
metric: metricToggle.classList.contains('active'),
228+
height: heightInput.value,
229+
weight: weightInput.value,
230+
heightFeet: heightFeetInput.value,
231+
heightInches: heightInchesInput.value,
232+
weightPounds: weightPoundsInput.value
233+
};
234+
localStorage.setItem('bmiData', JSON.stringify(bmiData));
235+
}
236+
237+
function loadFromLocalStorage() {
238+
const savedData = localStorage.getItem('bmiData');
239+
if (savedData) {
240+
const bmiData = JSON.parse(savedData);
241+
if (bmiData.metric) {
242+
metricToggle.click();
243+
heightInput.value = bmiData.height;
244+
weightInput.value = bmiData.weight;
99245
} else {
100-
category = "Obese";
246+
imperialToggle.click();
247+
heightFeetInput.value = bmiData.heightFeet;
248+
heightInchesInput.value = bmiData.heightInches;
249+
weightPoundsInput.value = bmiData.weightPounds;
101250
}
102-
$("#result").html("<i class='fas fa-weight'></i>Your BMI is " + bmi.toFixed(1) + " (" + category + ")");
103251
}
104-
function clearForm() {
105-
$("#height").val("");
106-
$("#weight").val("");
107-
$("#result").html("");
252+
}
253+
254+
function clearForm() {
255+
heightInput.value = '';
256+
weightInput.value = '';
257+
heightFeetInput.value = '';
258+
heightInchesInput.value = '';
259+
weightPoundsInput.value = '';
260+
261+
document.getElementById('heightError').style.display = 'none';
262+
document.getElementById('weightError').style.display = 'none';
263+
document.getElementById('heightImperialError').style.display = 'none';
264+
document.getElementById('weightImperialError').style.display = 'none';
265+
266+
resultsSection.style.display = 'none';
267+
localStorage.removeItem('bmiData');
268+
}
269+
270+
calculateBtn.addEventListener('click', calculateBMI);
271+
clearBtn.addEventListener('click', clearForm);
272+
loadFromLocalStorage();
273+
274+
document.addEventListener('keydown', function(e) {
275+
if (e.key === 'Enter' && document.activeElement !== calculateBtn && document.activeElement !== clearBtn) {
276+
calculateBtn.click();
108277
}
109-
</script>
110-
<script src="../sidebar.js"></script>
278+
});
279+
</script>
280+
<script src="../logo.js"></script>
111281
</body>
112282
</html>

0 commit comments

Comments
 (0)