DEV Community

Cover image for Building a 100/100 Lighthouse Static Website with GitHub Pages, Cloudflare & Vanilla Tech
Alessandro Mainardi
Alessandro Mainardi

Posted on Originally published at alexmaina.dev

Building a 100/100 Lighthouse Static Website with GitHub Pages, Cloudflare & Vanilla Tech

📖 Original Reference: Originally published with full semantic markup templates and Cloudflare DNS setup at alexmaina.dev/guida_sito.

In an era where personal portfolio sites and simple blogs pull in 250MB of node_modules, hydrate megabytes of JavaScript, and suffer from layout shifts, going back to pure semantic HTML5, modern CSS, and vanilla JS feels like a superpower.

By hosting static files on GitHub Pages paired with Cloudflare's Global Edge Network, you get:

  • Perfect 100/100 Lighthouse performance on mobile and desktop.
  • Sub-50ms Time to First Byte (TTFB) worldwide.
  • Enterprise DDoS mitigation, automatic HTTP/3, and free SSL certificates.
  • \$0/month forever hosting costs.

Here is how to set up the entire stack from scratch.


1. The Modern Zero-Framework Project Structure

You do not need a bloated build tool to get responsive layouts, dark mode, and offline caching:

my-website/
├── index.html              # Clean semantic HTML5
├── style.css               # Modern CSS with Custom Properties (variables)
├── script.js               # Vanilla JS for interactive widgets & theme toggle
├── sw.js                   # Lightweight Service Worker for offline support
├── robots.txt              # Search engine crawler rules
├── sitemap.xml             # XML sitemap for SEO indexing
└── .github/
    └── workflows/
        └── deploy.yml      # Automated linting and deployment
Enter fullscreen mode Exit fullscreen mode

2. Flash-Free Dark Mode in Modern CSS

The most common bug in static websites is the dark mode flash: the page renders bright white for 100ms before JavaScript executes and toggles the dark theme.

To prevent the flash completely, place an inline, blocking script in the <head> before any CSS renders:

<head>
  <meta charset="UTF-8">
  <title>Developer Portfolio</title>

  <!-- âš¡ Prevents theme flicker before CSS paints -->
  <script>
    (function() {
      const savedTheme = localStorage.getItem('theme') || 
        (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
      document.documentElement.setAttribute('data-theme', savedTheme);
    })();
  </script>

  <link rel="stylesheet" href="style.css">
</head>
Enter fullscreen mode Exit fullscreen mode

In style.css, leverage CSS custom properties:

:root {
  --bg-color: #ffffff;
  --text-color: #1e293b;
  --accent-color: #0284c7;
}

[data-theme="dark"] {
  --bg-color: #0b0f19;
  --text-color: #f1f5f9;
  --accent-color: #38bdf8;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  font-family: system-ui, -apple-system, sans-serif;
  transition: background-color 0.2s ease, color 0.2s ease;
}
Enter fullscreen mode Exit fullscreen mode

3. Offline Caching with a Vanilla Service Worker (sw.js)

Add native offline capability and instant page transitions using the Cache API:

// sw.js
const CACHE_NAME = 'site-cache-v1';
const ASSETS_TO_CACHE = [
  '/',
  '/index.html',
  '/style.css',
  '/script.js',
  '/offline.html'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS_TO_CACHE))
  );
  self.skipWaiting();
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // Return cached asset or fetch from network
      return response || fetch(event.request).catch(() => caches.match('/offline.html'));
    })
  );
});
Enter fullscreen mode Exit fullscreen mode

Register it in script.js:

if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });
}
Enter fullscreen mode Exit fullscreen mode

4. Pairing GitHub Pages with Cloudflare DNS & SSL

Deploying your site to a custom domain (e.g., alexmaina.dev) with Cloudflare provides edge caching and HTTPS enforcement:

  1. In your GitHub repository settings, go to Pages → Source: Deploy from a branch (main / root).
  2. Enter your custom domain in the Custom domain field (creates a CNAME file in your repo).
  3. In your Cloudflare Dashboard:
    • Add a CNAME record for @ pointing to <username>.github.io.
    • Add a CNAME record for www pointing to <username>.github.io.
    • Toggle the proxy status to Orange Cloud (Proxied).
  4. Under SSL/TLS, set encryption mode to Full (Strict).

5. Automated CI/CD Validation with GitHub Actions

Catch broken links, unclosed tags, and syntax errors automatically on every git push:

Create .github/workflows/deploy.yml:

name: Deploy & Validate Static Site

on:
  push:
    branches: [ main ]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate HTML5 Syntax
        uses: Cyb3r-Jak3/html5validator-action@v7.2.0
        with:
          root: .
          blacklist: "node_modules"

      - name: Check for Broken Links
        uses: gaurav-nelson/github-action-markdown-link-check@v1
        with:
          use-quiet-mode: 'yes'
Enter fullscreen mode Exit fullscreen mode

Results & Benchmarks

  • Performance: 100/100
  • Accessibility: 100/100
  • Best Practices: 100/100
  • SEO: 100/100
  • Monthly Infrastructure Cost: \$0.00

Conclusion

Before reaching for complex React/Next.js scaffolds for personal portfolios, documentation sites, or small marketing landing pages, consider the elegance of vanilla web technologies. It is simpler to maintain, faster for your users, and immune to framework deprecation cycles.

Author: Alessandro Mainardi — Computer Engineering Student @ UniBo & Full-Stack Developer. Explore the live implementation at alexmaina.dev.

Top comments (0)