Personal website and digital garden
jws.dev
A statically generated personal site in English and Spanish: professional experience, résumé, travel history, projects, and public activity, with its own JSON API and self-hosted behind NGINX.


At a glance
Project statistics
- Travel destinations
- 112
- Languages
- 2
- Technologies documented
- 84
- API collections
- 14
- Uptime
- 100%Over the last 30 days, measured by UptimeRobot.
- Days since last change
- 51
- Days since last build
- 0
Purpose and direction
The goal
jws.dev brings my professional history, projects, travel, and public activity together under one domain I own and host myself. It is deliberately built to be evidence rather than assertion: every page is prerendered and served by NGINX as static files, so Node.js is used only by the build system and never at request time. The whole site is published in English and Spanish, the same content is available through a versioned JSON API so a native app can be built against it, and an automated audit suite has to pass before a build ships.
Changelog
Recent changes
The last few things I have documented about jws.dev.
Build output kept out of the repository
The static maps, social cards, site metrics and photo snapshots are all rebuilt from scratch on every build, so they no longer belong in version control.
A preview card for every page
Every page now previews with a drawn card of its own — a heading, a line of context and either my portrait or the site mark — instead of the bare app icon.
A state map opens on the state
Opening New Mexico showed the whole of the United States: the map set its zoom floor before framing the shape, leaving the fit with nothing it was allowed to do.
China, and New Mexico
Two additions to the travel section: the 2018 trip through Shanghai, Wuxi, Suzhou and Beijing, and a New Mexico page drawn from two family trips to Cimarron and Taos.
Connected to my experience
Technologies used
Select a technology to read more about my experience with it and how I use it.
Explore the technology stack
Built for a second front end
The JSON API
The same content that renders these pages is published as a versioned JSON API, so a native app could be built against it without scraping the site. Version 1 exposes 14 collections across 2 languages from 15 endpoints.
A client only needs one URL. The manifest lists every collection with a hash of its contents, so an app can compare hashes and tell what changed since its last sync before downloading anything.
Measured, not asserted
Performance and accessibility
These numbers are generated during the build by measuring the output, so they describe the version of the site you are reading rather than a good day in a testing tool.
- Pages prerendered
- 604Every route is static HTML; nothing is rendered on request.
- JavaScript shipped
- 895 KBAcross the whole site. Interactive islands load only where a page needs them.
- CSS shipped
- 120 KBAcross the whole site, most of it in one stylesheet that every page shares.
- Median HTML page
- 50 KBThe HTML alone, before scripts, styles, and images. The largest is 391 KB.
Accessibility scores
- WCAG 2.2 A and AA
- 301 / 301Pages with no failures, checked by axe-core in a real browser.
- AAA colour contrast
- 301 / 301Pages where all text reaches 7:1, or 4.5:1 when large.
- WebAIM AIM score
- 10.0 / 10Average of 8 pages through the WAVE API, measured October 4, 2026.
axe-core 4.13.0 loaded every English page in headless Chrome on November 11, 2026, maps included, and tested it against WCAG 2.0, 2.1 and 2.2 at levels A and AA, and against the AAA contrast level. The Spanish pages share their templates. No page failed a check. The AIM score, from WebAIM, rates a page from 1 to 10 against a million home pages; 5 is about average.
Accessibility across all 604 pages
- Declare a document languageevery page
- Have exactly one top-level headingevery page
- Expose a main landmarkevery page
- Describe every imageevery page
- Avoid positive tab indexesevery page
These are checks a build can make honestly; contrast is measured by the browser run above. Focus order is verified by hand, because it needs a real keyboard to judge.
