figma frames, token sheet & terminal — hero shot

This Portfolio

Design Systems & Front-End Build

Building this portfolio with AI, and learning where it actually helps.

I designed and built the site you are reading with AI in every seat: as a thought partner for choosing a stack, as a design tool, and as the engineer that wrote most of the markup. Two full design attempts and six weeks later, the useful lesson was not about prompting.

The work that mattered turned out to be writing things down. A markdown file of design tokens and a reusable section prompt did more for the build than any single generated component.

Role

Solo build

design + engineering

Timeline

May – June 2026

5 weeks

Stack

Astro 4

Tailwind CSS

TypeScript

Tools

Figma & Figma Make

Claude Design

Claude Code

Stack candidates

Design passes

Build time

Hand-written CSS

3

2

5 wks

~5%

frameworks weighed against the same written brief before committing to one.

attempts at the visual language; only the second survived contact with code.

from an empty repository to a deployed site, on nights and weekends.

of the final stylesheets typed by hand rather than generated from a spec.

figma exploration frames
design token sheet
finished site — full spread

Step 01 · Stack

Using AI as a thought partner, not an oracle.

My first instinct was to ask which framework was best, which produced exactly the listicle that question deserves. The second attempt worked: I wrote down my constraints and asked for the argument both for and against each candidate.

The brief I gave it

A static portfolio, image-heavy, no CMS, cheap to host, and still editable in a year by a designer who writes some code. Argue both sides of every option.

Three candidates, one brief.

Forcing a case against each option was the part that changed the outcome. The framework I would have reached for by default lost on a constraint I had written down myself.

Next.js

Passed

The strongest case on paper, and the one I expected to pick. Pushed on the brief, it conceded that a React runtime, a server, and a hydration story bought nothing for a site with no dynamic data and no logged-in state.

Astro

Chosen

Ships no JavaScript unless a component asks for it, treats sections as static markup, and lets interactivity opt in island by island. The trade-off is a smaller ecosystem and fewer answers when something goes wrong — acceptable at this size.

Hand-written HTML & CSS

Passed

The most durable option, and genuinely tempting. But a shared nav, footer, and card would live in every file by copy-paste. Fine across five pages, a maintenance tax by fifteen.

stack comparison — constraints matrix from the AI conversation

Stack comparison

Choice made

Astro 4 with Tailwind and TypeScript, built to static files and deployed from Git.

Lesson learned

AI is most useful when it has constraints to push against. An open question returns a survey of options; a written brief plus 'argue both sides' returns a decision I could still defend six weeks later, when changing it would have been expensive.

Step 02 · Deploy

Deploy the empty thing first.

Before a single design decision existed, I generated a blank Astro app, pushed it to GitHub, and wired continuous deployment to a live URL. For the first day the site was public, functional, and completely boring.

Why an empty deploy earns its day.

Separate The Problems

Broken hosting and a broken layout look alike from the browser. Solving deployment against an empty page meant never debugging both at the same time.

Make Changes Reversible

With deploys running from the main branch, every section arrived as one commit against a working site. Undoing a bad suggestion was a revert, not an afternoon.

Give The Agent A Real Project

Agents reason better about a codebase that exists than one described to them. Config, tokens, and conventions already in place framed every prompt that followed.

repository, commit history & deploy pipeline

Repo and deploy pipeline

Choice made

Static output, Git-based continuous deploy, and main as the only long-lived branch.

Lesson learned

Infrastructure first is not a detour. The empty deploy became the safety net for every risky prompt afterward — when generated code broke the build, I knew the break was mine and recent, which made it a two-minute problem instead of an investigation.

Step 03 · First Attempt

Designing in Figma, prototyping with Figma Make.

The first pass followed the familiar path: references, a type scale, a handful of desktop frames, then Figma Make to turn those frames into something I could actually click.

What the prototype was genuinely good for.

Clicking through it answered questions static frames could not. A hero that looked balanced standing still fell apart the moment the page scrolled under it, and the nav needed a translucent state I had not thought to draw. Both problems surfaced in minutes.

figma frames — type scale & layout exploration

Figma frames

figma make prototype — clickable walkthrough

Figma Make prototype

Where it broke down.

The prototype was a good answer to the wrong question. Three problems only became visible once I tried to turn it into a real build.

A prototype is not a specification.

Figma Make produced something convincing to click through, but the code behind it was not code I wanted to own. Handing it to an agent as a reference produced markup that matched the picture and quietly ignored every convention I cared about.

Desktop-only frames hid the hard part.

Three polished frames at 1440px said nothing about how a four-column metadata grid should behave at 700px. Every responsive decision got deferred to implementation, which is where deferred decisions turn into guesses.

The reasoning stayed in my head.

The frames showed a 30px semibold heading. They never said 'section heading'. Without a name for the role, the agent had to infer intent from pixels, and it inferred differently each time it looked.

Choice made

Keep the Figma pass as exploration and throw the prototype away rather than build on top of it.

Lesson learned

A visual prototype answers 'does this look right'. It cannot answer 'what are the rules', and implementation only needs the rules. Pixels are a lossy way to carry intent to whoever builds next — including an agent.

Step 04 · Second Attempt

Designing in the language the build would speak.

The second pass used Claude Design and inverted the order of work. Instead of drawing screens and deriving rules from them afterward, I settled the rules first: type roles, color tokens, spacing rhythm, and the page margin systems. Screens came out of the system rather than the other way around.

From pixels to named roles.

Every typographic decision got a name, a Tailwind class list, and a note about where it applies. The table below is the artifact that replaced my Figma type scale — and the one the agent actually read.

specs/design-tokens.md

| Role                 | Tailwind classes
|----------------------|------------------------------------------------
| Case study title     | text-5xl sm:text-6xl md:text-[64px] font-bold
| Opening paragraph    | text-2xl leading-relaxed
| Header / subheader   | text-base font-semibold
| Section label        | text-sm uppercase tracking-widest text-secondary
| Body copy            | text-sm leading-relaxed
| Caption / muted      | text-xs text-muted

Deciding the awkward things on purpose.

The valuable entries were not the type sizes. They were the decisions I would otherwise have improvised forty separate times: that the homepage uses flat 88px margins while case study prose sits inside a narrower pair of guide lines, that decorative grid lines sit at one paint layer and opaque surfaces at another, that a hairline is 2% black and stays that way.

Each of those started as a correction I had made twice. Writing it into the token file with the reason attached is what stopped it becoming a third.

token sheet with guide-grid and margin-system overlay

Design tokens and margin systems

Choice made

One markdown token file as the single source of truth, checked into the repo beside the code it governs.

Lesson learned

Writing the system down changed who could enforce it. The same document is documentation for me and a constraint for the agent, which means the effort of maintaining it gets paid back twice. Design decisions that live only in a Figma file cannot do that second job.

Step 05 · Build

Design output as a prompt, not a picture.

The handoff between design and code turned out to be the real design problem. Claude Design's job was not to emit components — it was to emit a brief precise enough that Claude Code could build a section without having to guess what a heading meant or where a margin came from.

Anatomy of the section prompt.

The template settled into six blocks: stack, scope, component granularity, deliverable format, workflow, and constraints. Scope and deliverable format did most of the work, because they are where ambiguity would otherwise survive.

prompts/create-section-from-cl-design.md

SCOPE
- Fidelity target: [pixel-exact desktop] / [close approximation]
- Interactivity to port: [specific behaviors, or "static markup only"]
- Responsive behavior: [breakpoints and what changes at each]
- Images: [real assets at these paths] or [PlaceholderImage.astro]

DELIVERABLE FORMAT
- Exact copy (verbatim)
- Text styling as design-token roles, not raw values
- Colors as token names (bg-surface, text-secondary), not hex
- Spacing using the vertical rhythm already defined in tokens

One section per conversation.

Each section got its own prompt, its own commit, and a gate before the next one started: render it, screenshot it, compare it against the reference, approve or revise. Batching four sections into one request was faster to type and consistently slower to finish, because a wrong assumption in the first section had already been copied into the other three.

prompt, generated section & screenshot comparison side by side

Prompt to rendered section

Choice made

One section per prompt, with a screenshot-and-approve gate before moving to the next.

Lesson learned

The prompt template became the most reused artifact in the project. Generating code was never the expensive part — re-explaining the same conventions was, and a template is how you stop paying for that repeatedly.

Step 06 · Fine-Tuning

What the agent got close, and I had to finish.

Generated sections landed somewhere around 85% complete, and the remaining 15% was remarkably predictable. Five patterns accounted for nearly all of it. Drag a frame to move between versions.

Paint order nobody had designed.

Decorative guide lines rendered straight through the footer and the card covers, because nothing had told the agent which surfaces were meant to be opaque. Fixing the instance took a minute; the fix only held once the layer bands were written into the token file so the next section inherited them.

Before After

Arbitrary values creeping back in.

Left alone, the agent reached for a hard-coded width whenever a measurement was awkward. Most of those were genuine gaps in the scale and became new tokens. A few were shortcuts, and had to be resolved back to the scale with the reason noted.

Before After

Responsive behavior guessed, not specified.

Multi-column grids collapsed straight to a single column at the first breakpoint, which was almost never right. A four-column metadata grid reads better at two columns on a tablet, and that intent had to be stated explicitly rather than left to inference.

Before After

Sticky offsets with no reference point.

The sticky section rail and the pinned panels each used their own hand-picked offset, all of them slightly out of step with the header. Keying every offset and the scroll-spy threshold to the nav height removed a whole category of small adjustments.

Before After

Copy written to fill a box.

The agent produced confident placeholder prose that fit each layout perfectly and communicated nothing. Every line of body copy on this site was rewritten by hand. It is the one part of the process where generation saved no time at all, and the part I would not want it to.

Before After

Choice made

Fix the class of problem in the token file rather than the instance in the component.

Lesson learned

Fine-tuning was mostly a documentation exercise wearing a coding disguise. Every correction I made twice was a rule I had failed to write down, and editing the spec was consistently faster than editing the next section that would have repeated it.

Next Steps

What this build still owes.

Three things I would action before calling the site genuinely finished.

Replace The Placeholders

Every illustration on this site is still a striped block. Real screenshots and photography are the last dependency between this build and a finished portfolio.

Measure The Build

Performance budgets and bundle checks in CI would turn 'Astro ships less JavaScript' from a stated reason for the stack into a verified one.

Test The System On Someone Else

The real proof that the rules are written down clearly is another person, or another agent, building a new section from the token file without asking me a single question.

Reflection

Final takeaways on building with AI.

I expected this project to teach me about prompting. It mostly taught me about specification. The tools were capable enough that the limiting factor became how clearly I could state what I wanted — which is the same skill that makes a design system work between two people.

The bottleneck moved, it did not disappear.

Generating a section took minutes. Deciding what the section should be, and judging whether the result was actually right, took the rest of the week. AI compressed the part of this work I was already fastest at and left the slow part untouched.

The specification was the real deliverable.

The most valuable file in this repository contains no code. A token document, a set of conventions, and a prompt template outlasted every individual component the agent wrote — several of which were replaced twice.

Two attempts was the right number.

The Figma pass looked like wasted effort until the second attempt reused everything it taught me about hierarchy, rhythm, and what the page needed to do while scrolling. The mistake would have been committing to the first prototype, not making it.

Taste is not delegable.

The agent matched references accurately and followed written rules well. It had no opinion about whether a reference was worth matching, or whether a section deserved to exist. Every judgment that actually shaped the site was still mine to make.

Thank You

Built on other people's work.

Thank you to the maintainers of Astro and Tailwind CSS, whose defaults are good enough that most of my decisions were about restraint rather than repair. Thank you to the designers whose case study typography I studied closely enough to calibrate my own type scale against — the influence is deliberate, the fonts are my own.

And thanks to everyone who read a half-built section and told me plainly that the copy was not working yet. They were right every time.

Next case study