Skip to content

Commit 162e16d

Browse files
committed
Made the webpage prettier, added a reset button and a light/dark mode button
1 parent badce31 commit 162e16d

File tree

3 files changed

+276
-21
lines changed

3 files changed

+276
-21
lines changed

index.html

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,80 @@
1-
<html>
2-
<head>
3-
<script src="script.js"></script>
4-
<link rel="stylesheet" href="style.css">
5-
</head>
6-
<body>
7-
<h1>Simple Interest Calculator</h1>
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>Simple Interest Calculator</title>
87

9-
Amount <input type="number" id="principal"> <br/>
10-
Rate <input type="number" id="rate"> <br/>
11-
No. of Years <input type="number" id="years"> <br/>
12-
Interest : <span id="result"></span><br>
8+
<!-- Styles first -->
9+
<link rel="stylesheet" href="style.css" />
1310

14-
<button onclick="compute()">Compute</button>
15-
</body>
11+
<!-- Script deferred so DOM is ready when it runs -->
12+
<script src="script.js" defer></script>
13+
</head>
14+
<body>
15+
<main class="container" role="main" aria-labelledby="page-title">
16+
<header class="header">
17+
<h1 id="page-title">Simple Interest Calculator</h1>
18+
<div class="header-actions">
19+
<button id="theme-toggle" type="button" aria-pressed="false" aria-label="Toggle light and dark theme">
20+
☀️ Light
21+
</button>
22+
</div>
23+
</header>
24+
25+
<form id="si-form" novalidate>
26+
<div class="row">
27+
<label for="principal">Amount</label>
28+
<input
29+
type="number"
30+
id="principal"
31+
name="principal"
32+
min="0"
33+
step="0.01"
34+
inputmode="decimal"
35+
placeholder="e.g. 1000"
36+
required
37+
/>
38+
</div>
39+
40+
<div class="row">
41+
<label for="rate">Rate (% per year)</label>
42+
<input
43+
type="number"
44+
id="rate"
45+
name="rate"
46+
min="0"
47+
step="0.01"
48+
inputmode="decimal"
49+
placeholder="e.g. 5"
50+
required
51+
/>
52+
</div>
53+
54+
<div class="row">
55+
<label for="years">No. of Years</label>
56+
<input
57+
type="number"
58+
id="years"
59+
name="years"
60+
min="0"
61+
step="0.01"
62+
inputmode="decimal"
63+
placeholder="e.g. 2"
64+
required
65+
/>
66+
</div>
67+
68+
<div class="row result-row" aria-live="polite">
69+
<strong>Interest:</strong>
70+
<span id="result" class="result-value"></span>
71+
</div>
72+
73+
<div class="buttons">
74+
<button type="submit" id="compute" class="primary">Compute</button>
75+
<button type="button" id="reset" class="ghost">Reset</button>
76+
</div>
77+
</form>
78+
</main>
79+
</body>
1680
</html>

script.js

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,103 @@
1-
function compute()
2-
{
3-
p = document.getElementById("principal").value;
4-
5-
}
6-
1+
// script.js
2+
// Modern, safe code: event-driven, input validation, accessible updates.
3+
4+
document.addEventListener('DOMContentLoaded', () => {
5+
const form = document.getElementById('si-form');
6+
const principalEl = document.getElementById('principal');
7+
const rateEl = document.getElementById('rate');
8+
const yearsEl = document.getElementById('years');
9+
const resultEl = document.getElementById('result');
10+
const resetBtn = document.getElementById('reset');
11+
const themeToggle = document.getElementById('theme-toggle');
12+
13+
// Format numbers to 2 decimal places with locale formatting
14+
function formatAmount(n) {
15+
return Number(n).toLocaleString(undefined, {
16+
minimumFractionDigits: 2,
17+
maximumFractionDigits: 2
18+
});
19+
}
20+
21+
function showError(message) {
22+
resultEl.textContent = message;
23+
resultEl.classList.add('error');
24+
}
25+
26+
function clearError() {
27+
resultEl.classList.remove('error');
28+
}
29+
30+
function compute() {
31+
// parseFloat used to accept decimal input
32+
const p = parseFloat(principalEl.value);
33+
const r = parseFloat(rateEl.value);
34+
const y = parseFloat(yearsEl.value);
35+
36+
// Validate numbers and non-negative
37+
if (Number.isNaN(p) || Number.isNaN(r) || Number.isNaN(y)) {
38+
showError('Please enter numeric values for all fields.');
39+
return;
40+
}
41+
if (p < 0 || r < 0 || y < 0) {
42+
showError('Please enter non-negative values.');
43+
return;
44+
}
45+
46+
clearError();
47+
48+
// Simple interest formula: interest = (p * r * y) / 100
49+
const interest = (p * r * y) / 100;
50+
const total = p + interest;
51+
52+
resultEl.innerHTML = `${formatAmount(interest)} (Interest). Total: ${formatAmount(total)}`;
53+
}
54+
55+
// handle form submit
56+
form.addEventListener('submit', (ev) => {
57+
ev.preventDefault();
58+
compute();
59+
});
60+
61+
// reset handler
62+
resetBtn.addEventListener('click', () => {
63+
form.reset();
64+
resultEl.textContent = '—';
65+
resultEl.classList.remove('error');
66+
});
67+
68+
// make compute available globally in case someone expects window.compute()
69+
window.compute = compute;
70+
71+
/* -----------------------
72+
Theme toggle (light/dark)
73+
- persists selection in localStorage
74+
- respects system preference if no saved choice
75+
----------------------- */
76+
const THEME_KEY = 'si.theme';
77+
78+
function applyTheme(theme) {
79+
document.documentElement.setAttribute('data-theme', theme);
80+
const isDark = theme === 'dark';
81+
themeToggle.setAttribute('aria-pressed', String(isDark));
82+
themeToggle.textContent = isDark ? '🌙 Dark' : '☀️ Light';
83+
localStorage.setItem(THEME_KEY, theme);
84+
}
85+
86+
// Initialize theme
87+
(function initTheme() {
88+
const stored = localStorage.getItem(THEME_KEY);
89+
if (stored === 'dark' || stored === 'light') {
90+
applyTheme(stored);
91+
return;
92+
}
93+
// fallback to system preference
94+
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
95+
applyTheme(prefersDark ? 'dark' : 'light');
96+
})();
97+
98+
// Toggle handler
99+
themeToggle.addEventListener('click', () => {
100+
const current = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
101+
applyTheme(current === 'dark' ? 'light' : 'dark');
102+
});
103+
});

style.css

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,98 @@
1-
body {background-color:tan;}
2-
h1{color:green;}
1+
/* Themeable variables */
2+
:root{
3+
--bg: #f5efe6;
4+
--card: #ffffff;
5+
--accent: #2e8b57;
6+
--accent-600: #237246;
7+
--text: #1f2933;
8+
--muted: #505a63;
9+
--input-bg: #ffffff;
10+
--input-border: #e6e6e6;
11+
--input-text: #1f2933;
12+
--shadow: 0 10px 30px rgba(18, 24, 28, 0.08);
13+
--soft-shadow: 0 6px 18px rgba(18, 24, 28, 0.06);
14+
--btn-radius: 10px;
15+
--focus-ring: rgba(46,139,87,0.18);
16+
--surface: rgba(255,255,255,0.6);
17+
}
318

19+
[data-theme="dark"]{
20+
--bg: #071021;
21+
--card: #071827;
22+
--accent: #60b38a;
23+
--accent-600: #4aa073;
24+
--text: #e6eef1;
25+
--muted: #9fb3b7;
26+
--input-bg: #071827;
27+
--input-border: #133042;
28+
--input-text: #e6eef1;
29+
--shadow: 0 8px 24px rgba(2, 8, 15, 0.6);
30+
--soft-shadow: 0 6px 18px rgba(2,8,15,0.5);
31+
--focus-ring: rgba(96,179,138,0.18);
32+
--surface: rgba(255,255,255,0.02);
33+
}
434

35+
/* Reset & base */
36+
* { box-sizing: border-box; }
37+
html,body { height: 100%; margin: 0; font-family: Inter, ui-sans-serif, system-ui; color: var(--text); background: var(--bg); }
38+
39+
body { display: flex; align-items: center; justify-content: center; padding: 2rem; min-height: 100vh; }
40+
41+
.container {
42+
width: 100%;
43+
max-width: 520px;
44+
background: var(--card);
45+
border-radius: 12px;
46+
padding: 1.5rem;
47+
box-shadow: var(--shadow);
48+
}
49+
50+
/* Header */
51+
.header { display:flex; align-items:center; justify-content:space-between; margin-bottom:1rem; }
52+
h1 { margin:0; color: var(--accent-600); font-size:1.25rem; }
53+
54+
/* Theme toggle */
55+
#theme-toggle{
56+
padding:0.35rem 0.6rem;
57+
border-radius:999px;
58+
border:none;
59+
cursor:pointer;
60+
background: var(--surface);
61+
font-weight:600;
62+
color: var(--text); /* <- changed: matches page text for light/dark mode */
63+
transition: transform 120ms ease, box-shadow 160ms ease, opacity 160ms ease;
64+
}
65+
#theme-toggle:active { transform: translateY(1px); }
66+
#theme-toggle:hover { opacity: 0.95; }
67+
68+
/* Form rows */
69+
.row { display:flex; gap:0.8rem; align-items:center; margin-bottom:0.85rem; }
70+
.row label { min-width:130px; font-weight:600; color: var(--muted); font-size:0.95rem; }
71+
.row input[type="number"]{
72+
flex:1; padding:0.65rem 0.75rem; border-radius:10px; border:1px solid var(--input-border);
73+
background: var(--input-bg); color: var(--input-text); outline:none; box-shadow: var(--soft-shadow);
74+
font-size:1rem; transition: all 0.15s ease;
75+
}
76+
.row input:focus { box-shadow: 0 8px 22px var(--focus-ring); border-color: var(--accent-600); }
77+
78+
/* Result row */
79+
.result-row { align-items: baseline; margin-top: 0.25rem; padding-top: 0.5rem; border-top:1px dashed rgba(0,0,0,0.1); }
80+
.result-value { margin-left:0.65rem; font-weight:700; font-size:1rem; }
81+
82+
/* Buttons area */
83+
.buttons { display:flex; gap:0.6rem; margin-top:1.1rem; justify-content:center; }
84+
button { padding:0.55rem 0.95rem; border-radius:var(--btn-radius); border:none; font-weight:700; font-size:0.95rem; cursor:pointer; transition: all 0.12s ease; }
85+
button:active { transform: translateY(1px); }
86+
button.primary { background: linear-gradient(180deg, var(--accent), var(--accent-600)); color:white; }
87+
button.ghost { background:transparent; border:1px solid rgba(0,0,0,0.1); color: var(--muted); }
88+
89+
/* Error styling */
90+
.result-value.error { color:#b00020; }
91+
92+
/* Responsive */
93+
@media (max-width: 420px){
94+
.row { flex-direction: column; align-items: stretch; }
95+
.row label { min-width: 0; }
96+
.header { gap: 0.5rem; }
97+
.container { padding: 1rem; border-radius: 10px; }
98+
}

0 commit comments

Comments
 (0)