Tshivhidzo Mbedzi AI & Technology Specialist

Projects This portfolio

Built · not yet proven live Web Engineering & Tooling November 2026

This portfolio

Zero-dependency static site generator and publishing toolchain

The site you are reading, and the tooling behind it: a generator with no dependencies, a build that refuses to publish broken output, and document generation written down to the file format.

My role
Sole designer, builder and author
Period
November 2026
Category
Web Engineering & Tooling
  • 0 Runtime dependencies Nothing to install, builds offline
  • 18 Pages generated From plain data files
  • ~50 ms Build time Measured over repeated runs
  • 23 Verification rules Including accessibility; any failure fails the build

In short

  • A zero-dependency static site generator in Node: 18 pages, no framework, no web fonts, nothing to install.
  • A verification gate of 23 rules plus 27 unit tests, failing the build on a broken link, a duplicate id, an unnamed button or an unfilled template slot.
  • An ATS-safe .docx written directly as WordprocessingML, and a share image from a hand-rolled PNG encoder — standard library only.

Project overview

This site is produced by a build system I wrote, with no framework and no dependencies at all. Content lives in plain data files, a generator turns them into static HTML, a verification step measures the output, and only if that passes does anything get published.

It is included as a case study for one reason: it is the only project here a reader can check completely. Every other case study asks you to take my word for how something was built. This one is the thing you are looking at.

The problem being addressed

A portfolio has to be fast, accessible, discoverable, cheap to host, and still easy to change in a year when I have forgotten how it works. The conventional answer is a framework, which brings several hundred megabytes of dependencies, a required install step, and a toolchain I did not choose and could not fully explain.

There is a second problem specific to this site. Its whole argument is that I verify my own work before claiming it. A portfolio making that claim while shipping broken links or missing headings would refute itself on contact.

The solution

A generator of a few hundred lines using only the Node standard library. Output is plain HTML, CSS and JavaScript, so it deploys by copying a folder and will still build in five years.

The part that matters is the gate. A separate program walks the built output and checks 23 rules: every internal link and asset resolves, every in-page anchor exists, each page has exactly one top-level heading and a description and a canonical URL, every image carries alternative text, no external link opens without the right rel attribute, and no placeholder marker or unfilled template slot survives. It exits non-zero on any failure, and the deployment workflow runs it before publishing, so a failed check leaves the previous version live.

Two smaller pieces were written down to the file format rather than pulled from a library. The CV is generated as WordprocessingML and zipped with the standard library, so it contains exactly the markup I wrote and provably none of the structures that applicant tracking systems mangle. The social share image comes from a PNG encoder written here, because the alternative was a headless browser for one 1200x630 file.

How it works

This site has no framework, no dependencies and nothing to install. It builds in about fifty milliseconds and refuses to publish itself if anything is broken. Here is the whole machine.

  1. Content lives as data, not markup

    Projects, skills, education, the roadmap and the CV are plain data files. Changing what the site says never means touching a template, which is what makes it maintainable by someone who has not looked at it in a year.

  2. A generator renders it to static HTML

    A few hundred lines using only the Node standard library turn that data into finished pages. No framework runtime is sent to the browser, there are no web fonts, and the only JavaScript on the site is about ninety lines for the menu and the contents highlight.

  3. Documents are written down to the file format

    The CV is generated as WordprocessingML and zipped with the standard library, so it provably contains none of the structures applicant tracking systems mangle. The social share image comes from a PNG encoder written here, because the alternative was a headless browser for one image.

  4. A gate measures the output

    Twenty-three rules run against the built site: every link and asset resolves, every anchor exists, each page has exactly one top-level heading, a description and a canonical URL, every image has alternative text, every landmark is present, no id is duplicated, no link or button lacks an accessible name, and no placeholder or unfilled template slot survives.

  5. Nothing publishes unless it passes

    The deployment workflow runs the gate before publishing. A failure stops the release, and the previously published version stays live. A broken build never becomes a broken site.

Technology stack

Generator

  • Node.js
  • JavaScript (ESM)
  • Template literals
  • No framework

Output

  • Static HTML
  • One CSS file
  • About 90 lines of JavaScript

Documents

  • WordprocessingML
  • ZIP via zlib
  • Print stylesheet for PDF

Images

  • Hand-written PNG encoder
  • Inline SVG diagrams

Delivery

  • GitHub Actions
  • GitHub Pages
  • Gate before deploy

System architecture

Portfolio build and release pipeline Content lives as plain data, a dependency-free generator renders it to static HTML, and a twenty-three-rule gate measures the output. A failure stops the release and leaves the previous version live. Content as data files projects · skills · CV · roadmap Design tokens & templates Generator Node standard library only · no framework Base path derived from the site URL, so a subpath deploy needs no second setting. Static HTML 18 pages ATS .docx WordprocessingML + zlib Share image hand-written PNG encoder Verification gate — 23 rules links · anchors · headings · alt text · metadata · placeholders Exits non-zero on any failure, so the deploy stops before anything is published. Published site Previous version kept live on failure
Portfolio build and release pipeline — Content lives as plain data, a dependency-free generator renders it to static HTML, and a twenty-three-rule gate measures the output. A failure stops the release and leaves the previous version live.

My responsibilities

  • Designed and wrote the generator, the preview server and the verification gate.
  • Designed the visual system: tokens, type scale, layout, responsive behaviour and print styles.
  • Wrote every word of the content, and the editorial rules that govern what may appear.
  • Wrote the .docx generator against the WordprocessingML format directly.
  • Wrote the PNG encoder that produces the social share image.
  • Set up continuous deployment with verification as a release gate.
  • Audited the repository for secrets before publication, and rewrote history when one was found.

Development process

  1. Content as data, not markup

    Every page is generated from plain data files. Changing a project, a skill or a roadmap step never means touching a template, which is what keeps the site maintainable by someone who has not looked at it for a year.

  2. Write the gate early

    The verification step was built before most of the content. It has since caught a broken link, a duplicate heading, several over-long meta descriptions and a skipped heading level — all of which I would otherwise have shipped.

  3. Look at the output, do not only test it

    The PNG encoder passed every structural check while silently failing to draw part of the image. Opening the file was the only thing that found it.

  4. Audit before publishing, not after

    The repository was scanned for credentials before it was ever pushed. Publishing is irreversible in practice: a secret in a public history is already compromised.

Challenges encountered

A bug that no structural test could catch

The generated share image passed everything: valid signature, correct header, correct dimensions, sensible file size. Opening it showed an empty box where a monogram should have been. The cause was that a centring calculation produced a fractional coordinate, and writing to a fractional index in a byte buffer is silently ignored rather than throwing. The fix was one line. The lesson was that "the file is valid" and "the file is correct" are different questions, and only one of them is cheap to automate.

A checker that cried wolf

The gate scanned for leftover placeholder text without regard to case. The CV page legitimately contains the phrase "leftover placeholder text", describing this very feature, so the build failed on correct content. The tempting fix is an exception for that page. The right fix was to tighten the rule, since real markers are written in capitals — because a checker that produces false positives is one people learn to ignore, which is worse than having no checker at all.

Root-relative links break on a subpath

Every internal link here is root-relative, which is correct at the root of a domain and completely broken when the site is served from a subpath, as a project-level GitHub Pages site is. The usual symptom is an entirely unstyled page. Rather than add a separate setting that has to be kept in sync, the base path is derived from the configured site URL, so canonical URLs, the sitemap and in-page links all follow from one value.

Credentials in a file that was about to be published

A pre-publication scan found a live server username, host, port and key filename sitting in the deployment guide. Removing them in a later commit would not have been enough, because the earlier commit still carried them in history and anyone could read it. The history was rewritten before anything was pushed, and the result verified by searching the entire history for the strings rather than assuming.

Testing and validation

Sixteen automated rules, enforced as a release gate

Links, assets, anchors, headings, titles, descriptions, canonical URLs, image alternative text, link rel attributes, placeholder markers and template slots. Non-zero exit on any failure, run before every deployment.

Responsive behaviour verified by measurement

Checked at desktop and mobile widths, with horizontal overflow measured in the browser rather than judged by eye.

Behaviour verified, not assumed

The mobile menu was confirmed to toggle both its ARIA state and its visibility by reading them directly, after a screenshot had made it look as though it had not worked.

Document structure validated

The generated .docx is opened as a ZIP, every XML part parsed, and the absence of tables, text boxes, drawings and column definitions confirmed by counting them.

Evidence and demonstrations

There is no public URL for this project, and there are no screenshots on this page. That is deliberate: a screenshot is trivial to fabricate and I would rather show you a description of the real architecture than an image you have no way to verify. The diagram above is authored from the actual system.

Source code is not currently published. Publishing this portfolio repository and making my work reviewable is step 2 on my roadmap, and it is a hiring problem before it is a technical one. I am happy to walk through any of this code in a screen share.

Current development status

Built, tested and in use

  • Full static site generator with content, templates, diagrams and a local preview server.
  • Verification gate of twenty-three rules including accessibility, wired into continuous deployment.
  • 27 unit tests covering escaping, the base-path rewrite and the editorial invariants, run in CI before the build.
  • ATS-safe .docx generator written directly against the WordprocessingML format.
  • PNG encoder for the social share image, standard library only.
  • Subpath-aware builds, so the same source deploys to a root domain or a project subpath.
  • Accessibility fundamentals, and a print stylesheet that produces a clean PDF of the CV.

Not built, or not proven

  • It is not published yet. The repository is local and the site has not been deployed, which is why this page carries no live link.
  • No image optimisation pipeline, because the site currently has almost no images.

Future improvements

  • Publish it. Until that is done it is the only item on this list that matters.
  • Add an automated accessibility check to the gate, so accessibility is enforced rather than remembered.
  • Add unit tests for the generator itself, not only checks on its output.
  • Record page weight over time, so a size regression becomes visible rather than discovered.