My First Portfolio

← Back to Projects

Training project

What This Site Is

My first full site, and the project where I actually learned JavaScript by using it — a mobile hamburger nav, client-side form validation, and a project filter that reads each project's category data and builds its own filter buttons automatically.

View the live site · View the repo on GitHub

How I Worked Through This

The project filter was built data-driven on purpose: each project card carries its own category tags, and the JavaScript reads whatever categories exist on the page and generates the filter buttons from them. That means adding a new project later means touching one attribute, not the JavaScript itself — a small upfront decision that pays off every time a new project gets added.

Two real bugs surfaced after the project was already marked "finished," both the sneaky kind — nothing looked broken until you tried the exact right interaction. The submit handler was listening for a click on the submit button specifically, so pressing Enter in a text field — a completely normal way to submit a form — bypassed validation entirely and reloaded the page, wiping whatever someone had typed. Separately, the form itself was missing novalidate, so the browser's own built-in validation was silently intercepting submission before my JavaScript ever ran — same symptom, a browser message instead of mine, for a completely different reason.

Getting the success message to announce correctly for screen reader users took two attempts, not one. The first pass added aria-live="polite" to a success message that was appended to the DOM after submission — and testing with VoiceOver showed it silently failed to announce automatically. The real issue: a live region only reliably announces changes to content already present in the accessibility tree; a brand-new appended element isn't consistently treated as a "change." The fix was a permanent, empty live region present from page load, with JavaScript only ever updating its content, never creating or removing it.

A smaller one, caught during the filter's desktop layout: filtered items stopped hiding correctly once the desktop media query kicked in. Traced it to a CSS specificity conflict — a more specific rule inside the media query was quietly overriding the hide rule — and fixed it by adding the same override inside that query, rather than assuming it was a JavaScript problem and debugging the wrong layer.

Technical Details
Tech stack for my first portfolio project
Component Role
HTML Semantic structure — articles for project cards, per-field error spans
CSS Custom properties, mobile-first responsive layout
Vanilla JavaScript Nav toggle, form validation, data-driven project filter
Fetch API / async-await Real form submission and success/failure handling via Formspree
ARIA (aria-live, aria-expanded) Accessible form errors and submission status, verified with VoiceOver