diff options
| author | Himanshu Sardana <himanshusardana2005@gmail.com> | 2026-03-26 21:26:35 +0000 |
|---|---|---|
| committer | Himanshu Sardana <himanshusardana2005@gmail.com> | 2026-03-26 21:26:35 +0000 |
| commit | 103e84d847262830bbaa550b37218e9ca8b317d3 (patch) | |
| tree | e19d3bfd6594600fb28be1ccac1a3869207bc49c /output/1/index.html | |
| parent | 5c631f0cdb8ee3238ff054d171dd8babd158047b (diff) | |
refactor: split into cmd, pkg
Diffstat (limited to 'output/1/index.html')
| -rw-r--r-- | output/1/index.html | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/output/1/index.html b/output/1/index.html new file mode 100644 index 0000000..0d04be8 --- /dev/null +++ b/output/1/index.html @@ -0,0 +1,283 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="description" content="Writing a static site generator for fun and profit" /> + <title>Writing a static site generator for fun and profit</title> + <link rel="preconnect" href="https://fonts.googleapis.com" /> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> + <link + href="https://cdn.jsdelivr.net/npm/prismjs/themes/prism.css" + rel="stylesheet" + /> + <script src="https://cdn.jsdelivr.net/npm/prismjs/prism.js"></script> + <script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js"></script> + <link rel="stylesheet" href="../style.css" /> + <style> + .toc { + position: static !important; + width: auto !important; + right: auto !important; + top: auto !important; + background: var(--bg-subtle) !important; + border: 1px solid var(--border) !important; + padding: 1.2rem 1.4rem !important; + margin: 0 0 3rem !important; + } + + .toc-title { + margin: 0 0 0.8rem !important; + font-size: 0.65rem !important; + line-height: 1 !important; + display: block; + } + + .toc ul { + display: block; + margin: 0; + padding: 0; + } + + .toc li { + display: block; + padding-left: 0 !important; + margin: 0.25rem 0 !important; + } + + .toc li::before { + display: none !important; + } + + .toc a.active { + color: var(--text); + font-weight: 500; + } + + .toc-toggle { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + background: none; + border: none; + padding: 0; + font-family: var(--mono); + font-size: 0.65rem; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--text-muted); + margin-bottom: 0.8rem; + width: 100%; + text-align: left; + } + + .toc-toggle svg { + transition: transform 140ms ease; + flex-shrink: 0; + } + + .toc-toggle[aria-expanded="false"] svg { + transform: rotate(-90deg); + } + + .toc-body { + overflow: hidden; + transition: + max-height 240ms ease, + opacity 200ms ease; + max-height: 800px; + opacity: 1; + } + + .toc-body.collapsed { + max-height: 0; + opacity: 0; + } + + @media (max-width: 699px) { + .toc-title { + display: none; + } + .toc-toggle { + display: flex; + } + .toc-body { + } + } + + @media (min-width: 700px) { + .toc-toggle { + display: none; + } + .toc-title { + display: block; + } + .toc-body { + max-height: none !important; + opacity: 1 !important; + } + } + + @media (min-width: 1100px) { + .toc { + position: fixed !important; + top: 6rem !important; + left: max(2rem, calc((100vw - 680px) / 2 - 220px)) !important; + width: 200px !important; + background: transparent !important; + border: none !important; + padding: 0 !important; + margin: 0 !important; + } + + .toc-toggle { + display: none; + } + .toc-title { + display: block; + } + .toc-body { + max-height: none !important; + opacity: 1 !important; + } + + .toc-level-2 { + margin-left: 0.8rem !important; + } + .toc-level-3 { + margin-left: 1.4rem !important; + } + .toc-level-4 { + margin-left: 2rem !important; + } + } + </style> + </head> + <body> + <main class="article"> + <nav class="toc" aria-label="Table of contents"> + <button + class="toc-toggle" + aria-expanded="false" + aria-controls="toc-body" + > + <svg + width="10" + height="10" + viewBox="0 0 10 10" + fill="none" + aria-hidden="true" + > + <path + d="M2 3.5L5 6.5L8 3.5" + stroke="currentColor" + stroke-width="1.4" + stroke-linecap="round" + stroke-linejoin="round" + /> + </svg> + Contents + </button> + + <div class="toc-body collapsed" id="toc-body"> + <ul> + + <li class="toc-level-2"> + <a href="#how-an-ssg-works">How an SSG works</a> + </li> + + <li class="toc-level-2"> + <a href="#parsing-frontmatter">Parsing Frontmatter</a> + </li> + + </ul> + </div> + </nav> + + <h1>Writing a static site generator for fun and profit</h1> + <p>Tired of bloated Next.js projects taking up 100MBs of RAM on my VPS, I decided +to shift my personal site to a more minimal framework. Remembering <a href="https://www.youtube.com/@LukeSmithxyz">Luke +Smith</a>’s old video about Hugo, I decided +to give it a try. I was thinking of writing my own Hugo Theme when I had the +“bright” idea of writing my very own static site generator instead.</p> + +<blockquote> +<p>Enter <strong>Kite</strong></p> +</blockquote> + +<h2 id="how-an-ssg-works">How an SSG works</h2> + +<p>A static site generator takes in a directory full of markdown files and +converts them plain-old HTML files while maintaining a theme. For my use-case, +since I mainly wanted a blog, the most important page would be the <strong>post</strong> +page.</p> + +<p>The Page struct can therefore be defined as:</p> + +<pre><code class="language-go">type Page struct { + Title string + Content template.HTML + TOC []TOCItem +} +</code></pre> + +<h2 id="parsing-frontmatter">Parsing Frontmatter</h2> + +<p>Frontmatter is the small YAML-like block at the top of each markdown file +containing info such as the post title, it’s date, tags etc.</p> + +<pre><code class="language-go">type Frontmatter struct { + Title string `yaml:"title"` + Date string `yaml:"date"` + Tags []string `yaml:"tags"` +} +</code></pre> + + </main> + + <script> + const toggle = document.querySelector(".toc-toggle"); + const body = document.getElementById("toc-body"); + + if (toggle && body) { + toggle.addEventListener("click", () => { + const expanded = toggle.getAttribute("aria-expanded") === "true"; + toggle.setAttribute("aria-expanded", String(!expanded)); + body.classList.toggle("collapsed", expanded); + }); + } + + const tocLinks = document.querySelectorAll(".toc a"); + + if (tocLinks.length > 0) { + const headingIDs = Array.from(tocLinks).map((a) => + decodeURIComponent(a.getAttribute("href").slice(1)), + ); + const headings = headingIDs + .map((id) => document.getElementById(id)) + .filter(Boolean); + + let current = null; + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + current = entry.target.id; + } + }); + + tocLinks.forEach((a) => { + const id = decodeURIComponent(a.getAttribute("href").slice(1)); + a.classList.toggle("active", id === current); + }); + }, + { rootMargin: "0px 0px -70% 0px", threshold: 0 }, + ); + + headings.forEach((h) => observer.observe(h)); + } + </script> + </body> +</html> |
