Public Holiday Finder with HTML, CSS & JavaScript

30 DAYS 30 PROJECT CHALLENGE

Day #30

Project Overview

The Public Holiday Finder is a beginner-friendly project built with HTML, CSS, and JavaScript. It allows users to search for public holidays by selecting a country and entering a year. It also supports countries like United States, India, the United Kingdom, Australia, and Canada. This project uses different data sources depending on the selected country. For India, JavaScript fetches Google’s public holiday calendar in ICS format and processes the calendar data to find holidays for the selected year. For other supported countries, the application uses the Nager.Date public API to retrieve holiday information. 

Each holiday appears with its name and date, while results for other countries also include the local holiday name. This makes the project a useful final challenge for learning API integration and dynamic data rendering.

Key Features

  • Country Selection: Users can select a country from the dropdown, including the United States, India Australia, Canada etc. and the selected country determines which holiday data source the application uses.
  • Year Search: Users can enter a specific year to find public holidays for that period. If no year is entered, JavaScript automatically uses the current year.
  • India Holiday Support: When users select India, the project retrieves holiday information from Google’s public Indian holiday calendar and it processes the returned ICS calendar data to find holidays.
  • International Holiday API: For countries other than India, the application uses the Nager.Date public API, which provides a simple way to retrieve public holiday information using the selected country and year.
  • Multiple Data Sources: The project uses two different approaches for retrieving holiday information. Therefore, it demonstrates how one application can work with different external data formats and services.
  • Holiday Name Display: Each result shows the name of the public holiday. For countries using Nager.Date, the application also displays the local holiday name returned by the API.
  • Holiday Date: The application displays the date for every holiday in the result list. This allows users to quickly check when each public holiday occurs.
  • Loading Feedback: The holiday list immediately displays "Loading..." when a search begins. As a result, users receive feedback while the application processes the external request.
  • Year-Based Filtering: For India’s ICS data, JavaScript extracts the event date and filters the results using the selected year. Therefore, only holidays belonging to the requested year appear in the list.
  • No Holiday Handling: If the Indian holiday calendar does not contain matching holidays for the selected year, the application displays "No holidays found for this year." 
  • Error Handling: The project handles failed requests separately for India and other countries. It displays an appropriate error message when the required holiday data cannot be retrieved.
  • Dynamic Holiday Cards: JavaScript creates a separate .holiday-card for each holiday and inserts the information into the page dynamically. Therefore, the same HTML structure can display different holiday lists.
  • Clean Interface: The application uses a centered card with country and year controls, a full-width button, and a vertical holiday list. This keeps the search process simple and the results easy to scan.

What You'll Learn

  • Learn how to retrieve public holiday data from external sources.
  • How to use fetch() for asynchronous requests.
  • How to work with both JSON and text-based API responses.
  • How to use an ICS calendar URL in a JavaScript application.
  • How to process text using split() and match().
  • Understand how regular expressions can extract holiday names and dates.
  • How to filter results based on a selected year.
  • How to use map() and filter() to process arrays.
  • How to use forEach() to display multiple holiday records.
  • How to generate dynamic HTML using template literals.
  • How to work with different API sources based on user input.
  • How to display loading and error messages.
  • How to handle failed API requests with .catch().

HTML Code

HTML creates the basic structure of the Public Holiday Finder. The .holiday-container holds the complete application, while the <h2> displays the project title. Inside .controls, the #country dropdown provides the five available country options, while the #year input allows users to enter the required year.

Next, the Find Holidays button calls the getHolidays() function when clicked. The #holidayList element starts empty and provides the area where JavaScript displays the holiday cards or error messages. Finally, the page loads script.js after the main HTML structure so the JavaScript can access these elements.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Public Holiday Finder</title>
  <link rel="stylesheet" href="style.css" />
</head>
<body>

  <div class="holiday-container">
    <h2>Public Holiday Finder</h2>

    <div class="controls">
      <select id="country">
        <option value="US">United States</option>
        <option value="IN">India</option>
        <option value="GB">United Kingdom</option>
        <option value="AU">Australia</option>
        <option value="CA">Canada</option>
      </select>

      <input type="number" id="year" placeholder="Year (e.g. 2025)" />
    </div>

    <button onclick="getHolidays()">Find Holidays</button>

    <div id="holidayList" class="holiday-list"></div>
  </div>

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

CSS Code

CSS controls the layout and appearance of the Public Holiday Finder. The universal * selector sets box-sizing: border-box and applies the Segoe UI font, while the body uses Flexbox to center the application horizontally with some top spacing.

This .holiday-container creates the main white card with a fixed width, padding, rounded corners, and a shadow. Next, .controls uses Flexbox to place the country dropdown and year input side by side, while select and input use flex: 1 so both controls share the available space.

The button selector creates the full-width Find Holidays button with padding, rounded corners, and a pointer cursor. Finally, .holiday-list arranges the results vertically, .holiday-card adds borders and spacing to each holiday, and .error displays error messages in red.

* {
  box-sizing: border-box;
  font-family: "Segoe UI", sans-serif;
}

body {
  min-height: 100vh;
  background: #042453;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding-top: 40px;
}

.holiday-container {
  background: #ffffff;
  width: 420px;
  padding: 25px;
  border-radius: 14px;
  box-shadow: 0 20px 40px rgba(0,0,0,0.25);
}

h2 {
  text-align: center;
  margin-bottom: 15px;
}

.controls {
  display: flex;
  gap: 10px;
  margin-bottom: 10px;
}

select,
input {
  flex: 1;
  padding: 10px;
  border-radius: 6px;
  border: 1px solid #ccc;
}

button {
  width: 100%;
  padding: 10px;
  border: none;
  border-radius: 6px;
  background: #4f46e5;
  color: white;
  cursor: pointer;
  margin-bottom: 15px;
}

.holiday-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.holiday-card {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 10px;
  font-size: 14px;
}

.holiday-card span {
  font-size: 12px;
  color: #555;
}

.error {
  color: red;
  text-align: center;
}

Javascript Code

JavaScript handles the country and year selection, API requests, data processing, and result rendering. The getHolidays() function first reads the selected country and year. If the year field is empty, it uses new Date().getFullYear() to select the current year. It then displays "Loading..." before checking which country the user selected.

When the country is "IN", the code uses Google’s Indian holiday calendar in ICS format. It sends the calendar URL through AllOrigins, converts the response into text with res.text(), and splits the calendar using "BEGIN:VEVENT". The code then uses regular expressions to extract SUMMARY and DTSTART;VALUE=DATE, converts the date into YYYY-MM-DD format, and filters the holidays using the selected year.

For other countries, the function requests data from the Nager.Date API using the selected year and country code. The response is converted with res.json(), and forEach() creates a holiday card containing localName, name, and date. Finally, separate .catch() blocks display error messages when either the India calendar or the Nager.Date request fails.

const holidayList = document.getElementById("holidayList");

function getHolidays() {
  const country = document.getElementById("country").value;
  const year = document.getElementById("year").value || new Date().getFullYear();

  holidayList.innerHTML = "Loading...";

  // 🇮🇳 INDIA → Google Calendar (ICS)
  if (country === "IN") {
    const icsUrl =
      "https://calendar.google.com/calendar/ical/en.indian%23holiday%40group.v.calendar.google.com/public/basic.ics";

    fetch(`https://api.allorigins.win/raw?url=${encodeURIComponent(icsUrl)}`)
      .then(res => res.text())
      .then(data => {
        const events = data.split("BEGIN:VEVENT").slice(1);

        const holidays = events
          .map(event => {
            const summary = event.match(/SUMMARY:(.*)/);
            const date = event.match(/DTSTART;VALUE=DATE:(\d+)/);
            if (!summary || !date) return null;

            return {
              name: summary[1],
              date:
                date[1].slice(0, 4) +
                "-" +
                date[1].slice(4, 6) +
                "-" +
                date[1].slice(6, 8)
            };
          })
          .filter(h => h && h.date.startsWith(year));

        if (!holidays.length) {
          holidayList.innerHTML =
            "<p class='error'>No holidays found for this year.</p>";
          return;
        }

        holidayList.innerHTML = "";
        holidays.forEach(h => {
          holidayList.innerHTML += `
            <div class="holiday-card">
              <strong>${h.name}</strong><br>
              <span>${h.date}</span>
            </div>
          `;
        });
      })
      .catch(() => {
        holidayList.innerHTML =
          "<p class='error'>Unable to fetch India holidays.</p>";
      });

    return;
  }

  // 🌍 OTHER COUNTRIES → Nager.Date
  fetch(`https://date.nager.at/api/v3/PublicHolidays/${year}/${country}`)
    .then(res => res.json())
    .then(data => {
      holidayList.innerHTML = "";
      data.forEach(h => {
        holidayList.innerHTML += `
          <div class="holiday-card">
            <strong>${h.localName}</strong><br>
            ${h.name}<br>
            <span>${h.date}</span>
          </div>
        `;
      });
    })
    .catch(() => {
      holidayList.innerHTML =
        "<p class='error'>Unable to fetch holidays.</p>";
    });
}
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Related Projects

Day 20 : Form Auto-Save (Local Storage)

Automatically saves form input data and restores it on reload.

Concepts: LocalStorage usage, input event handling, persistent UI state.

Day 27 : Recipe Finder App

Searches and displays recipes dynamically using a public food API.

Concepts: API integration, async data fetching, dynamic UI rendering.

Day 28 : Language Translator

Translates text between multiple languages using a public translation API.

Concepts: API integration, async data handling, form handling, dynamic UI updates.