Most AI-generated UIs look great in screenshots and fall apart in real product use. The screenshots show the happy path: real data, full layout, no loading states, no errors. Real product use is everything else: the network is slow and the skeleton has to render; the data is empty and the empty state has to communicate; the user has a screen reader on and the focus order has to make sense; the device is a phone in landscape and the layout has to reflow without breaking. Every one of those situations is invisible in a screenshot, and every one of them is the situation that turns a demo-ready prototype into something that should not ship. The gap between "the AI built me a UI" and "the AI built me a UI I can put in front of a customer" is exactly that set of situations.
Generalist coding tools optimize for the screenshot. They produce the UI that looks correct on the first render, with placeholder data and assumptions about happy-path layout, because that is the form of output that demos well in a chat window. Production UI is harder than the screenshot. It requires the developer to think through the loading state, the error state, the empty state, the partial-data state, the responsive breakpoints, the keyboard navigation, the focus management, and the accessibility tree. None of that work shows in a single screenshot, which is why generalist tools tend to skip it, which is why teams that depend on those tools end up with codebases full of UIs that look fine until they hit production traffic.
Why generalist AI ships prototypes that look like production
When you ask ChatGPT or Cursor to build a screen, you get something that renders. The HTML is well-structured, the CSS produces a credible layout, the data hooks fetch from the API. The thing that looks like a finished feature is in fact a prototype dressed in production clothing. The loading state is missing because the prompt did not mention it. The empty state is missing because the prompt assumed data would be present. The error state is the React error boundary fallback, or worse, an unhandled exception. The keyboard navigation is whatever the default focus order produces, which is rarely what a user expects. The accessibility tree is whatever the framework gives you, which usually has the right semantic intent but the wrong ARIA labels.
These omissions are not a moral failing of the tools; they are a property of the output format. A chat window favors the response that looks complete in one paste. A complete production UI is not one paste. It is a page, several state branches, a set of edge-case components, a responsive layout, and a small accessibility audit. The right tool for production UI has to know about all of those by default, even when the prompt does not ask for them, because the cost of remembering them at the prompt level is exactly the cost of remembering them in code. /prism-ui is built with the omissions as the default behavior: the loading state, the empty state, the error boundary, the responsive layout, and the accessibility checks are not optional, they are part of every screen the skill produces.
What production UI actually requires
A production-ready screen has at minimum five states beyond the happy path: loading (skeleton or spinner that matches the eventual layout), empty (when there is no data to display), error (when fetching failed and the user has a recovery action), partial (when some of the data loaded and some did not), and over-quota (when the data exceeds the screen and pagination or virtualization kicks in). The screen has to render correctly in each state, and the transition between states has to be smooth: no flash of empty content while loading, no jump in layout when the data arrives, no scroll position lost on a refetch. Each of these is a small concern on its own and a large concern in aggregate.
Beyond the state matrix, production UI has to handle the device and accessibility matrix. The page has to render correctly at the breakpoints the project supports (typically a small phone, a large phone, a tablet, and a desktop, sometimes with print styles). The keyboard navigation has to follow a sensible focus order, with visible focus indicators and ARIA labels that match the visual content. The screen reader experience has to communicate the same information as the visual one, with the structural cues (headings, regions, landmarks) that screen readers depend on. Every shortcut to ship faster on these dimensions is a shortcut that excludes a fraction of the user base, and the fraction is rarely zero.
How /prism-ui works
Step one: read the existing component patterns
Before generating any UI, /prism-ui reads the existing components in the project to identify the patterns: the design system in use, the component library (shadcn, Radix, Material, custom), the routing convention, the data fetching pattern (React Query, RSC, SWR), the state management style, and the styling approach (Tailwind, CSS modules, styled-components). The new screen is built using those patterns rather than introducing new ones. This is what separates a UI that drops into the codebase cleanly from one that imports a different button library and clashes with everything else.
Step two: build the page with all states
The screen is built with the state matrix as a default. The loading state is a skeleton that matches the eventual layout, not a generic spinner. The empty state has copy that explains why it is empty and a path forward. The error state has a recovery action (retry, contact support, refresh) and surfaces the error category to the user without leaking implementation details. The partial state handles the case where some sections of the page have data and some are still loading. The state matrix is verifiable: the skill produces a Storybook entry per state so the team can review each branch before approval.
Step three: responsive and accessibility
The layout is built responsive from the start. The breakpoints come from the project's existing config, and each breakpoint is verified to produce a usable layout: no horizontal scroll on small phones, no center-only block of content on wide desktops. Accessibility is handled in the same pass: semantic HTML for structure, ARIA labels where the visual content does not communicate role, keyboard navigation that follows the visual order, and focus indicators that are visible against the project's color scheme. The skill runs an axe-core check on the final output and surfaces any violations as part of the review.
Step four: real data and tests
The screen connects to the actual API endpoints, not mock data, with the data fetching pattern the project uses. The fetching includes retry behavior, cache management, and error handling that surfaces to the UI through the state matrix. Tests are generated alongside the screen: integration tests for the data flow, component tests for the state branches, and accessibility tests that fail the build if the page regresses on axe-core or keyboard navigation. The tests are the proof that the screen will continue to work as the codebase evolves around it.
The most common UI bug in production is a mismatch between the loading skeleton and the eventual layout. If the skeleton is rectangles in different positions from the real content, the page jumps when the data arrives. /prism-ui builds the skeleton from the same layout as the eventual content so the transition is invisible.
Tonone's /prism-ui skill builds production-ready screens with every state covered: loading, empty, error, partial, responsive across breakpoints, keyboard accessible, with conformance tests.
When to use /prism-ui, and when not to
/prism-ui is the right call when the brief calls for a user-facing page that needs to ship to production. The signal is the word "page" or "screen" in the brief, paired with any indication that real users will see it (login flow, dashboard, checkout, settings, onboarding). The skill is also the right call when an existing prototype needs to be hardened for production: starting from a working but incomplete UI and adding the state matrix, responsive layout, and accessibility audit.
Skip the skill for purely experimental UI (a quick prototype to test an interaction pattern), for internal admin tools where the production discipline is not warranted, and for individual components without a full page context. For component-level work, /prism-component is the right call; for admin dashboards where the surface area is large but the polish bar is lower, /prism-dashboard is calibrated to that style.
| Capability | Tonone | Generalist chatbot | Cursor / Copilot |
|---|---|---|---|
| Reads existing component patterns | Yes, design system, routing, data fetching, styling | No, picks defaults from training data | Partial, suggests within open file context |
| Builds all UI states by default | Loading, empty, error, partial, all included | Happy path only unless prompted | Happy path only |
| Responsive across project breakpoints | Yes, verified at each breakpoint | Generic responsive classes, untested | Single-line suggestions, not layout-aware |
| Accessibility checked by default | Yes, axe-core run on output, violations surfaced | ARIA optional, no audit | Not in scope |
| Real API integration, not mock data | Yes, uses project's data fetching pattern | Often mock arrays | Whatever the line context suggests |
A worked example: building a settings page
Suppose the brief is: build a /settings/profile page that lets users edit their name, avatar, and notification preferences. Run /prism-ui and the output is not just the form. It is the form plus the loading state plus the empty state (first-time user with no profile) plus the error state (save failed) plus the responsive layout plus the keyboard navigation plus the accessibility tree.
// src/app/settings/profile/page.tsx (excerpt)
'use client';
import { useProfile, useUpdateProfile } from '@/hooks/profile';
import { ProfileSkeleton } from './_skeleton';
import { ProfileForm } from './_form';
import { ErrorState } from '@/components/states/ErrorState';
import { EmptyProfileState } from './_empty';
export default function ProfileSettingsPage() {
const { data, isLoading, isError, error, refetch } = useProfile();
if (isLoading) return <ProfileSkeleton />;
if (isError) {
return (
<ErrorState
title="Couldn't load your profile"
body={error.message}
action={{ label: 'Retry', onClick: refetch }}
/>
);
}
if (!data) return <EmptyProfileState />;
return <ProfileForm initialData={data} />;
}
// + _skeleton.tsx (matches form layout exactly)
// + _form.tsx (form with optimistic update, error toast)
// + _empty.tsx (first-time user, with CTA to create profile)
// + _form.test.tsx (integration tests for save success/fail/network)
// + page.a11y.test.ts (axe-core, keyboard nav, focus order)The output is not glamorous in a screenshot. It is the structural pieces that mean the screen will not break when the network is slow, when the user lands here for the first time, when the API returns 500. The Storybook entries cover each branch so the team can review them visually; the tests cover the same branches so regressions fail the build. That is what a production UI looks like, and it is the default output of /prism-ui.
Related skills
/prism-ui builds full screens. For component-level work, /prism-component produces reusable, accessible, typed components from a design spec. For internal admin tools, /prism-dashboard is calibrated to dense data tables, filters, and CRUD flows. For audits of an existing frontend, /prism-audit runs the same accessibility and performance checks across the whole codebase.
Install
/prism-ui ships with the Prism agent in the Tonone for Claude Code package. Install Tonone, invoke /prism-ui from any Claude Code session inside the project, and the skill builds production-ready screens that match the existing patterns and ship with the full state matrix.
1. Add to marketplace
2. Install Prism
The difference between a prototype and a production screen is the boring work: the loading state, the error state, the empty state, the responsive layout, the accessibility audit. The skill is built so that work is included by default, which is the only way it actually gets done.
Frequently asked questions
- What does /prism-ui do?
- It builds production-ready UI screens with loading, empty, error, and partial states by default, responsive layout across the project's breakpoints, accessibility checked with axe-core, and real API integration with the project's data fetching pattern.
- How is /prism-ui different from a generalist AI building UI?
- A generalist produces the happy path that looks correct in screenshots. /prism-ui produces the full state matrix, reads existing component patterns, and runs accessibility checks before the screen is approved.
- When should I use /prism-ui?
- When the brief calls for a user-facing page that needs to ship to production. Skip it for component-level work (use /prism-component), admin tools (use /prism-dashboard), or experimental prototypes.
- What frameworks does /prism-ui support?
- The skill detects the project's framework and matches it. React with Next.js, Remix, Vite, or plain CRA are all supported. Vue, Svelte, and SolidJS are also detected and matched. The skill uses the project's existing patterns rather than imposing a default stack.
- Does /prism-ui generate tests?
- Yes. Integration tests for the data flow, component tests for each state branch, and accessibility tests that fail the build if the page regresses on axe-core or keyboard navigation.
- How do I install /prism-ui?
- Install Tonone for Claude Code via the get-started guide at tonone.ai/get-started. /prism-ui ships with the Prism agent and is invoked as a slash command in any Claude Code session. Tonone is free and MIT-licensed.
- Is /prism-ui free?
- Yes. The skill is part of Tonone, which is MIT-licensed. The only cost is Claude Code token usage during the work.
- Does /prism-ui work with design specs from Figma?
- Yes. If a Figma file or exported design spec is provided, the skill uses it as input. If not, the skill produces a default layout consistent with the project's existing screens.