<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged github-pages</title>
<link>https://coderlegion.com/tag/github-pages</link>
<description>Powered by Question2Answer</description>
<item>
<title>How a Dead Polyfill.io Link Got My Entire GitHub Pages Domain Flagged as Dangerous</title>
<link>https://coderlegion.com/22868/how-a-dead-polyfill-io-link-got-my-entire-github-pages-domain-flagged-as-dangerous</link>
<description>&lt;p&gt;I recently published a digital anthology project at &lt;code&gt;nevmenandr.github.io/WLA&lt;/code&gt;.&lt;br&gt;
The setup was smooth – I used &lt;strong&gt;Obsidian&lt;/strong&gt; with the excellent&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/jobindjohn/obsidian-publish-mkdocs/&quot;&gt;obsidian-publish-mkdocs&lt;/a&gt; plugin to generate a clean, documentation‑style site.  &lt;/p&gt;
&lt;p&gt;Everything worked beautifully… until Google Chrome started showing a bright red warning:&lt;br&gt;
&lt;strong&gt;&quot;Deceptive site ahead&quot;&lt;/strong&gt; – not just for my new project, but for &lt;strong&gt;every&lt;/strong&gt; site hosted under my &lt;code&gt;nevmenandr.github.io&lt;/code&gt; domain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11879479001821455838&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-culprit-a-single-innocent-looking-script-tag&quot;&gt;The Culprit: A Single, Innocent‑Looking Script Tag&lt;/h3&gt;
&lt;p&gt;While inspecting the generated HTML, I noticed lines where the &lt;code&gt;polyfill.io&lt;/code&gt; link caught my eye. I tried visiting it directly (the full URL with &lt;code&gt;/v3/polyfill.min.js?features=es6&lt;/code&gt; on the polyfill.io domain).&lt;br&gt;
&lt;strong&gt;Result:&lt;/strong&gt; &lt;code&gt;401 Authorization Required&lt;/code&gt; – a dead service.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-critical-red-flag-the-repository-is-stale&quot;&gt;A Critical Red Flag: The Repository Is Stale&lt;/h3&gt;
&lt;p&gt;Digging deeper, I realised that the &lt;code&gt;obsidian-publish-mkdocs&lt;/code&gt; repository on GitHub &lt;strong&gt;has not been updated for over two years&lt;/strong&gt;.  &lt;/p&gt;
&lt;p&gt;This is a classic warning sign:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The plugin still bundles legacy dependencies that were once free but have since changed hands.  &lt;/li&gt;
&lt;li&gt;The maintainer hasn't patched or removed deprecated resources.  &lt;/li&gt;
&lt;li&gt;No security audits or dependency refreshes have occurred in that time.  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the tool works brilliantly out‑of‑the‑box, &lt;strong&gt;it's effectively unmaintained&lt;/strong&gt; – and that's a risk you inherit as a user.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-triggered-google-s-safe-browsing&quot;&gt;Why This Triggered Google's Safe Browsing&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;polyfill.io&lt;/code&gt; used to be a free, trusted CDN. But the domain was sold to a new owner who now requires payment and returns &lt;strong&gt;401&lt;/strong&gt; (or even malicious redirects) for unauthenticated requests.&lt;/p&gt;
&lt;p&gt;Google's crawler saw that my site included a script from a domain that now behaves suspiciously – and &lt;strong&gt;flagged the entire &lt;code&gt;nevmenandr.github.io&lt;/code&gt; origin&lt;/strong&gt; as a potential threat. This is a domain‑level penalty, meaning &lt;strong&gt;all&lt;/strong&gt; my other GitHub Pages projects were blocked too.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-fix-step-by-step-recovery-guide&quot;&gt;The Fix: Step‑by‑Step Recovery Guide&lt;/h3&gt;
&lt;p&gt;If you ever face this, here's the exact process that got my site back online within ~24 hours.&lt;/p&gt;
&lt;h4 id=&quot;1-remove-the-offending-resource&quot;&gt;1. Remove the Offending Resource&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Locate and delete&lt;/strong&gt; the problematic script tag from your HTML template.&lt;br&gt;
In my case, I disabled the polyfill inclusion in the MkDocs theme config (or simply removed the line from the generated &lt;code&gt;index.html&lt;/code&gt;).  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rebuild and redeploy&lt;/strong&gt; your site to GitHub Pages.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;2-verify-the-site-is-clean&quot;&gt;2. Verify the Site is Clean&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Visit your URL in a private/incognito window to confirm the red warning still appears (it will, until Google re‑crawls).&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;Google Transparency Report&lt;/strong&gt; tool (search for &quot;Google Transparency Report&quot; and then enter your domain in the Safe Browsing section) to check your domain's current status.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-claim-ownership-in-google-search-console&quot;&gt;3. Claim Ownership in Google Search Console&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Google Search Console&lt;/strong&gt; (search for it – it's Google's official tool for webmasters).&lt;/li&gt;
&lt;li&gt;Add your site as a &lt;strong&gt;property&lt;/strong&gt; (use the URL‑prefix type, e.g., &lt;code&gt;https://nevmenandr.github.io/WLA/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Verify ownership using the recommended method – for GitHub Pages, the easiest is:&lt;ol&gt;
&lt;li&gt;Download the HTML verification file from Search Console.&lt;/li&gt;
&lt;li&gt;Add it to the root of your repository (&lt;code&gt;/path/to/your/repo/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Commit and push – GitHub Pages will serve it automatically.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-request-a-review&quot;&gt;4. Request a Review&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;In Search Console, navigate to &lt;strong&gt;Security &amp;amp; Manual Actions&lt;/strong&gt; → &lt;strong&gt;Security Issues&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If the warning is still active, you'll see a &lt;strong&gt;&quot;Request Review&quot;&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;In the review form:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Explain&lt;/strong&gt; clearly that you've removed the external resource that caused the issue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mention&lt;/strong&gt; that you've scanned your site for any other suspicious content.&lt;/li&gt;
&lt;li&gt;If you use any other external scripts, list them and confirm they are safe.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;5-wait-usually-24-48-hours-&quot;&gt;5. Wait (Usually 24‑48 Hours)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Google's team will review your request. In my case, the domain was cleared in about &lt;strong&gt;24 hours&lt;/strong&gt;.  &lt;/li&gt;
&lt;li&gt;You can monitor the status in Search Console – it will change from &quot;Pending&quot; to &quot;Approved&quot; or &quot;Rejected&quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=306045379319634575&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;key-lessons-for-developers&quot;&gt;Key Lessons for Developers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Third‑party scripts are liabilities.&lt;/strong&gt; Always audit CDN links – especially free ones that can change ownership or monetisation models overnight.&lt;br&gt;
Consider self‑hosting critical libraries or using well‑established CDNs with clear SLAs (e.g., cdnjs, jsdelivr).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check the maintenance status of your tools.&lt;/strong&gt;&lt;br&gt;
A plugin that hasn't seen a commit in two years is a ticking time bomb. Always review the last update date before relying on any open‑source dependency in production. If it's stale, fork it, update the dependencies yourself, or look for an actively maintained alternative.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google Safe Browsing penalises at the domain level.&lt;/strong&gt;&lt;br&gt;
A single compromised resource can take down &lt;strong&gt;all&lt;/strong&gt; your sub‑projects, including unrelated ones. This is particularly painful on GitHub Pages where you share the &lt;code&gt;*.github.io&lt;/code&gt; namespace.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automate dependency checks.&lt;/strong&gt;&lt;br&gt;
Add a step in your CI/CD pipeline to test external URLs for HTTP 2xx responses. For example, with a simple &lt;code&gt;curl&lt;/code&gt; check:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;if ! curl -s -o /dev/null -w &quot;%{http_code}&quot; https://polyfill.io/v3/polyfill.min.js | grep -q &quot;200&quot;; then
  echo &quot;⚠️  Polyfill.io is unreachable – aborting build&quot;
  exit 1
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Know the recovery path.&lt;/strong&gt;&lt;br&gt;
Having a verified Google Search Console property is &lt;strong&gt;not optional&lt;/strong&gt; if you publish public sites. It's your only channel to communicate with Google's security team when false positives occur.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't panic – the process works.&lt;/strong&gt;&lt;br&gt;
The warning looked scary, but the fix was straightforward. The key is to act quickly, be transparent in your review request, and double‑check that no other deprecated services remain.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thought&quot;&gt;Final Thought&lt;/h3&gt;
&lt;p&gt;Open‑source tools like &lt;code&gt;obsidian-publish-mkdocs&lt;/code&gt; are brilliant for productivity – but they often bundle legacy dependencies for maximum compatibility, and when a repository stagnates for years, those dependencies can rot. As developers, it's our responsibility to &lt;strong&gt;review every external asset&lt;/strong&gt; and &lt;strong&gt;check the pulse of our toolchain&lt;/strong&gt; before going live.&lt;/p&gt;
&lt;p&gt;One dead polyfill link and an unmaintained plugin cost me a day of heartache. Don't let it happen to you. 🚀&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22868/how-a-dead-polyfill-io-link-got-my-entire-github-pages-domain-flagged-as-dangerous</guid>
<pubDate>Wed, 26 Aug 2026 04:47:00 +0000</pubDate>
</item>
<item>
<title>Weather Service Project (Part 2): Building the Frontend (GitHub Pages or Netlify and JavaScript)</title>
<link>https://coderlegion.com/10138/weather-service-project-part-building-the-frontend-github-pages-netlify-and-javascript</link>
<description>&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://datalaria.com/en/posts/weather-service-part-1/&quot;&gt;first part of this series&lt;/a&gt;, we laid the groundwork for our global weather service. We built a Python script to fetch weather data from OpenWeatherMap, efficiently stored it in city-specific CSV files, and automated the entire collection process using GitHub Actions. Our &quot;robot&quot; is diligently gathering data 24/7.&lt;/p&gt;
&lt;p&gt;But what good is data if you can't see it? Today, we shift our focus to the &lt;strong&gt;frontend&lt;/strong&gt;: building an interactive, user-friendly dashboard that allows anyone to explore the weather data we've collected. We'll leverage the power of static site hosting with &lt;strong&gt;GitHub Pages or Netlify&lt;/strong&gt;, use &quot;vanilla&quot; &lt;strong&gt;JavaScript&lt;/strong&gt; to bring it to life, and rely on some excellent libraries for data handling and visualization. Let's make our data shine!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://datalaria.com/en/posts/app_openweather_part2_frontend/AI_App_Weather_Image_Frontend.png&quot; alt=&quot;Conceptual image of Weather Service Frontend&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;free-web-hosting-github-pages-vs-netlify&quot;&gt;Free Web Hosting: GitHub Pages vs. Netlify&lt;/h4&gt;
&lt;p&gt;The first hurdle for any web project is hosting. Traditional servers can be costly and complex to manage. Following our &quot;serverless and free&quot; philosophy, both &lt;strong&gt;GitHub Pages&lt;/strong&gt; and &lt;strong&gt;Netlify&lt;/strong&gt; are perfect solutions for hosting static websites directly from your GitHub repository.&lt;/p&gt;
&lt;h5 id=&quot;option-1-github-pages&quot;&gt;Option 1: GitHub Pages&lt;/h5&gt;
&lt;p&gt;GitHub Pages allows you to host static websites directly from your GitHub repository.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Activation is trivial:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;code&gt;Settings &amp;gt; Pages&lt;/code&gt; in your repository.&lt;/li&gt;
&lt;li&gt;Select your &lt;code&gt;main&lt;/code&gt; branch (or the branch containing your web content) as the source.&lt;/li&gt;
&lt;li&gt;Choose the &lt;code&gt;/root&lt;/code&gt; folder (or a &lt;code&gt;/docs&lt;/code&gt; folder if you prefer) as the location of your web files.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Save&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And just like that, your &lt;code&gt;index.html&lt;/code&gt; file (and any linked assets) becomes publicly accessible at a URL like &lt;code&gt;https://your-username.github.io/your-repository-name/&lt;/code&gt;. Simple, effective, and free! &lt;/p&gt;
&lt;h5 id=&quot;option-2-netlify-the-final-choice-for-this-project-&quot;&gt;Option 2: Netlify (the final choice for this project!)&lt;/h5&gt;
&lt;p&gt;For this project, I ultimately opted for &lt;strong&gt;Netlify&lt;/strong&gt; due to its flexibility, ease of managing custom domains, and integrated continuous deployment. It also allows me to host the project directly under my Datalaria domain (&lt;code&gt;https://datalaria.com/apps/weather/&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Steps to deploy on Netlify:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Connect Your Repository&lt;/strong&gt;: Log in to Netlify. Click &quot;Add new site&quot; then &quot;Import an existing project&quot;. Connect your GitHub account and select your Weather Service project repository.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment Configuration&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Owner&lt;/strong&gt;: Your GitHub account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Branch to deploy&lt;/strong&gt;: &lt;code&gt;main&lt;/code&gt; (or the branch where your frontend code resides).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base directory&lt;/strong&gt;: Leave this empty if your &lt;code&gt;index.html&lt;/code&gt; and assets are in the root of the repository, or specify a subfolder if applicable (e.g., &lt;code&gt;/frontend&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build command&lt;/strong&gt;: Leave it empty, as our frontend is purely static with no build step required (no frameworks like React/Vue).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publish directory&lt;/strong&gt;: &lt;code&gt;.&lt;/code&gt; (or the subfolder containing your static files, e.g., &lt;code&gt;/frontend&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy Site&lt;/strong&gt;: Click &quot;Deploy site&quot;. Netlify will fetch your repository, deploy it, and provide you with a random URL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Domain (Optional but recommended)&lt;/strong&gt;: To use a domain like &lt;code&gt;datalaria.com/apps/weather/&lt;/code&gt;:&lt;ul&gt;
&lt;li&gt;Go to &lt;code&gt;Site settings &amp;gt; Domain management &amp;gt; Domains &amp;gt; Add a custom domain&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Follow the steps to add your domain and configure it with your provider's DNS (by adding &lt;code&gt;CNAME&lt;/code&gt; or &lt;code&gt;A&lt;/code&gt; records).&lt;/li&gt;
&lt;li&gt;For the specific path (&lt;code&gt;/apps/weather/&lt;/code&gt;), you would typically configure a &quot;subfolder&quot; or &quot;base URL&quot; within your application if it's not directly at the root of the domain. In this case, our &lt;code&gt;index.html&lt;/code&gt; is designed to be served from a subpath. Netlify handles this transparently once the site is deployed and your domain is configured.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It's that simple! Each &lt;code&gt;git push&lt;/code&gt; to your configured branch will trigger a new deployment on Netlify, keeping your dashboard always up-to-date.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;the-frontend-tech-stack-html-css-and-javascript-with-a-little-help-&quot;&gt;The Frontend Tech Stack: HTML, CSS, and JavaScript (with a little help)&lt;/h4&gt;
&lt;p&gt;For this dashboard, I opted for a lightweight approach: plain HTML for structure, a bit of CSS for styling, and &quot;vanilla&quot; &lt;strong&gt;JavaScript&lt;/strong&gt; (without complex frameworks) for interactivity. To handle specific tasks, I incorporated two fantastic libraries:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.papaparse.com/&quot;&gt;&lt;strong&gt;PapaParse.js&lt;/strong&gt;&lt;/a&gt;: The fastest in-browser CSV parser for JavaScript. It's the bridge between our raw CSV files and the JavaScript data structures we need for visualization.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.chartjs.org/&quot;&gt;&lt;strong&gt;Chart.js&lt;/strong&gt;&lt;/a&gt;: A powerful and flexible JavaScript charting library that makes creating beautiful, responsive, and interactive charts incredibly easy.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h4 id=&quot;the-dashboard-logic-bringing-data-to-life-in-index-html&quot;&gt;The Dashboard Logic: Bringing Data to Life in &lt;code&gt;index.html&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Our &lt;code&gt;index.html&lt;/code&gt; acts as the main canvas, orchestrating the fetching, parsing, and rendering of weather data.&lt;/p&gt;
&lt;h5 id=&quot;1-dynamic-city-loading&quot;&gt;1. Dynamic City Loading&lt;/h5&gt;
&lt;p&gt;In stead of hardcoding a list of cities, we want our dashboard to automatically update if we add new cities in the backend. We achieve this by fetching a simple &lt;code&gt;ciudades.txt&lt;/code&gt; file (containing one city name per line) and dynamically populating a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; dropdown element using JavaScript's &lt;code&gt;fetch&lt;/code&gt; API.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const citySelector = document.getElementById('citySelector');
let myChart = null; // Global variable to store the Chart.js instance

async function loadCityList() {
    try {
        const response = await fetch('ciudades.txt');
        const text = await response.text();
        // Filter out empty lines from the text file
        const cities = text.split('\n').filter(line =&amp;gt; line.trim() !== '');

        cities.forEach(city =&amp;gt; {
            const option = document.createElement('option');
            option.value = city;
            option.textContent = city;
            citySelector.appendChild(option);
        });

        // Load the first city by default when the page initializes
        if (cities.length &amp;gt; 0) {
            loadAndDrawData(cities[0]);
        }
    } catch (error) {
        console.error('Error loading city list:', error);
        // Optional: Display a user-friendly error message
    }
}

// Trigger city loading when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', loadCityList);
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;2-reacting-to-user-selection&quot;&gt;2. Reacting to User Selection&lt;/h5&gt;
&lt;p&gt;When a user selects a city from the dropdown, we need to respond immediately. An &lt;code&gt;addEventListener&lt;/code&gt; on the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element detects the &lt;code&gt;change&lt;/code&gt; event and calls our main function to fetch and draw the data for the newly selected city.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;citySelector.addEventListener('change', (event) =&amp;gt; {
    const selectedCity = event.target.value;
    loadAndDrawData(selectedCity);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;3-fetching-parsing-and-drawing-data&quot;&gt;3. Fetching, Parsing, and Drawing Data&lt;/h5&gt;
&lt;p&gt;This is the central function where everything comes to life. It is responsible for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Constructing the URL for the specific city's CSV file (e.g., &lt;code&gt;data/Leon.csv&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;Papa.parse&lt;/code&gt; to download and process the CSV content directly in the browser. PapaParse handles asynchronous fetching and parsing, making it incredibly easy.&lt;/li&gt;
&lt;li&gt;Extracting relevant &lt;code&gt;labels&lt;/code&gt; (dates) and &lt;code&gt;data&lt;/code&gt; (temperatures) from the parsed CSV for Chart.js.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crucial!&lt;/strong&gt;: Before drawing a new chart, we must &lt;strong&gt;destroy the previous Chart.js instance&lt;/strong&gt; (&lt;code&gt;if (myChart) { myChart.destroy(); }&lt;/code&gt;). Forgetting this step leads to overlapping charts and performance issues! &lt;/li&gt;
&lt;li&gt;Creating a new &lt;code&gt;Chart()&lt;/code&gt; instance with the updated data.&lt;/li&gt;
&lt;li&gt;Additionally, it calls a function to load and display the AI prediction for that city, seamlessly integrating it into the dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function loadAndDrawData(city) {
    const csvUrl = `datos/${city}.csv`; // Note the 'datos/' folder from Part 1
    const ctx = document.getElementById('weatherChart').getContext('2d');

    Papa.parse(csvUrl, {
        download: true, // Tells PapaParse to download the file
        header: true,   // Treats the first row as headers
        skipEmptyLines: true,
        complete: function(results) {
            const weatherData = results.data;

            // Extract labels (dates) and data (temperatures)
            const labels = weatherData.map(row =&amp;gt; row.fecha_hora.split(' ')[0]); // Extract only the date
            const maxTemp = weatherData.map(row =&amp;gt; parseFloat(row.temp_max_c));
            const minTemp = weatherData.map(row =&amp;gt; parseFloat(row.temp_min_c));

            // Destroy the previous chart instance if it exists to prevent overlaps
            if (myChart) {
                myChart.destroy();
            }

            // Create a new Chart.js instance
            myChart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: labels,
                    datasets: [{
                        label: `Max Temp (°C) - ${city}`,
                        data: maxTemp,
                        borderColor: 'rgb(255, 99, 132)',
                        tension: 0.1
                    }, {
                        label: `Min Temp (°C) - ${city}`,
                        data: minTemp,
                        borderColor: 'rgb(54, 162, 235)',
                        tension: 0.1
                    }]
                },
                options: { // Chart options for responsiveness, title, etc.
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: { y: { beginAtZero: false } },
                    plugins: { legend: { position: 'top' }, title: { display: true, text: `Historical Weather Data for ${city}` } }
                }
            });

            // Load and display AI prediction
            loadPrediction(city);
        },
        error: function(err, file) {
            console.error(&quot;Error parsing CSV:&quot;, err, file);
            // Optional: display a user-friendly error message on the dashboard
            if (myChart) { myChart.destroy(); } // Clear chart if loading fails
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;4-displaying-ai-predictions&quot;&gt;4. Displaying AI Predictions&lt;/h5&gt;
&lt;p&gt;The integration of AI predictions (which we'll delve into in Part 3) is also managed from the frontend. The backend generates a &lt;code&gt;predicciones.json&lt;/code&gt; file, and our JavaScript simply fetches this JSON, finds the prediction for the selected city, and displays it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;async function loadPrediction(city) {
    const predictionElement = document.getElementById('prediction');
    try {
        const response = await fetch('predicciones.json');
        const predictions = await response.json();
        if (predictions &amp;amp;&amp;amp; predictions[city]) {
             predictionElement.textContent = `Max Temp. Prediction for tomorrow: ${predictions[city].toFixed(1)}°C`;
        } else {
             predictionElement.textContent = 'Prediction not available.';
        }
    } catch (error) {
         console.error('Error loading predictions:', error);
         predictionElement.textContent = 'Error loading prediction.';
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;conclusion-part-2-&quot;&gt;Conclusion (Part 2)&lt;/h4&gt;
&lt;p&gt;We've transformed raw data into an engaging and interactive experience! By combining static hosting from GitHub Pages or Netlify, &quot;vanilla&quot; JavaScript for logic, PapaParse.js for CSV handling, and Chart.js for beautiful visualizations, we've built a powerful frontend that is both free and highly effective.&lt;/p&gt;
&lt;p&gt;The dashboard now provides immediate insight into the historical weather patterns of any selected city. But what about the future? In the &lt;strong&gt;third and final part of this series&lt;/strong&gt;, we'll delve into the exciting world of &lt;strong&gt;Machine Learning&lt;/strong&gt; to add a predictive layer to our service. We'll explore how to use historical data to forecast tomorrow's weather, turning our service into a true weather &quot;oracle.&quot; Stay tuned!&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;references-and-links-of-interest-&quot;&gt;References and Links of Interest:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Complete Web Service&lt;/strong&gt;: You can see the final project in action here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://datalaria.com/apps/weather/&quot;&gt;https://datalaria.com/apps/weather/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Project GitHub Repository&lt;/strong&gt;: Explore the source code and project structure in my repository: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Dalaez/app_weather&quot;&gt;https://github.com/Dalaez/app_weather&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PapaParse.js&lt;/strong&gt;: Fast in-browser CSV parser for JavaScript: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.papaparse.com/&quot;&gt;https://www.papaparse.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chart.js&lt;/strong&gt;: Simple, yet flexible JavaScript charting for designers &amp;amp; developers: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.chartjs.org/&quot;&gt;https://www.chartjs.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Pages&lt;/strong&gt;: Official documentation on how to host your sites: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/pages&quot;&gt;https://docs.github.com/en/pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Netlify&lt;/strong&gt;: Official Netlify website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.netlify.com/&quot;&gt;https://www.netlify.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/10138/weather-service-project-part-building-the-frontend-github-pages-netlify-and-javascript</guid>
<pubDate>Wed, 21 Jan 2026 06:44:50 +0000</pubDate>
</item>
</channel>
</rss>