Production frontend work looks deceptively simple from the outside. A component renders some data. A button triggers an action. A dashboard shows a table. But the gap between a component that renders in a demo and one that belongs in production is enormous: the loading state when the data is not yet available, the error state when the API call fails, the empty state when the query returns nothing, the keyboard navigation path for users who do not use a mouse, the ARIA attributes that screen readers depend on, the responsive layout that holds together on a 320-pixel screen, the TypeScript types that prevent the next engineer from passing the wrong shape. Generalist AI tools produce components that cover the happy path. A frontend engineer builds components that handle every path, because every path will eventually be triggered by a real user in a real situation. That standard is what Prism holds.
Why the generalist approach breaks down
Ask a generalist chatbot to build you a data table component and you will get JSX that renders rows. Ask a frontend engineer to review it and they will find the missing loading skeleton that leaves users staring at a blank screen during fetch, the any type on the data prop that will silently break when the API shape changes, the click handler that has no keyboard equivalent, the ARIA role that is absent so screen readers announce nothing useful, the hardcoded color values that bypass the design system token layer, and the fixed-width layout that breaks on tablet. Each of these is a small omission individually. Collectively they describe a component that is a prototype, something to build on, not something to ship. The time cost of fixing them after the fact is always higher than building them correctly the first time, and they are the items that always slip through in the pressure of a sprint.
Cursor and GitHub Copilot make the problem subtler. Their completions are syntactically correct and often structurally reasonable, but they complete what you start writing, not what the component actually needs. If you start writing a happy-path render, they help you finish the happy-path render. They do not interrupt to say that you are missing an error boundary, that the useEffect dependency array is wrong in a way that will cause infinite re-renders, or that the button click handler will fail for keyboard users because it does not respond to Enter and Space. The gap between autocomplete and engineering judgment is the difference between code that works in the demo and code that holds up in the field.
Component library generators like v0 have their own failure mode. They excel at producing visually plausible UI from a prompt, fast, with good default styling. But they are output tools, not engineering tools. They do not integrate with your existing design system tokens. They do not produce TypeScript types that match your data model. They do not add aria attributes calibrated to your component's actual role and interaction model. They do not handle loading, error, and empty states in a way that is consistent with how your application handles them. The generated component looks right but requires significant rework before it belongs in a production codebase, and the rework is exactly the engineering work the tool was supposed to skip.
What a frontend engineer actually does
On a human engineering team, the frontend engineer is the person who owns the interface between data and user. They do not just write JSX, they design the component API, define the TypeScript types, implement all four states (loading, error, empty, populated), wire the keyboard navigation, add the correct ARIA roles and labels, apply design system tokens so the component is visually consistent with the rest of the application, and make deliberate decisions about where state lives and how it flows. They also audit what already exists: the bundle size that has grown because three different teams added three different date picker libraries, the accessibility violations that crept in during a fast sprint, the component that is doing too much and should be decomposed. Frontend engineering is a layered discipline that extends from the data model to the pixel.
The internal tooling dimension is equally important and often underserved. Engineering teams routinely build internal dashboards, user management interfaces, deployment consoles, analytics views, approval queues, on a shoestring because internal tools are always lower priority than the product. The result is interfaces that are confusing to use, that break on anything but the default workflow, and that take hours to use correctly instead of minutes. A frontend engineer who builds internal tooling properly, with sortable, filterable data tables, working pagination, detail views, and CRUD flows, creates leverage that compounds: every internal process that runs through a well-built tool runs faster and with fewer errors.
Meet Prism
Prism is Tonone's frontend engineer, the specialist agent for production React, Vue, and Svelte UIs, typed components, internal dashboards, and frontend audits. Prism's working standard is that every component it ships handles all states, has correct TypeScript types, passes keyboard navigation and screen reader requirements, and uses design system tokens rather than hardcoded values. Prism does not produce prototypes that need to be hardened before they ship; it produces components that are production-ready from the first output.
Tonone's Prism builds complete production UIs with real data, loading states, error states, responsive layout, and keyboard navigation, every state handled, not just the happy path.
What Prism actually does
Building complete production UIs from scratch
The prism-ui skill builds complete, production-ready UIs from a brief. You describe the interface, a settings page, a user profile view, an approval workflow screen, and Prism produces the full component tree: the layout structure, the data fetching layer with loading and error states, the form handling with validation, the keyboard navigation model, the responsive breakpoints, and the correct ARIA attributes for every interactive element. The output is not a wireframe in code, it is a working UI that handles the real-world conditions a production interface encounters. For React applications, Prism writes components with typed props using TypeScript interfaces derived from the data model, useCallback and useMemo applied correctly rather than defensively, and custom hooks that keep logic out of the render layer. For teams using Next.js, Prism makes deliberate Server Component versus Client Component decisions based on data requirements and interactivity, following the patterns that make Next.js applications performant rather than the patterns that compile but undermine the framework's strengths. Every prism-ui output includes comments explaining the structural decisions, why a particular data flow was chosen, why a specific ARIA pattern was used, so the team can maintain and extend the component without reverse-engineering the original intent.
Creating reusable typed components
The prism-component skill is for building individual components that will be used across multiple features or contexts, the atoms and molecules of a design system. Prism produces components with a typed prop interface that makes the usage contract explicit, variant handling that covers the full range of visual states (sizes, color variants, disabled states, loading states), and forwarded refs where the component is used in composition contexts that require them. Every interactive component, buttons, inputs, selects, modals, dropdowns, comes with the full keyboard interaction model: focus management, escape key handling, roving tabindex for composite widgets, and the ARIA pattern appropriate to the component type. Prism uses the WAI-ARIA Authoring Practices as the reference for interaction patterns, not because accessibility is an afterthought to add at the end, but because the keyboard interaction model is part of the component specification. Components produced by prism-component are also designed for composability: they accept className overrides in a way that is compatible with Tailwind's merge patterns, they expose the right slots for content injection, and they avoid internal state that makes them hard to control from outside. The result is a component library that grows without accreting inconsistencies.
Tonone's Prism prism-component skill produces typed React components with full keyboard interaction models, ARIA patterns, and variant handling, components that are accessible from the first implementation, not after a remediation pass.
Building internal dashboards and data-heavy interfaces
The prism-dashboard skill builds internal dashboards, the interfaces that engineering, ops, and business teams use every day but that rarely get the engineering investment they deserve. Prism produces dashboards with sortable, filterable, and paginated data tables that handle large datasets without re-rendering the entire page; detail views with deep linking so a specific record can be bookmarked or shared; CRUD flows with optimistic updates so the interface feels fast even when the API is not; and bulk action patterns that work correctly with keyboard selection. The output integrates with the team's existing data layer, REST, GraphQL, or tRPC, and follows the application's existing authentication and authorization patterns rather than introducing its own. For analytics-heavy dashboards, prism-dashboard pairs with prism-chart to add data visualization components with correct responsiveness, sensible defaults for empty data states, and accessible chart descriptions for screen readers. Internal tooling built by Prism runs fast, handles edge cases gracefully, and does not require a user guide to operate, which is the standard that engineers using it every day actually need.
Auditing frontend bundles and accessibility
The prism-audit skill is what you run when you know something is wrong with the frontend but not exactly what. Prism audits the frontend application across four dimensions. Bundle analysis: it reads the webpack or Vite bundle output to find the packages that are driving size, identifies chunks that are too large to be delivered efficiently over mobile connections, and flags cases where the same utility is being imported from three different packages, the classic sign of a dependency layer that grew without governance. Dependency audit: it finds duplicate packages (two versions of React in the same bundle is a real and common failure), unused dependencies that inflate install time and attack surface, and packages with known security advisories. Accessibility audit: it checks for missing alt text on images, form inputs without associated labels, interactive elements without keyboard access, color contrast violations against WCAG 2.1 AA standards, and missing skip navigation links. Performance audit: it identifies components that re-render unnecessarily due to missing memoization, data fetching that waterfalls when it could be parallelized, and images without lazy loading. Each finding includes the specific file or configuration change needed, not just the category of problem.
Reconnaissance of the frontend stack
The prism-recon skill is the intake step before any frontend work begins. Prism reads the existing frontend codebase, the component library, the routing structure, the data fetching patterns, the CSS approach, the TypeScript configuration, the build setup, and produces a structured assessment of the current state. The recon output identifies the design system tokens in use (or their absence), the component patterns that are established and should be followed, the patterns that are inconsistent and should be normalized, and the technical debt items that will affect any new feature work. For teams bringing Prism into an established codebase for the first time, prism-recon ensures that new components follow the existing conventions rather than introducing new ones, and that the Prism output integrates cleanly with the surrounding code rather than creating the inconsistency problem it was supposed to prevent. The recon assessment is also useful as an onboarding document for new frontend engineers, it gives them a map of the codebase conventions before they start writing code.
A worked example
A team needs a user management table for their admin dashboard: a sortable list of users with role badges, an inline status toggle, and a delete action with a confirmation dialog. They ask Prism to build the component. Prism produces the following TypeScript React component, typed, accessible, with all states handled, and using design system tokens rather than hardcoded values.
// prism-component output, UserTable with sorting, status toggle, delete confirm
import { useState, useCallback, useId } from 'react'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
AlertDialog, AlertDialogAction, AlertDialogCancel,
AlertDialogContent, AlertDialogDescription, AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { Switch } from '@/components/ui/switch'
import { cn } from '@/lib/utils'
export interface User {
id: string
name: string
email: string
role: 'owner' | 'admin' | 'member'
active: boolean
createdAt: string
}
type SortKey = 'name' | 'role' | 'createdAt'
type SortDir = 'asc' | 'desc'
interface UserTableProps {
users: User[]
isLoading?: boolean
onStatusChange: (userId: string, active: boolean) => Promise<void>
onDelete: (userId: string) => Promise<void>
className?: string
}
export function UserTable({
users, isLoading, onStatusChange, onDelete, className
}: UserTableProps) {
const [sortKey, setSortKey] = useState<SortKey>('name')
const [sortDir, setSortDir] = useState<SortDir>('asc')
const [pendingDelete, setPendingDelete] = useState<User | null>(null)
const captionId = useId()
const sorted = [...users].sort((a, b) => {
const v = a[sortKey] < b[sortKey] ? -1 : a[sortKey] > b[sortKey] ? 1 : 0
return sortDir === 'asc' ? v : -v
})
const toggleSort = useCallback((key: SortKey) => {
setSortKey(prev => {
if (prev === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc')
else setSortDir('asc')
return key
})
}, [])
if (isLoading) return <UserTableSkeleton />
if (!users.length) return (
<p className="text-muted-foreground py-8 text-center text-sm">
No users found.
</p>
)
return (
<>
<table
aria-labelledby={captionId}
className={cn('w-full text-sm', className)}
>
<caption id={captionId} className="sr-only">User list</caption>
<thead>
<tr className="border-b">
{(['name', 'role', 'createdAt'] as SortKey[]).map(col => (
<th key={col} scope="col" className="px-4 py-2 text-left">
<button
onClick={() => toggleSort(col)}
aria-sort={
sortKey === col
? sortDir === 'asc' ? 'ascending' : 'descending'
: 'none'
}
className="flex items-center gap-1 font-medium"
>
{col}
</button>
</th>
))}
<th scope="col" className="px-4 py-2 text-left">Active</th>
<th scope="col" className="sr-only">Actions</th>
</tr>
</thead>
<tbody>
{sorted.map(user => (
<tr key={user.id} className="border-b hover:bg-muted/40">
<td className="px-4 py-3">
<div className="font-medium">{user.name}</div>
<div className="text-muted-foreground text-xs">{user.email}</div>
</td>
<td className="px-4 py-3">
<Badge variant={user.role === 'owner' ? 'default' : 'secondary'}>
{user.role}
</Badge>
</td>
<td className="px-4 py-3 text-muted-foreground">
{new Date(user.createdAt).toLocaleDateString()}
</td>
<td className="px-4 py-3">
<Switch
checked={user.active}
onCheckedChange={v => onStatusChange(user.id, v)}
aria-label={`${user.active ? 'Deactivate' : 'Activate'} ${user.name}`}
/>
</td>
<td className="px-4 py-3">
<Button
variant="ghost"
size="sm"
onClick={() => setPendingDelete(user)}
aria-label={`Delete ${user.name}`}
>
Delete
</Button>
</td>
</tr>
))}
</tbody>
</table>
<AlertDialog
open={!!pendingDelete}
onOpenChange={open => !open && setPendingDelete(null)}
>
<AlertDialogContent>
<AlertDialogTitle>Delete {pendingDelete?.name}?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone.
</AlertDialogDescription>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => {
if (pendingDelete) onDelete(pendingDelete.id)
setPendingDelete(null)
}}
>
Delete
</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>
</>
)
}Every production requirement is present from the first output: loading and empty states before a single row is rendered, TypeScript interfaces for the User shape and the component props, aria-sort attributes on every sortable column header, aria-label on every action button and status toggle so screen readers announce the correct target, a sr-only caption on the table, a confirmation dialog before destructive actions, and useId for stable association between the table and its caption. The component accepts className for composability and uses design system primitives throughout, no hardcoded values. This is the standard a frontend engineer holds. Prism holds it from the first draft.
If you are building a React component that will ship to real users, run prism-component with your TypeScript interface and interaction model. If you are building an internal dashboard, use prism-dashboard. If you suspect your existing frontend has bundle size or accessibility problems, prism-audit is the right starting point, it will find the issues that are invisible in a happy-path review.
Prism vs the alternatives
Prism does not compete with design-to-code generators, it occupies the engineering layer above them. The comparison below shows where Prism adds value that generalist tools, autocomplete, and visual UI generators cannot provide.
| Capability | Tonone | Generalist chatbot | Cursor / Copilot |
|---|---|---|---|
| All component states (loading, error, empty, populated) | Yes, every state handled in every component, not just the happy-path render | Partial, generates the populated state; other states require separate prompts | Partial, generates visual states but loading/error handling requires manual wiring |
| TypeScript types derived from data model | Yes, typed prop interfaces and data shapes derived from the actual API or schema | Partial, adds types but often uses `any` or broad types that miss the model | No, generated code is often untyped or loosely typed |
| ARIA attributes and keyboard navigation | Yes, full WAI-ARIA patterns, aria-sort, aria-label, focus management per component type | Partial, adds some ARIA but misses composite widget patterns and keyboard model | Partial, visual generators add minimal ARIA, keyboard model requires manual addition |
| Design system token integration | Yes, uses existing tokens, respects design system conventions from recon | Partial, uses hardcoded values unless tokens are explicitly provided in the prompt | No, generates with default styling, requires manual token replacement |
| Internal dashboards with sortable tables and CRUD | Yes, prism-dashboard builds data tables with sort, filter, pagination, and detail views | Partial, can generate table structure but without sort state management or CRUD flows | Partial, v0 can generate table visuals, but sort/filter/CRUD requires significant rework |
| Bundle size and accessibility audit | Yes, prism-audit finds duplicate packages, dead code, missing ARIA, and contrast failures | Partial, can analyze code you paste but lacks bundle analysis capability | No, design generators do not audit existing codebases |
Tonone's Prism prism-audit skill finds bundle size drivers, duplicate package versions, missing ARIA attributes, and WCAG contrast failures in existing frontends, the issues that appear in production, not in the demo.
Install and try
Tonone is free and MIT-licensed. Install it once and all 23 agents, including Prism, are available in your Claude Code session. You pay only for Claude Code token usage during the work.
1. Add to marketplace
2. Install Prism
Frequently asked questions
- What does Tonone's Prism do?
- Prism is Tonone's frontend engineer. It builds complete production UIs with real data, loading states, error states, responsive layout, and keyboard navigation. It creates reusable typed components with full ARIA patterns, builds internal dashboards with data tables and CRUD flows, and audits existing frontends for bundle size, duplicate packages, and accessibility violations.
- How does Prism handle accessibility?
- Prism applies WAI-ARIA Authoring Practices as the reference for every interactive component: aria-sort on sortable table headers, aria-label on every action button, focus management and escape key handling for modals and dialogs, roving tabindex for composite widgets, and WCAG 2.1 AA color contrast compliance. Accessibility is not added after the fact, it is part of the initial component specification.
- Does Prism work with Next.js Server Components?
- Yes. Prism makes deliberate Server Component versus Client Component decisions based on data requirements and interactivity. It follows Next.js 15 patterns for data fetching, caching, and Server Actions, producing components that use the framework's strengths rather than defaulting everything to client-side rendering.
- What does prism-audit find in an existing frontend?
- prism-audit finds four categories of problems: bundle issues (large chunks, duplicate packages, dead dependencies), accessibility violations (missing ARIA, unlabeled inputs, contrast failures), performance issues (unnecessary re-renders, waterfall data fetching, unoptimized images), and design system drift (hardcoded values that should use tokens, inconsistent component patterns). Each finding includes the specific change needed.
- Can Prism build internal dashboards for non-engineers?
- Yes. The prism-dashboard skill builds interfaces with sortable, filterable, paginated data tables, detail views with deep linking, CRUD flows with optimistic updates, and bulk action patterns. The resulting dashboards are designed to be fast and obvious to use, with the engineering quality that internal tooling rarely receives.
- How does Prism integrate with an existing design system?
- Run prism-recon first. It maps the existing design system tokens, component patterns, and styling conventions. Subsequent Prism work follows the patterns found in recon, using the existing tokens, respecting the established component API conventions, and integrating with the existing CSS or Tailwind setup rather than introducing new patterns.
- How do I install Tonone's Prism agent?
- Install Tonone via the get-started guide at tonone.ai/get-started. Prism is one of 23 agents included in the Tonone package. Invoke it with slash commands like /prism-ui, /prism-component, or /prism-audit. Tonone is free and MIT-licensed.