The whole case, in under a minute.

Problem

Strong work, badly ordered.

The old gallery set every project at the same level and left a reviewer to piece my career together.

What I found

I never lacked evidence.

The job was making the strongest work easy to find without flattening the range behind it.

What I decided

Depth on demand.

The homepage answers a hiring manager first: flagship projects lead into full case studies, supporting work stays compact, and the resume is one PDF.

The live homepage on a phone: my photo, name and role, where I work and that I am open to new roles, then Design. Build. Prove., the pitch, three figures with their projects, six logos, and two buttons, Reach out and View resume
The live homepage work section on a phone: no heading, straight into the Precisely Developer Portal card with its outcome, eight to three steps to test an API, then the ThoughtSpot mobile card

What I decided

Built like a product.

Shared navigation, theme-aware media, route metadata and release checks keep every project page in one system.

The live Precisely case study on a phone: the title naming the outcome, one paragraph, a grey button to the live portal, then the case film

What I decided

I deleted a particle engine that worked.

Claude built a 1.49 MB particle engine that hit its frame-rate target; I deleted it because it explained nothing.

See the heroes I cut
A cut hero direction: the Design. Build. Proof. statement over a painted sky of sunlit clouds
Clouds: the background became the main character.
A cut hero direction: the same statement with faint embossed tool icons scattered between the words
Embossed tool icons.
A cut hero direction: the same statement with colorful tool and emoji stickers between the words
Tool stickers.

What shipped

Mistakes I made once.

Each gate exists because an earlier release let that mistake through.

Color and target size

64 checks

Added after four real contrast defects were found already live.

Asset weight

14.8 MB of 15 MB budget for all images across the site

Added after the homepage was found shipping 8 MB.

Social cards

20 routes covered

Added after every route was found sharing a single image, so a shared link never showed the page it pointed at.

The Runable case study’s social card: its headline, A promise, not a prediction, set in the site’s type beside a screenshot of the product

Search and sharing

23 routes read

Added after the sitemap was found listing a noindex page and the structured data pointing at a card that no longer existed.

Alt text

30 alt texts in the source checked

Added after 32 images shipped with placeholder alt like “Research overview 4,” which passed the accessibility gate and still told a screen reader nothing.

Open the full build report

The build report

Generated on 2026-11-02 from the same build data that feeds the gates above.

Build chain

  1. generate-journal-metadata
  2. tsc -b
  3. check-decisions
  4. build-report
  5. vite build
  6. postbuild
  7. prerender
  8. csp
  9. check-glass
  10. check-a11y
  11. check-weight
  12. check-og
  13. check-seo
  14. check-alt
  15. check-escapes
  16. check-voice

What each gate checks

Color and target size
64 checks

Recomputes contrast for every color token pair in both themes against WCAG AA, and requires interactive text controls to be at least 24px tall.

scripts/check-a11y.mjs
Asset weight
14.8 MB of 15 MB budget for all images across the site

Fails on any image over 300 KB, any unconverted raster, and any source model left where it would be deployed.

scripts/check-weight.mjs
Social cards
20 routes covered

Requires every case study and note to ship its own social card, drawn from the site’s own type and color tokens and held under 400 KB.

scripts/check-og.mjs
Search and sharing
23 routes read

Reads every page’s built HTML the way a crawler does, and fails on a missing or duplicate title, a description outside 120 to 160 characters, a canonical that disagrees with og:url, a card of the wrong size, JSON-LD that does not parse, or a page without exactly one h1.

scripts/check-seo.mjs
Alt text
30 alt texts in the source checked

Fails on alt text that names a section and a counter, a filename, or a bare noun.

scripts/check-alt.mjs
Decision record integrity
9 records verified

Requires every published design decision to include considered options, positive and negative consequences, and a kill criterion. A decision cannot ship as a preference with the cost removed.

scripts/check-decisions.mjs
Flat surfaces
0 blurred surfaces

Fails the build if any compiled CSS blurs the backdrop, so glass cannot creep back in through a refactor or a utility class.

scripts/check-glass.mjs
Rendered copy
0 literal escapes or em dashes

Fails on visible escape sequences and em dashes in source copy. Comments are ignored, so the gate checks what readers can actually encounter.

scripts/check-escapes.mjs
Practice log voice
0 generated-writing tells

Fails when a log entry uses the phrases that most often give generated writing away: filler transitions, stock openers, and vocabulary nobody says out loud.

scripts/check-voice.mjs

Looking back

Next, I watch real reviewers read it.

Next time I’d put the site in front of hiring managers much earlier: the checks verify my rules, not whether a reviewer understood the work or every accessibility issue was found.

Beyond the brief

There was no brief, so I took on what nobody asks of a portfolio: a social card for every case study and note, and a build that checks contrast, alt text and its own copy before anything ships. I talk each change through out loud, Claude Code writes it, and I review every change.