How I AI

Why this page exists

AI knowledge and experience is the norm now. A portfolio without it reads as incomplete. Putting AI inside one case study makes it look like a side project, discovered only by the people who scroll far enough into PinPoint. That felt like the wrong funnel for it. I would rather give the work its own surface here, then let the case studies show what comes of it. PinPoint is the clearest example today. More to come.

How I got to Claude Code

Every era of design has had one tool that took over. Here are mine, plus the moment the AI-era one stuck.

EraTools
PrintQuarkXPress, PageMaker, Photoshop
Early webPhotoshop, then Fireworks
Modern webSketch + InVision, then Figma
AI eraClaude Code

When I first started experimenting with vibe-coding in the summer of 2024, I tried it with ChatGPT. I asked it to write code snippets, copied them into Visual Studio Code, watched them break, pasted them back in for a hopefully-working version, and repeated. It was slow and tedious and there had to be a better way.

I follow tech news. Claude Code's emergence got covered in many places. I tried it. Using Claude Code itself to learn how to use Claude Code, I found my new platform.

Picking the right platform is the easy part. The harder part is the workflow around it, and that workflow starts with how each piece of work gets scoped.

Scope before execution

Backlog Buddy is the workflow I built around Claude Code. It runs on Trello, where each card is one piece of work. Every card gets groomed before it goes into implementation, even the small ones.

The board predated Backlog Buddy. I started it for PinPoint to catch overflow feature ideas as they hit at dinner, on walks, while talking with players at arcades during MNP tournaments. The board was a place to put thoughts before they evaporated. Visual reference, low effort.

Once I was comfortable in Claude Code, I wired Trello's API into the workflow. The same board served two purposes now. Kanban for me, programmatic surface for Claude. Backlog Buddy emerged from that integration. A pattern that fit the gap.

The grooming workflow is the part that matters. Before any card moves to In Progress, it gets scoped with full knowledge of the codebase. I sit with Claude and we read the relevant files together, ask questions, surface duplicates, weigh tradeoffs. The scope ends up in the card description as the source of truth. Before this was the workflow, scope drifted mid-build. We would discover a constraint two hours in and have to rework. Now constraints land in the scope before the first file is touched. Less rework. Fewer dead ends.

A PinPoint run from 2026-05-25.

📊 Backlog Summary: 189 items
CategoryCount
🔄 ONGOING EPICS4 (informational)
⚠️ IN PROGRESS4 (finish first!)
🗣️ USER FEEDBACK10 (across all lists with 💬 label)
🚨 PLAYER CLAIM DISPUTES0
↔️ EXPANSION36 (NWPAS deadline 2026-06-03)
🧹 GROOMED110 (ready to pull)
📋 BACKLOG51
🚨 P0 outside Up Next0
🔥 Top Priority Items ⚠️ IN PROGRESS (finish what's started)1. Portfolio demo: demo.pinpoint.lol with DEMO_MODE2. Admin: Finals Venue (W14) — neutral venue picker (Ready for Dev)3. Competitive Data Persistence Layer (↔️ Expansion, P0)4. /player Page Redesign — top-down from PlayerCard header (↔️ Expansion, P1) 🗣️ USER FEEDBACK1. League-wide machine averages + score-percentile predictor (needs grooming)2. Pinball Maps integration: lineup diff + practice venue finder (P2, Ready)3. Sign in with email — OAuth alternative (P2, Ready)4. Team Practice Score Tracking (P2, Ready)5. Pindigo score import for player profiles (P2, Ready) ↔️ EXPANSION (NWPAS deadline — 9 days away)1. Player /player OVERVIEW segment — content + data contract (P1, Ready)2. Live Tournament Scout: MatchPlay /api/dashboard surface3. Player /player LEAGUE segment — cleanup + segment extraction (Ready)4. Explore: StreetLeague Northend Thursday league data (Needs Grooming)5. EPIC: Competitive integration (IFPA / MatchPlay / Pinscores) anchor ⚠️ NWPAS reality check: 9 days until 2026-06-03. The Expansion-labeled In Progress items (Competitive Data Persistence + /player redesign) plus Overview/League segments are the critical path. Should we tackle any backlog items today?

Scoping is the first discipline. Judging what comes back is the second.

Refining to vision

Refining is reframing. When Claude Code ships something working, the next move is often to recognize the framing is wrong and start over. Pixels are downstream. The framing is where most of the work happens.

Thirty minutes into a session I am usually doing this work. Reviewing the surface, comparing it to the version in my head, naming what is off, and not telling Claude how to fix it. What follows are three sessions where working code was the wrong frame, and the rebuild started from saying that out loud.

The modal that felt like a different page

This portfolio site. The case-study carousel on the homepage should expand a card into a full modal when clicked.

The carousel-to-modal interaction shipped with Phase A tests green, build clean, types clean. By every functional measure, done. I looked at it and rejected the framing.

It certainly doesn't feel like the card has popped out of the carousel. It feels like a completely different page. It should be as if we are making an exact duplicate of the carousel card, its contents and dimensions, and expanding it to near full window.

That reframe forced Claude to throw out a working dialog-shell implementation, extract a shared component, and rebuild from a different mental model. Twenty-plus iterations followed across two sessions on flash artifacts, translucent grow states, text reflow on close, the order in which the case-study content collapses out of the panel. By the end the carousel card and the modal hero were visibly the same DOM node morphing in place.

The session closed on a softer note.

the whole carousel and modal interaction is complex and elegant. Good job us.

The whole /player experience

PinPoint league analytics. The /player page is where a player views their own stats and league history.

Claude had just shipped a four-phase unified /player redesign across two days. Segment control, three new views, hero summary, cross-platform machines, dual-tier ribbon. Release report upbeat, four commits, all routes 200.

I asked Claude to start over.

The whole /player experience is not great. I blame myself for not being more explicit in my direction. What's good is that we have hooks in to the data on /player. What is bad is the separation of components, what content lives under what segment, and the overall value to the player when viewing this page. The most common use case is going to be a player checking in on themselves.

The page had become league-centric in a way that did not serve the primary user. The fix was a top-down revision starting from the wrong dominant use case. Naming the primary user out loud reorients everything downstream.

Sticky and full-bleed, four tries deep

Same PinPoint /player page. The header card and segment control needed to stay sticky as a player scrolled through their stats.

I asked for a player header and segment control to stay sticky with a #101414 background so content would not bleed through. Claude tried three different CSS approaches. width: 100vw plus marginLeft: calc(-50vw + 50%). Then flexShrink: 0. Then absolutely positioned ::before pseudo-elements past the box bounds. Each compile returned 200. Each iteration looked successful in code. In the browser the background kept clipping at the parent flex container's edges, and the last hack introduced horizontal document overflow that broke position: sticky page-wide.

I pushed back with screenshots.

It is still not extending full width.

vertical spacing looks correct. The width is still not full width.

Then it got worse, when the topbar lost its stickiness too.

Now the newTopAppBar lost its stickiness, too! Thats not good. Nothing is sticky now.

Claude eventually abandoned the calc/pseudo approach and restructured the layout instead. The sticky group moved out of the inner container and became a direct child of <main>, which is already viewport-width. Background extended edge-to-edge naturally. Topbar stickiness returned. Working code review doesn't catch what an eye on the rendered surface does.

By the time the work ships, it has cleared four adjectives. Tasteful, useful, usable, and in line with my overall vision.

Refining catches problems after Claude has produced them. The other way to handle them is upstream, in the prompts.

Prompting in UI-native language

Prompts that work open with framing. Personas. Primary use case. Mobile-first. Perceived performance. After twenty-five years of UX work, that framing comes naturally, and prompts written that way tend to get different output back from Claude.

Three examples from the corpus.

A design-system rule, written into the docs in one sentence

Write into our design system documentation that we are never to use
true white or true black. "white" or "black" should always be a very bright
or not bright version of our teal

Written as a system rule. Brand-tinted neutrals are a known good practice for visual cohesion. Linear, Stripe, and Vercel all do it. Most people cannot name it. The sentence above got Claude to add --color-brand-white and --color-brand-black tokens in OKLCH space, update the docs, and apply the rule retroactively across components. Without that framing, the equivalent prompt would have been "change all the whites to a slightly teal-tinted white," which fixes the symptom and leaves the system alone.

Personas before pixels

I want to be very deliberate and methodical about what components are
displayed on the unified player page. I want to talk through the personas
that will be using this, starting with the competitive player with an IFPA
and MatchPlay who also plays in MNP. This is our primary user. I want to
talk through how to separate league data from competitive data on /player,
how we might allow the user to switch between viewing their wholistic career
(competitive & league), Just IFPA, Just MatchPlay, Just MNP. Perhaps that is
a universalSegmentControl that has two segments: TOURNAMENTS|LEAGUE

The first move is naming the primary user. Then the design problem of separating league data from competitive data. Then the user-facing control surface. Then a hypothesis without locking it. That framing forced Claude to respond with a section inventory grouped by belonging, two rhythms of attention, and a tested hypothesis instead of jumping to code. Without those moves, the prompt would have been "build me a unified player page with tabs for each data source," which gets you a working tab component and the wrong information architecture.

Four design decisions in one sentence

reduce vertical space between metadata label and value. Tint the labels
more teal while staying AAA contrast ratio compliant with the gradient
background

Four design decisions packed into one sentence. "Vertical space between label and value" names the spacing problem in IA terms instead of "spacing." "Tint the labels more teal" stays inside the existing palette and shifts the tone. "AAA contrast ratio compliant" names AAA-level contrast at 7:1, above the more common 4.5:1 of AA. "On the gradient background" tells Claude the background is non-uniform so test against the worst case. Claude did not have to ask a single follow-up. Without that vocabulary, the prompt becomes "make the labels a different color and closer to the values," which ships an arbitrary hex value and probably the wrong gap token.

These are not exotic prompts. They are prompts written from a clear picture of what the rendered output should do for the user. Claude builds. The prompt does the aiming.