I just added a chat to my site, robr0 GPT, that answers questions about my work and my design system on the pages it is describing. It came together fast, partly because I was lead design architect on Intuit’s in-product agent chat, the white-label platform capability powering agent conversational experiences across Intuit, now serving millions of customers. That gave me a build order I trusted going in. Here is that order, written as advice rather than a recap, so you can skip the parts that cost me the most time.
Build the parts before the whole
Do not start with a chat. Start with components: a status indicator, a reasoning disclosure, a message bubble, an input that can stream. Build and review each one on its own, with the same rigour as anything else in your system, before any of it touches a model. If a component is still in question by the time you assemble it, you will not be able to tell whether a bug in the chat is a bug in the piece or a bug in the composition.
Fake the model before you wire one in
Combine your parts into a widget running on a scripted, fake transport, one that emits the same event shapes a real model will eventually send. This lets you judge streaming pace and scroll behaviour purely as design work, with no API cost and no latency variance muddying the read. Design one event contract that both the fake and the real transport speak, and swapping one for the other later becomes a one-line change instead of a rewrite. Only once the shell feels right should you wire up a real backend, and even then, test it on a disconnected bench first so you can hammer it without touching your live site.
Expect this stage to take more passes than you think. My commit history over those two days runs into the dozens, nearly all of it scrolling. My first attempt scrolled the conversation up from the bottom as each turn landed, and it read as jumpy the moment a real answer streamed in at an uneven pace. What worked was inverting the problem: let a new turn float to the top of the viewport, with a spacer sized to the exact shortfall between where it lands and what content already exists, handed back as the answer fills it in. Nothing jumps, because the scroll range itself never moves. Give the very first message in an empty thread its own case, a transform rather than a scroll, since there is no scroll distance yet to work with.
Treat the corpus as the real project
The widget is pure design work, visual and interaction, almost creative, all about feel. The corpus is a different kind of problem entirely, closer to engineering than craft, and it is what actually decides whether the answers are worth anything. It will not be enough by default. I found that out asking my own chat for my email address: it told me the site does not publish one, while it sat in plain text on my contact page.
Pull page prose automatically off the filesystem so a new page reaches the chat the moment it ships, rather than depending on you to remember. Let structured facts opt in explicitly, so nothing sensitive leaks in by accident. Hold your corpus to two rules: it should regenerate identically every time you build, and it should only ever contain what is already public.
Then test it properly. Write your questions from personas, not from your codebase. Not “does the corpus contain X” but what a recruiter would actually ask, what a developer would actually ask, plus a few hostile ones. That shift in point of view will find gaps that a spec-first test never will. Run them through your real route, end to end, not the raw model. My first pass caught 71 of 78. After fixing what broke, 77 of 78, and the one still failing taught me something too: the corpus was describing a page that no longer existed.
Live with it before you call it finished
Once the answers are decent, dock the panel to the side on wide screens and let it take the full screen on narrow ones, gliding between the two rather than snapping. Then actually use your own site for a while: open and close it mid-scroll, navigate away mid-stream, ask it questions from every page. That is how you find what is missing, page-aware starter questions, real clickable links back into the site instead of descriptions of them, thumbs on every answer wired to the exchange that produced it, so a bad answer becomes a test case instead of a shrug.
Watch the bill before it watches you
You will burn tokens fast without noticing. The single biggest fix is how long you let a cached prompt stay warm. The default is usually far too short: if your visitors leave ten to fifty minutes between messages, a five-minute cache never survives the gap, and you end up paying to rebuild context on nearly every exchange. Raise it. Then add a per-visitor rate limit, a site-wide daily spend cap that turns the feature off entirely if hit, and a hard ceiling on the account itself as the layer that holds even if your code is wrong. None of this should turn your guardrails off. It should just stop you paying twice for the same context.
The short version
Build on a fake transport first. Design one event contract for both sides. Expect the corpus to eat most of your time, not the widget, and treat any page it cannot see as a bug. Test with personas, not specs. Read the actual transcripts, not just the pass rate. Log what people ask and feed their reactions back into your test set, that loop is what makes the thing improve after launch instead of freezing at day one.
My design system, the one this chat is built on, is published and versioned. Take it, and build your own version of this. If you are already partway through your own, I would like to hear about it.





