The first version of mcpasha.dev did what a lot of developer portfolios do. It listed my skills as a wall of badges, showed a few project thumbnails, and linked to GitHub. It was fine. It also didn’t tell anyone what I’d actually built, who I’d built it for, or how to get in touch about building something for them.
I spend my days at a small agency building and maintaining sites for nonprofits, local businesses and larger organizations, mostly on Drupal, WordPress, Nuxt and Laravel. On the side I take freelance work. The portfolio should connect those two things, and the old one didn’t. So I started over.
This post covers the whole process: picking a direction, the type and motion decisions, and the small interactions I spent too long on. A few sections have live demos you can poke at.
Decide who it’s for
Before opening a design file, I wrote one sentence about who I build for. It’s now the line under my name on the home page:
Developer building fast, careful websites for nonprofits, local businesses & larger organizations.
That line did more for the design than any moodboard. Once the audience is named, a lot of choices answer themselves. The skills list and internships moved off the homepage to the About page. Case studies moved to the front. Every project got a label saying whether it was agency or freelance work, with the agency credited, because being clear about my role reads as more senior than being vague about it.
As for why now, there wasn’t a big moment. I was just tired of the old one.
Two rounds of mockups
I mocked up four homepage directions across two rounds. The first two were safe and thoughtful. They also didn’t feel like me. The second round leaned toward the sites I keep bookmarking from Awwwards: oversized type, a mostly monochrome palette with one accent, and motion carrying a lot of the personality.
Passed
Editorial
The site as a small magazine. Each case study is an “issue” with a big serif headline, wide margins and pull-quote outcomes.
Thoughtful, and a good fit for history clients, but it felt like someone else’s personality.
Passed
Blueprint
Technical drawing as a theme: grid paper, dashed boxes, dimension lines and monospace everywhere.
Clever for a developer, but the gimmick would wear thin by the third page.
Picked
Index
Dark, kinetic and type-led. My name set huge, a tech marquee, a numbered work list with hover previews, and one orange accent.
Loud in exactly one place. It looked like the sites I actually bookmark.
Pill nav kept
Gallery
Warm off-white with an electric blue accent, a staggered image grid, and a video pill tucked inline in the headline.
Lovely, but quieter than I wanted. Its pill nav moved over to Index.
Index stuck immediately. It’s dark, loud in one place, and quiet everywhere else. The only thing I took from Gallery was its pill-shaped navigation, which looked better centered over Index’s layout than its own nav did. The pill’s highlight slides from one page to the next as you move around the site.
Boring tools, sharp edges
The old site was Astro and so is the new one. It’s fast, I know it well, and the site itself becomes a small proof that I can ship with it. The rest of the stack:
- Astro content collections with MDX for case studies and posts, so any entry can drop in a custom component. This post is the reason that matters.
- Hand-written SCSS, organised with CUBE CSS, Every Layout’s composition primitives, and Utopia’s fluid type and space scales. No Tailwind. Tailwind sites tend to look like Tailwind sites, and I wanted the CSS to be part of the portfolio.
- GSAP for the few animations that need real choreography, and three.js for the golf ball behind the hero, loaded only on the page that has one.
- Native cross-document view transitions, so a project’s preview can morph into its case study header without turning the site into a single-page app.
- Vercel for hosting and analytics, and FormSubmit for the contact form (more on that below).
The name in the hero
The homepage opens on my name, set huge in Archivo’s compressed width. On load, each letter rises out of a mask. It’s the one big type moment on the page.
The trick is simple: GSAP’s SplitText wraps each character in its own mask, and the characters animate up from below it with a small stagger. Try changing the stagger and replaying it.
Parker McCrary
// main.ts
SplitText.create(heading, { type: "chars", mask: "chars" });
gsap.from(split.chars, {
yPercent: 140,
duration: 1,
ease: "expo.out",
stagger: 0.04,
delay: 0.1,
onComplete: () => split.revert(),
}); 40ms ended up being the sweet spot for me. Faster and it reads as one block sliding up. Slower and you’re waiting on your own name.
One detail took longer than the rest: letters like the Y and R overhang their boxes, so a tight mask clips them. Each mask gets padding to grow the clip and a matching negative margin so the layout doesn’t move. Kerning is off on the heading too, so the split letters sit exactly where the finished text does and nothing shifts when the split is undone.
The work index
Below the hero, projects are a numbered list, not a grid of cards. Hovering a row turns it orange and a screenshot follows the cursor. It keeps the page typographic while still showing the work, and it scales to any number of projects without the layout falling apart. It’s easier to feel than to describe, so try it on the home page.
Two details made this feel right. First, the preview eases toward the cursor instead of snapping to it, which gives it a little weight. Second, on touch devices there’s no hover, so the preview is skipped entirely and each row is just a link: to its case study, or out to the live site for projects without one.
Clicking through is where the view transition comes in. The screenshot under your cursor morphs into the case study’s hero image as the new page loads, and it shrinks back into place when you go back.
One font, many widths
The whole site runs on Archivo, a variable family with a width axis. Headlines use the compressed end, section headings sit at condensed, body copy at normal width, and Instrument Serif sets the ledes and pull quotes, with its italic saved for small accents. JetBrains Mono handles labels and metadata.
Sizes are fluid, built with clamp() so type scales smoothly between a minimum and a maximum instead of jumping at breakpoints. The scale comes from Utopia: 16px body copy at a 400px viewport, stepping up by 1.2, grows to 20px at 1900px, stepping up by 1.25. Drag the slider to watch the whole scale move together.
- Min viewport
- 400px · 16px · 1.2 Minor third
- Max viewport
- 1900px · 20px · 1.25 Major third
| Step | Min | Max | Now | Sample |
|---|---|---|---|---|
| 6 | 47.8px | 76.3px | 64.5px | |
| 5 | 39.8px | 61px | 52.3px | |
| 4 | 33.2px | 48.8px | 42.4px | |
| 3 | 27.6px | 39.1px | 34.3px | |
| 2 | 23px | 31.3px | 27.9px | |
| 1 | 19.2px | 25px | 22.6px | |
| 0 | 16px | 20px | 18.3px | |
| -1 | 13.3px | 16px | 14.9px |
Spacing works the same way, with a small set of fluid steps from Utopia. Breakpoints mostly handle layout changes, like the work index lining up into columns from tablet width. The exceptions are the hero name, which also sizes itself to the screen’s height once there’s room, and the root font size, which steps up on very large screens.
A Setup page with a toggle
I’ve always liked /uses pages, where developers list their tools, so this site has one too. Mine has a twist, because half of my desk time isn’t spent coding. A toggle at the top switches between my development setup and my gaming setup, and the rig gets its own accent color.
The contact form
The contact page doesn’t need a backend, so it doesn’t have one. The form posts to FormSubmit, a free service that emails each submission to me as a tidy table. There’s no account to set up: the first message to a new address sends an activation email, and once that’s confirmed everything comes through.
It posts as a plain HTML form rather than through FormSubmit’s AJAX endpoint, on purpose. Their captcha and the autoresponse, which sends the person a copy of their message, only work with a native post. After sending, FormSubmit redirects back to the contact page with #sent on the URL, and that fragment swaps the form for a confirmation in CSS, so the whole flow works without JavaScript.
Spam meets four layers: the captcha, a hidden honeypot field people never see, a list of phrases that drops a message outright, and a small script that asks you to press send again if the form was filled in under three seconds.
What I’d tell myself at the start
- Words. Write the one-sentence positioning before anything else. It cut more from the design than I did.
- Show off, a little. A portfolio is the one site where some flash is the point. The letter rise, the golf ball and the page transitions are there to grab attention and show what I can build, but each one has to stay fast and sit still when reduced motion is on.
- Honesty. Label agency work as agency work. Prospects care more about your role than about who owned the contract.
- MDX. Let posts carry their own components. A demo explains a CSS trick faster than three paragraphs.
- Ship. Don’t wait for perfect. There’s always one more detail to tune, and a site that’s live and improving beats a perfect one that never launches.
Still building
The site is live, but it isn’t finished. Right now I’m mostly updating the case studies and making screen recordings of the sites. This blog is new too. It’s where I’ll write about whatever I’m working on, dev or otherwise.
If you’re a nonprofit, local business or larger organization that needs a site built or looked after, the work page is the best place to start. Otherwise, thanks for reading the first one.