D4 Armory (d4-gear)
- » info: d4-gear on Vercel
- » info: d4-gear on GitHub Pages
An armory page for one Diablo IV character — a Spiritborn Gorilla Thorns build at Paragon
236 — generated entirely from YAML. Repo eswat2/d4-gear (private); both published sites
are public and serve the repo root, so the YAML sources, the build script, and the
screenshots are all publicly readable alongside the page.
The subject is deliberately small and personal. What makes it worth recording is the pipeline: it is the same data-driven static-site shape as proto-watchlist, scaled up to a multi-file source and a schema strict enough to be validated.
Four YAML files, one JSON, one page
The source is split by concern rather than kept in one document:
| File | Holds |
|---|---|
gear.yaml |
the 9 equipped items — affix rolls with their [min, max] ranges, masterworking flags, unique powers, runewords, sockets, tempers, flags, lore |
charms.yaml |
the Horadric Seal, active charm sets and their bonus thresholds, 6 equipped charms, plus a stash section for owned-but-unequipped items |
glyphs.yaml |
the Legendary Glyphs socketed on the Paragon board |
stats.yaml |
a character-stats snapshot — core stats, resistances, offensive/defensive/utility panels, Spirit Hall detail |
scripts/build_site.py compiles all four into data.json, then renders that payload into
index.html — a two-stage pipeline where the intermediate stage is itself a published
artifact. data.json is no longer what the page reads, but it is still emitted and still
linked from the document head as <link rel="alternate" type="application/json">, so a
tool or model that finds the page gets the structured data without scraping the HTML.
The page ships finished. index.html is content-complete as committed — gear, charms,
seal, glyphs, and stats are all server-rendered into mount points by the build, not
templated in the browser. app.js is 85 lines and only wires the detail overlay. Neither
host runs a build step, so nothing in the repo is host-specific: no vercel.json, no
workflow, no config. Every path in the HTML and CSS is relative, which is what lets one
committed page work at / on Vercel and at /d4-gear/ on Pages.
Rolls are recorded as rolls, not as numbers
The schema keeps the rolled value next to the affix's normal range, with
boosted: true where the affix has been masterworked — so a value that legitimately
exceeds its listed range reads as masterworking rather than as a typo. Unique effects
preserve the in-game [min–max] notation. Runewords carry both the rune-pair identifier
(BacWat) and the in-game lore name (Selachian Disdain). Charm sets track the raw
equipped count and the effective count after the seal's set-requirement reduction,
because those differ.
build_site.py validates the payload before it writes anything, including that every
source_image path resolves — every tracked item has a matching full-resolution in-game
screenshot under assets/, which is the verification story for the whole dataset. Game
data is checked against Maxroll as the primary source; other
databases were found to disagree with it.
Build-time asset handling
Beyond rendering, the build stamps content hashes into the page: styles.css, app.js,
and every gear/charm/glyph image URL are cache-busted from their own content, and a build
id lands in the footer. scripts/optimize_images.py generates downsized WebP variants
under assets/web/thumb/ and assets/web/detail/ for the grid cards and the detail
overlay — the full-resolution captures are never touched and stay checked in as the
reference copies.
The page self-hosts its Cinzel faces rather than fetching from Google Fonts, does not lazy-load the first row of gear cards (an LCP fix), and had its rarity accent colours lightened to clear WCAG AA contrast against the dark ground.
License
MIT (LICENSE in the repo — private, so the link resolves only for the owner).