Vibe Coding Session: Building This Site From Scratch With Claude Code
This is a session log. Not a polished tutorial — a real account of what it looks like to build a site with Claude Code as your primary collaborator.
The Starting Point
Blank Astro project. npm create astro@latest, pick the minimal template, add Tailwind v4. That’s it. Everything else built from scratch with Claude Code.
The brief going in: dense magazine-style publication for agentic AI content, navy theme, topic-based navigation.
The First Layout Attempt
Claude Code’s first pass was a standard blog layout. Header, hero image, three columns of cards. Technically correct. Visually unremarkable.
Feedback: “more dense, like a news site. the cards have too much whitespace.”
Second pass added tighter spacing, smaller text, more articles per row. Still felt like a template.
The actual unlock was specificity: “look at how The Verge or Wired lays out their homepage — hero that spans 2/3, secondary stack on the right, then a grid below with a sidebar.”
With that reference point, the layout clicked.
The Image Problem
First version of the ArticleCard had topic-colored gradient boxes on every card — every size. Hero, large, medium, small. The homepage looked like a rainbow of boxes.
One line of feedback fixed it: “it doesn’t seem necessary and there are too many colored boxes.”
The rule we landed on: hero cards always have an image (topic gradient if no real image). Large cards show an image only if a real URL is provided. Medium, small, list — text only.
The visual noise disappeared immediately. The hierarchy became readable.
The Sidebar Overflow Bug
Took longer than it should have to find this one.
The layout had two separate sections: a 4-column grid with Latest articles (3 cols) + Sidebar (1 col), then below it a full-width section with topic strips. The sidebar was shorter than the topic strips, so it would end mid-page with dead space.
The fix was architectural: pull the topic strips inside the same lg:col-span-3 column as the Latest articles. Now the sidebar is in the same 4-column grid row as both Latest and the topic strips, so it stays contained.
// BEFORE: two separate grid containers
<div class="grid lg:grid-cols-4"> <!-- Latest + Sidebar -->
<div> <!-- Topic strips, full width -->
// AFTER: one container, topic strips nested inside left column
<div class="grid lg:grid-cols-4">
<div class="lg:col-span-3"> <!-- Latest + Topic strips -->
<div class="lg:col-span-1"> <!-- Sidebar -->
Spacing Iterations
The border-based card design meant spacing required explicit padding on both sides. pb-3 on the card created space below the content before the border, but nothing above — so the next card’s content sat right against the previous card’s border.
Solution: py-4 on the card, first:pt-0 to remove top padding on the first card in each column. Clean.
What Claude Code Got Right Without Being Told
- The sticky header behavior (already included
sticky top-0 z-50) - The
scrollbar-noneclass on the topic nav for mobile overflow - The
last:border-0on sidebar list items so the last item doesn’t have a bottom border - The
line-clamp-2on list card titles — they would overflow without it
Small things. But they add up to a component that actually works out of the box.
The CLAUDE.md Pattern
Midway through the session, we hit a bug where a new Claude Code session didn’t know the “no images on medium/small/list cards” rule and added them back.
The fix: CLAUDE.md with the explicit rule. Next session, it stuck.
This is the most important pattern from the whole build. Claude Code sessions are stateless. The CLAUDE.md is your shared memory. Anything you don’t want to re-teach goes in there.
Total Time
Homepage: about 3 hours of active prompting. Most of that was layout iteration, not debugging.
The server setup (separate session): about 90 minutes, including the apt lock incident where DigitalOcean’s unattended-upgrades process held the dpkg lock for over an hour. Lesson: provision droplets fresh, let them finish updating before you start.
What’s Next
Content collections, article template, writing agent. The vibe coding part is done. The agentic part starts now.