Static Site Generator, 2025

Sasha Article Transformation, 2025

Engineering

Static Site Generator

April 2025

GitHub: trainingmode/portfolio

  1. Overview
  2. Workflow
  3. Writing Articles
  4. Template System
  5. Portfolio Site
  6. Benchmarks & Metrics
  7. Improvement Targets

This site loads in under 0.8s on mobile, 0.2s on desktop (PageSpeed Insights)

My portfolio site is built with a custom static site generator.

Markdown articles & folder directories are rendered into HTML templates.

I needed a simple system to write articles and generate a site that was effortless to maintain. My core focus was writing articles and designing a site that was 100% accessible.

What I’ve learned from previous projects is to reduce or avoid complexity. I would have just written pure HTML, but that makes for a horrible experience writing neatly–formatted articles.

There is only 1 line of inline JavaScript on this site. It’s on the email button CTA, needed to copy my email to your clipboard and close the context menu:

onclick = `navigator.clipboard.writeText("alfred.r.duarte@gmail.com");this.blur();`;

Everything else is just vanilla HTML & Tailwind CSS.

My Lighthouse metrics speak for themselves.

I didn’t even know it had fireworks.

Tools used:

Overview

Built on the DirectoryArticle folder structure concept. Similar to other Static Site Generators like Jekyll or Gatsby.

It’s essentially a Markdown converter with a simple template system. Generated sites are just HTML files, organized in the folder structure you provide in the input directory.

Internally, articles are converted using Pandoc. Pandoc converts all files to HTML by default. While you could write articles in any markup format that Pandoc supports, only Markdown is supported.

A set of internal plugins preprocess special Markdown syntax, like video and embedded iFrames.

# Video Component

[<video> Attributes](link "type")

# Example

[loop controls playsinline width="320" class="rounded-lg ring-(--color-primary) ring-0 ring-offset-1 hover:ring-4 active:ring-2 transition-shadow"](/public/media/alfred-portfolio-lighthouse-metrics.mp4 "video/mp4")

Output:

Both article files & directory folders are rendered as pages in your site. Directory pages display article listings and other directory subfolders.

Complete documentation can be found in the project README.

Workflow

The static site generator is a set of bash scripts.

The CLI is used to watch the current project directory for changes and preview your site.

Your site is automatically rebuilt when the CLI observes any changes. You can specify an .ssgignore file to define excluded file/folder patterns.

A ssg.config Configuration file is created for you when you first run the CLI. It contains a few settings, like your domain for canonical & Open Graph URLs. None of the settings are required.

A folder of templates is used to render your folder of articles & directories into HTML pages. The generated pages are saved into the output folder. The structure of the output folder will mirror the input folder.

CLI

The CLI is the main entrypoint for the SSG.

It watches the current directory and serves your site for previewing.

./ssg.sh ssg.config "articles" "build"

Builder

You can run the Builder manually to build your site.

./build.sh ssg.config "articles" "build"

The CLI can be used to watch your project directory and automatically rebuild your site.

Development Server

The Development Server is a simple serve–based HTTP server for previewing your site.

./server.sh 3000

The server isn’t WebSockets–based, so you’ll need to refresh your browser to see changes.

The CLI will automatically launch the server for you.

Writing Articles

Articles are formatted in Markdown.

Article heading images are extracted as the thumbnail. The thumbnail is displayed in directory pages and for sharing on social media. The first image in the article is used.

Article titles are extracted from the filename. Filenames are slugified for SEO–friendly URLs.

Article descriptions are extracted as the first paragraph of the article.

You can define an article category by adding a > Blockquote before the first heading.

> Case Study
# Light & Shadows

Article Components

Article components allow you to transform content using special Markdown syntax.

A handful are available. Currently, they all extend the []() link` syntax.

For example, add + to the beginning of a link to turn it into a download link.

[Download Sketch Diagram](https://drive.google.com/uc?export=download&id=1p-IP3XXRX8CAVHfLlhyUw3MDdahVhUTv "Sasha Layout Assembly Diagram (Sketch), Alfred R. Duarte 2025.zip")

Output:

⤵️Download Sketch Diagram

See the project README for complete documentation.

Article Listing Symbols

Special symbols are used to designate the article listing type.

For example, ~ Hueshift, 2023.md will render as a page, but will not be listed in any directory page.

Template System

The template system assembles template fragments into a final layout.

It uses .frag.html fragment HTML files for templates.

There are no restrictions, and there are plenty of {{ }} Replacement Tags for injecting content parsed from your articles.

You can target templates to specific articles by mirroring the structure of your articles folder.

Layouts

Layout templates are used to render both article & directory pages.

Layouts follow a hierarchal order to building a final HTML page.

Sasha Layout Assembly Diagram, 2025

Articles are first converted to HTML using Pandoc.

Through a series of {{ }} Replacement Tags, the template system assembles fragments and injects them into the layout.frag.html Layout Template file.

The final output is a self–contained HTML page.

Replacement Tags

The template system uses {{ }} Replacement Tags to inject content into template fragments.

All tags are optional, and some tags can be used site–wide.

<!-- Layout Template -->
 
<!DOCTYPE html>
<html lang="en">
{{HEAD}}
{{BODY}}
{{FOOTER}}
</html>

The project README contains complete documentation for the template system.

Portfolio Site

For my portfolio site itself, I mixed pure HTML with Tailwind CSS to design templates.

My portfolio site is deployed on GitHub Pages.

Writing articles, designing templates, and building this SSG took blended about 3 weeks.

Designing with Tailwind

During development, the Tailwind Play CDN was used. Performance is fairly slow loading Tailwind from the CDN each time a page loads.

For production, the Tailwind CLI was used. Since Node.js is not used in this project, the CLI was installed via the standalone method recommended by Tailwind.

curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-macos-arm64 && chmod +x tailwindcss-macos-arm64 && mv tailwindcss-macos-arm64 tailwindcss

It downloads as a single tailwindcss binary.

├── ...
└── tailwindcss

After installing the executable, I continued the regular Tailwind CLI installation.

First import Tailwind into the main stylesheet.

styles.css

@import "tailwindcss";

Then replace the CDN with the stylesheet generated by Tailwind.

index.html

-  <script src="https://cdn.tailwindcss.com"></script>
+  <link rel="stylesheet" href="/public/tailwind.css" />

Finally, compile Tailwind.

The -m minifier reduces the output size.

The -w watcher observes changes in your project and automatically rebuilds the Tailwind stylesheet.

./tailwindcss -i "./public/styles.css" -o "./public/tailwind.css" -m -w

In total this setup took about 5–10 minutes.

This was my first time using Tailwind with vanilla HTML and I really enjoyed it. Using it with my template system felt like a natural extension from other technologies like React.

Benchmarks & Metrics

Every single page on my site achieves 100 Best Practices & SEO scores.

Certain pages on my site achieve perfect 100 scores on PageSpeed Insights (Lighthouse).

Alfred Portfolio Home Page Lighthouse Metrics, 2025

There are zero errors site-wide.

Alfred Portfolio Clean Console Logs, 2025

Lighthouse Metrics

Below are a sampling of Lighthouse reports (via PageSpeed Insights) for some pages.

Metrics are based on mobile devices.

FCP LCP Speed Index Performance Accessibility Report
/ 0.8s 0.8s 0.8s 100 100 PageSpeed
/design 0.9s 8.7s 3.5s 🟡 74 100 PageSpeed
/design/emojis 0.9s 15.5s 3.5s 🟡 74 🟡 86 PageSpeed

Performance drops when a page includes a lot of media.

Accessibility drops a bit on article pages due to the way some headings are used.

Personally, I think semantically skipping an <h2> to create a subheading <h3> is perfectly reasonable. It also reads in plain English—the subheading before the following section.

# Title

### Subheading ←[ Triggers Accessibility Error ]

## Section

Deployed sites ship with only what they need. A generated article page is only a few dozen kilobytes in size. Media used in an article will increase its page size.

My Emoji Case Study page is 13.5MB including images and transfers in 246ms.

Alfred Portfolio Network Load, 2025

As you can see, images are the largest bottleneck. Currently there is no image optimization (lazy loading, serving smaller images for mobile, etc). This is a clear target for future optimization.

Builder Benchmarks

An article with 2069 words (my Dataing article) renders in around 64ms.

Sasha Terminal Output, 2025

My entire portfolio site with 10 articles renders in just under 3s. It’s lengthy but doesn’t feel too bad when writing articles.

This could be improved by only rebuilding changed articles. However, it would require hacking the chokidar-cli output to determine which files changed.

It would make more sense to translate the CLI to Node.js and use WebSockets to trigger rebuilds. For this project, this was an acceptable compromise given I was focused primarily on writing articles.

Improvement Targets

There are a handful of targets for improvement that exceeded the initial scope of this project.

I was mainly focused on writing articles for my portfolio and didn’t want to add complexity.

In all, I’m satisfied with performance results and CLI UX given the scope.

Removing complexity from this project taught me a lot about focusing on the core experience.

Performant & accessible design can look beautiful, too.


🧑🏽‍💻 Book a meeting to discuss your project.

alfred.r.duarte@gmail.com Message