What You'll Build

An age calculator that takes a date of birth and shows the exact age in years, months, and days. It updates live as you pick a different date, handles edge cases like leap years, and shows the total days lived.

  • Accepts a date of birth from the user
  • Calculates exact age — years, months, and days
  • Shows the total days you've been alive
  • Validates input (no future dates)
  • Updates instantly as you change the date
▶ Live Preview — this is what you'll build

What You'll Learn

1

Build the HTML Structure

Create index.html. The structure is similar to the BMI project — a card, a date input, and a result area.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Age Calculator</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="card">
        <h1>🎂 Age Calculator</h1>
        <p class="sub">Pick your date of birth</p>

        <label for="dob">Date of Birth</label>
        <input type="date" id="dob">

        <div class="result" id="result">
            <div class="years" id="years">—</div>
            <div class="years-label">Years Old</div>
            <div class="detail" id="detail">Enter your date of birth</div>
            <div class="days" id="days"></div>
        </div>

        <div class="error" id="error"></div>
    </div>

    <script src="script.js"></script>
</body>
</html>

Key HTML piece: <input type="date"> gives you a native date picker in every modern browser — no library needed. The max attribute (set in JavaScript) prevents future dates.

2

Style It with CSS

CSS
* { box-sizing: border-box; margin: 0; padding: 0; }

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: #f1f5f9;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 20px;
    color: #0f172a;
}

.card {
    background: #fff;
    border-radius: 16px;
    padding: 28px;
    max-width: 400px;
    width: 100%;
    box-shadow: 0 10px 40px rgba(15,23,42,0.1);
}

h1 { font-size: 1.4rem; margin-bottom: 4px; }
.sub { color: #64748b; font-size: 0.85rem; margin-bottom: 20px; }

label {
    display: block;
    font-size: 0.82rem;
    font-weight: 600;
    color: #334155;
    margin-bottom: 6px;
}

input[type="date"] {
    width: 100%;
    padding: 12px 14px;
    border: 2px solid #e2e8f0;
    border-radius: 10px;
    font-size: 1rem;
    font-weight: 600;
    outline: none;
    transition: 0.2s;
    font-family: inherit;
}

input[type="date"]:focus {
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59,130,246,0.15);
}

.result {
    margin-top: 20px;
    padding: 20px;
    border-radius: 12px;
    text-align: center;
    background: #f1f5f9;
}

.result .years {
    font-size: 3rem;
    font-weight: 900;
    line-height: 1;
    color: #3b82f6;
    font-family: "Courier New", monospace;
}

.result .years-label {
    font-size: 0.85rem;
    color: #64748b;
    margin-top: 6px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.result .detail {
    font-size: 0.9rem;
    color: #475569;
    margin-top: 12px;
    font-family: "Courier New", monospace;
}

.result .days {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid #cbd5e1;
    font-size: 0.85rem;
    color: #64748b;
}

.result .days span {
    color: #16a34a;
    font-weight: 700;
    font-family: "Courier New", monospace;
}

.error {
    color: #dc2626;
    font-size: 0.85rem;
    margin-top: 12px;
    text-align: center;
    display: none;
}

.error.show { display: block; }
3

Write the JavaScript Logic

This is the interesting part. The Date object is trickier than it looks — that's why this project matters.

JavaScript
// 1. Grab elements
const dobEl    = document.getElementById("dob");
const yearsEl  = document.getElementById("years");
const detailEl = document.getElementById("detail");
const daysEl   = document.getElementById("days");
const errorEl  = document.getElementById("error");

// 2. Prevent future dates
const todayStr = new Date().toISOString().split("T")[0];
dobEl.max = todayStr;

// 3. Main calculate function
function calculate() {
    const value = dobEl.value;
    errorEl.classList.remove("show");

    // Guard: empty input
    if (!value) {
        yearsEl.textContent = "—";
        detailEl.textContent = "Enter your date of birth";
        daysEl.textContent = "";
        return;
    }

    // 4. Parse the date — add T00:00:00 to force local timezone
    const dob = new Date(value + "T00:00:00");
    const now = new Date();

    // Guard: future date
    if (dob > now) {
        errorEl.textContent = "Date of birth can't be in the future.";
        errorEl.classList.add("show");
        yearsEl.textContent = "—";
        detailEl.textContent = "Invalid date";
        daysEl.textContent = "";
        return;
    }

    // 5. Calculate years, months, days
    let years  = now.getFullYear() - dob.getFullYear();
    let months = now.getMonth()   - dob.getMonth();
    let days   = now.getDate()    - dob.getDate();

    // If days is negative, borrow from the previous month
    if (days < 0) {
        months--;
        // Day 0 of next month = last day of previous month
        const prevMonth = new Date(now.getFullYear(), now.getMonth(), 0);
        days += prevMonth.getDate();
    }
    if (months < 0) {
        years--;
        months += 12;
    }

    // 6. Total days alive
    const diffMs = now - dob;
    const totalDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

    // 7. Render
    yearsEl.textContent = years;
    detailEl.textContent =
        years  + " year"  + (years  !== 1 ? "s" : "") + ", " +
        months + " month" + (months !== 1 ? "s" : "") + ", " +
        days   + " day"   + (days   !== 1 ? "s" : "");

    daysEl.innerHTML = "Total: <span>" + totalDays.toLocaleString() + "</span> days alive";
}

// 8. Live updates
dobEl.addEventListener("change", calculate);
dobEl.addEventListener("input", calculate);

Save all three files, open index.html, and pick a date. You should see your exact age in years, months, and days, plus the total days you've been alive.

4

Understand the Tricky Parts

Why add T00:00:00 to the date string?

new Date("1990-05-15") is interpreted as UTC midnight, but new Date("1990-05-15T00:00:00") is interpreted as local midnight. In timezones behind UTC (like the US), this difference can shift the date by one day. Adding T00:00:00 ensures the date is treated consistently in the user's local timezone.

This is a famous JavaScript gotcha. new Date("2024-01-01") will show as December 31, 2023 in US timezones. The T00:00:00 suffix is the standard fix.

Why is getMonth() 0-indexed?

JavaScript's getMonth() returns 0 for January and 11 for December — a quirk from the 1990s that can't be fixed without breaking every website in the world. In this project we don't care because we're only subtracting months, and the difference is the same either way.

Why borrow from the previous month?

If today is May 10 and you were born May 15, getDate() returns 10 − 15 = −5. Negative days don't make sense, so we borrow from the month: −5 + (days in April) = 25, and decrement the months count.

The trick new Date(year, month, 0) gives you the last day of the previous month — that's how we know how many days to borrow.

5

Practice Challenges

🟢 Beginner

🟡 Intermediate

🔴 Advanced

Compare your solution: We built a full production version with all the advanced features — see the live Age Calculator.

Full Source Code

Common Mistakes & Fixes

❌ "My age is off by one day"

This is almost always the UTC vs. local timezone issue. Make sure you add "T00:00:00" to the date string.

❌ "The input shows no calendar on mobile"

Some older Android browsers don't support <input type="date"> natively. It degrades gracefully — users can still type the date. For full compatibility, use a library like Flatpickr (covered in a later project).

❌ "My 'total days' number is off"

Check that you're using Math.floor() and not rounding. Days should be whole numbers — fractions would represent partial days.

What to Build Next